Assign key to multiple objects - javascript

Is it possible to assign a value to multiple JavaScript objects at the same time?
Obviously this can be done with a for loop etc, but I'm curious if there's something in the new version of the language that makes this possible. Similar syntax already exists in a number of other languages, I just can't find the JavaScript equivalent.
Ideally, the syntax would look something like this:
{App1, App2, App3}.foo = "bar"
App1.foo === "bar" // true
App2.foo === "bar" // true

You are effectively looking for lenses, which can abstract over such operations and also provide multiple targets. There are various JS implementations around, though I didn't find any that uses lists. With them, it would look something like
set(onList(property("foo")), [App1, App2, App3]);
But that's ugly, right? And you were asking for new ES6 features. Yes, a Proxy can help us make this a lot more beautiful indeed:
ListProxy(App1, App2, App3).foo = "bar";
Here's how you'd implement such a function:
const ListProxy = (() => {
const handler = {
set(target, property, value) {
for (const t of target)
t[property] = value;
},
get(target, property) {
if (typeof target == "function")
target = target.values;
const maybe = target.filter(x => property in Object(x));
if (maybe.length == 0) return undefined;
let values = maybe.map(x => x[property]);
if (values.every(v => typeof v == "function")) {
function fnList(...args) {
return maybe.map(v => v[property](...args));
}
fnList.values = values;
values = fnList;
}
return new Proxy(values, handler);
}
};
return function ListProxy(...args) { return new Proxy(args, handler); };
})();
The get method is not so vitally important, but it does allow for deeper chaining and even function calls instead of assignments:
ListProxy({value:"ax"}, {value:"by"}).value[0].toUpperCase(); // ["A","B"]

There is no native way to do it. However, if you are just looking for similar syntax, you can do something similar. You can create a proxy function which will do it for you.
var _ = (...args) => {
var proxy = new Proxy(args, {
set: (target, property, value) => {
target.forEach(object => object[property] = value);
}
});
return proxy;
};
var App1 = {}, App2 = {}, App3 = {};
_(App1, App2, App3).value = {
foo: 'bar'
};
_(App1, App2, App3).someOtherValue = {
foo: 'baz'
};
console.log(App1); // { value: { foo: 'bar' }, someOtherValue: { foo: 'baz' } }
console.log(App2); // { value: { foo: 'bar' }, someOtherValue: { foo: 'baz' } }
console.log(App3); // { value: { foo: 'bar' }, someOtherValue: { foo: 'baz' } }

The only way to make something like the syntax you propose work is to extend the Object prototype, whether or not one thinks that's a good idea (it's not).
const App1 = {}, App2 = {}, App3 = {};
Object.defineProperty(Object.prototype, 'values', {
set(value) {
for (let prop in this) this[prop].value = value;
}
});
({App1, App2, App3}).values = "foo";
console.log(App1.value);
You would never be able to write {App1, App2, App2}.value, because the JS parser would interpret the leading { as the beginning of a block. Hence the need to enclose it in parentheses.
You cannot use value to set all the values, since that would conflict with the value property you want to set on the individual objects. Hence we use values instead.

I don't think a special syntax is required for this, I'd rather assign it using basic ES6:
const baz = { foo: "bar" };
[App1, App2, App3].forEach(app => app.value = baz);

you can use:
App1.value = App2.value = {foo: "bar"};
Or
App1.value.foo = App2.value.foo = "bar";

Related

Getter/Setter as function

Is it possible to create property getter/setter as a function?
Standard getter/setter work like following:
class Test {
something: any;
get prop() {
return something;
}
set prop(value) {
something = value;
}
}
let instance = new Test();
instance.prop = 'Foo';
console.log(instance.prop); // = Foo
I would need following:
let instance = new Test();
instance.prop('Bar') = 'Foo'; // access setter as a function of prop
console.log(instance.prop('Bar')); // = Foo
Yes, I know it's unorthodox usage and yes, I know that I could implement this functionality bit differently. I'm just interested if this is possible in JS/TS/ES6.
Update
This is the closest I got:
class Test {
something: any;
prop(area /* my custom symbol type */) {
const obj: any = {};
Object.defineProperty(obj, 'value', {
// get child object of my complex object
get: () => this.something[area];
// replace part of my complex object
set: (value) => {
this.something = {...this.something, [area]: value}
}
});
}
}
let instance = new Test();
instance.prop('Bar').value = 'Foo';
console.log(instance.prop('Bar').value); // = Foo
So in short, I'd like to get rid of value suffix if possible.
As #deceze mentioned in the first comment, it is not possible to assign to function call, so solution in the update is as best as it gets.

Why doesn't JavaScript ES6 support multi-constructor classes?

I want to write my Javascript class like below.
class Option {
constructor() {
this.autoLoad = false;
}
constructor(key, value) {
this[key] = value;
}
constructor(key, value, autoLoad) {
this[key] = value;
this.autoLoad = autoLoad || false;
}
}
I think it would be nice if we can write out class in this way.
Expect to happen:
var option1 = new Option(); // option1 = {autoLoad: false}
var option2 = new Option('foo', 'bar',); // option2 = {foo: 'bar'}
var option3 = new Option('foo', 'bar', false); // option3 = {foo: 'bar', autoLoad: false}
I want to write my Javascript class like below
You can't, in the same way you can't overload standard functions like that. What you can do is use the arguments object to query the number of arguments passed:
class Option {
constructor(key, value, autoLoad) {
// new Option()
if(!arguments.length) {
this.autoLoad = false;
}
// new Option(a, [b, [c]])
else {
this[key] = value;
this.autoLoad = autoLoad || false;
}
}
}
Babel REPL Example
Of course (with your updated example), you could take the approach that you don't care about the number of arguments, rather whether each individual value was passed, in which case you could so something like:
class Option {
constructor(key, value, autoLoad) {
if(!key) { // Could change this to a strict undefined check
this.autoLoad = false;
return;
}
this[key] = value;
this.autoLoad = autoLoad || false;
}
}
What you want is called constructor overloading. This, and the more general case of function overloading, is not supported in ECMAScript.
ECMAScript does not handle missing arguments in the same way as more strict languages. The value of missing arguments is left as undefined instead of raising a error. In this paradigm, it is difficult/impossible to detect which overloaded function you are aiming for.
The idiomatic solution is to have one function and have it handle all the combinations of arguments that you need. For the original example, you can just test for the presence of key and value like this:
class Option {
constructor(key, value, autoLoad = false) {
if (typeof key !== 'undefined') {
this[key] = value;
}
this.autoLoad = autoLoad;
}
}
Another option would be to allow your constructor to take an object that is bound to your class properties:
class Option {
// Assign default values in the constructor object
constructor({key = 'foo', value, autoLoad = true} = {}) {
this.key = key;
// Or on the property with default (not recommended)
this.value = value || 'bar';
this.autoLoad = autoLoad;
console.log('Result:', this);
}
}
var option1 = new Option();
// Logs: {key: "foo", value: "bar", autoLoad: true}
var option2 = new Option({value: 'hello'});
// Logs: {key: "foo", value: "hello", autoLoad: true}
This is even more useful with Typescript as you can ensure type safety with the values passed in (i.e. key could only be a string, autoLoad a boolean etc).
Guessing from your sample code, all you need is to use default values for your parameters:
class Option {
constructor(key = 'foo', value = 'bar', autoLoad = false) {
this[key] = value;
this.autoLoad = autoLoad;
}
}
Having said that, another alternative to constructor overloading is to use static factories. Suppose you would like to be able to instantiate an object from plain parameters, from a hash containing those same parameters or even from a JSON string:
class Thing {
constructor(a, b) {
this.a = a;
this.b = b;
}
static fromHash(hash) {
return new this(hash.a, hash.b);
}
static fromJson(string) {
return this.fromHash(JSON.parse(string));
}
}
let thing = new Thing(1, 2);
// ...
thing = Thing.fromHash({a: 1, b: 2});
// ...
thing = Thing.fromJson('{"a": 1, "b": 2}');
Here's a hack for overloading based on arity (number of arguments). The idea is to create a function from a number of functions with different arities (determined by looking at fn.length).
function overloaded(...inputs) {
var fns = [];
inputs.forEach(f => fns[f.length] = f);
return function() {
return fns[arguments.length].apply(this, arguments);
};
}
var F = overloaded(
function(a) { console.log("function with one argument"); },
function(a, b) { console.log("function with two arguments"); }
);
F(1);
F(2, 3);
Of course this needs a lot of bullet-proofing and cleaning up, but you get the idea. However, I don't think you'll have much luck applying this to ES6 class constructors, because they are a horse of a different color.
you can use static methods,look at my answer to same question
class MyClass {
constructor(a,b,c,d){
this.a = a
this.b = b
this.c = c
this.d = d
}
static BAndCInstance(b,c){
return new MyClass(null,b,c)
}
}
//a Instance that has b and c params
MyClass.BAndCInstance(b,c)
Use object.assigne with arguments with this
This={...this,...arguments}
Its not the overload I wanted, but this is a basic version of how I faked my way through creating an obj1 with some different initialization behavior. I realize I could have expanded the arguments as stated above, but I already had a nasty set of arguments and relatively different data sources to deconstruct that would have really distorted my objectives; this just made it cleaner for my situation...
class obj1{
constructor(v1, v2){
this.a = v1;
this.b = v2;
}
}
class obj1Alt{
constructor(v1, v2){
return new obj1(v1*2,v2*2);
}
}
new obj1(2,4) // returns an obj1
new obj1Alt(2,4) // also returns an obj1
Disclaimer: I've been programming for a long time, but I am fairly new to JS; probably not a best practice.

Creating a function with properties

Sorry, I donĀ“t know the name of this.
I want to have a function and an object with properties in only one variable.
Here is how it works:
var obj = function() {
return "foo";
};
obj.prop = "bar";
obj(); // => "foo"
obj.prop; // => "bar"
This works fine, but I would like to change the order of this:
var obj = { prop: "bar" };
obj = function() {
return "foo";
};
obj(); // => "foo"
obj.prop; // => undefined
Is there a way to do this?
I want do do this because I have a lot of properties to add to the object:
var obj = function() {
return "foo";
};
obj.prop1 = "bar1";
obj.prop2 = "bar2";
obj.prop3 = "bar3";
obj.prop4 = "bar4";
obj.prop5 = "bar5";
obj.prop6 = "bar6";
obj.prop7 = "bar7";
//...
This isn't possible because when you do:
obj = function() {
return "foo";
};
...you're assigning the variable obj to the new function, so it no longer points to the original object you created ({ prop: "bar" }) at all.
So if you want to add properties to a function object, you must always create the function first, then add properties.
As an alternative, you could do something like this:
var props = {
prop1: "bar1",
prop2: "bar2"
};
var obj = function() {
return "foo";
};
for (var key in props) {
obj[key] = props[key];
}
Or if you happen to have jQuery available (and don't have Object.assign available):
jQuery.extend(obj, props);
(Of course there are shims available for Object.assign, which would allow #Pointy's answer to work in older browsers.)
If you want to do this with one statement, ES2015 (and some libraries) let you do:
var obj = Object.assign(
function() { /* ... */ },
{ "hello": "world" }
);
Which will give you obj as a function with the property "hello". Note that this is really just the same thing as the separate assignment, but it's all wrapped up as one overall expression, which is nice because it means you can do something like
return Object.assign(function() { /* whatever */ }, {
prop: whatever,
// ...
});
I also agree with Grundy, but you could do something like that:
var x = function(){
var obj = {};
return {
objToReturn: obj,
objFunction: function(){return 'foo';},
addItemsToObject: function (key, value) {
obj[decodeURIComponent(key)] = value;
}
}
};
I honestly don't know if that's what you really want, but in that case you can execute the "x" function and after you can access the
"objFunction", the "objToReturn" or the "addItemsToObject" function.
So it will be something like that:
var y = x();
for (propertie in yourProperties){
y.addItemsToObject
(propertie, yourProperties[decodeURIComponent(propertie)]);
}
And then:
y.objFunction();
'foo'
Hope that helps.

Implementing a builder using closures in JavaScript

I would like to implement a builder using closures in JavaScript. I feel it can be done, but am struggling to put it into code.
I have something like this but I feel there is probably a better solution leveraging something like partial application.
function Builder() {
this.spec = {};
}
Builder.prototype.withFoo = function(value) {
this.spec.foo = value;
return this;
};
Builder.prototype.withBar = function(value) {
this.spec.bar = value;
return this;
};
Builder.prototype.build = function() {
var result = {};
result.foo = this.spec.foo;
result.bar = this.spec.bar;
this.spec = {}; // This is to avoid accidentally using the same builder repeatedly.
return result;
};
var builder = new Builder();
builder.withFoo('foo value')
.withBar('foo value')
.build(); // { foo: 'foo value' , bar: 'bar value' }
Can anyone help me do this?
Edit: The key thing here is that I want the object to be instantiated lazily.
Here is an alternative approach:
function Builder(obj){
return obj;
}
This is called by Fowler and Martin the "Identity Builder" and is quite common in enterprise architecture. It has the advantage of supporting arbitrarily nested hierarchies of objects and sub objects and it is completely generic.
var myBuildObject = Builder({
spec: {
foo: foo,
bar: bar
}
});
It does so much more though, it can also easily specify arrays:
var myBuildObject = Builder({
spec: [....]
});
It can be extended and subclassed with more sophisticated builders that can in turn do return Builder.call(this, obj) after decorating it.
It can even specify getters/setters.
It is usual with a builder to be able to chain the calls together, so something like the below should do what you're after:
var builder = (function(){
var obj = {};
return {
withFoo: function(foo){
obj.foo = foo;
return this;
},
withBar: function(bar){
obj.bar = bar;
return this;
},
build: function(){
var rtn = obj;
obj = {}; // clear so you can use builder again
return rtn;
}
}
})();
var result = builder.withFoo("foo")
.withBar("bar")
.build();
console.log(result); // { foo: 'foo' , bar: 'bar' }
Having said that, I like to have an instance of the builder, so in all honesty I prefer your original (except with return this to return the current instance of the builder from the methods). So I'd personally go with:
function Builder() {
this.spec = {};
}
Builder.prototype.withFoo = function(value) {
this.spec.foo = value;
return this;
};
Builder.prototype.withBar = function(value) {
this.spec.bar = value;
return this;
};
Builder.prototype.build = function() {
return this.spec;
};
var result = new Builder()
.withFoo('foo value')
.withBar('foo value')
.build();
Note there is no need to clear out spec when callingbuild - a new instance of Builder will have a new instance of spec!
One simple way to invoke code lazily is partial application and currying in particular.
Let's say you have a Person type and it looks like this:
function Person(name, lastName, age, height){
return {
name: name,
lastName: lastName,
age: age,
height: height
};
}
var pete = Person("Pete", "Doe", 40, 6.4);
What we really want it to do - is to be able to specify only some of those properties initially and some later:
var namedJoeDoe = Person("Joe", "Doe"); // won't work today
var tallJoe = namedJoeDoe(40, 8.6); // one object
var shortJoe = namedJoeDoe(40, 4.3); // another object
We only create the object (and your real objects are probably larger at the very end. This is currying, and if we use Ramda's curry we can write it as such:
Person = R.curry(Person); // impl at src/curryN.js
We get exactly this functionality, quoting the docs:
Returns a curried equivalent of the provided function. The curried function has two unusual capabilities. First, its arguments needn't be provided one at a time. If f is a ternary function and g is R.curry(f), the following are equivalent:
g(1)(2)(3)
g(1)(2, 3)
g(1, 2)(3)
g(1, 2, 3)
That is, only when you've supplied all properties for a person will an instance be created.

How would you overload the [] operator in javascript

I can't seem to find the way to overload the [] operator in javascript. Anyone out there know?
I was thinking on the lines of ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
or am I not looking at the right things.
You can do this with ES6 Proxy (available in all modern browsers)
var handler = {
get: function(target, name) {
return "Hello, " + name;
}
};
var proxy = new Proxy({}, handler);
console.log(proxy.world); // output: Hello, world
console.log(proxy[123]); // output: Hello, 123
Check details on MDN.
You can't overload operators in JavaScript.
It was proposed for ECMAScript 4 but rejected.
I don't think you'll see it anytime soon.
The simple answer is that JavaScript allows access to children of an Object via the square brackets.
So you could define your class:
MyClass = function(){
// Set some defaults that belong to the class via dot syntax or array syntax.
this.some_property = 'my value is a string';
this['another_property'] = 'i am also a string';
this[0] = 1;
};
You will then be able to access the members on any instances of your class with either syntax.
foo = new MyClass();
foo.some_property; // Returns 'my value is a string'
foo['some_property']; // Returns 'my value is a string'
foo.another_property; // Returns 'i am also a string'
foo['another_property']; // Also returns 'i am also a string'
foo.0; // Syntax Error
foo[0]; // Returns 1
foo['0']; // Returns 1
Use a proxy. It was mentioned elsewhere in the answers but I think that this is a better example:
var handler = {
get: function(target, name) {
if (name in target) {
return target[name];
}
if (name == 'length') {
return Infinity;
}
return name * name;
}
};
var p = new Proxy({}, handler);
p[4]; //returns 16, which is the square of 4.
We can proxy get | set methods directly. Inspired by this.
class Foo {
constructor(v) {
this.data = v
return new Proxy(this, {
get: (obj, key) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key]
else
return obj[key]
},
set: (obj, key, value) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key] = value
else
return obj[key] = value
}
})
}
}
var foo = new Foo([])
foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]
As brackets operator is actually property access operator, you can hook on it with getters and setters. For IE you will have to use Object.defineProperty() instead. Example:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
The same for IE8+:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
For IE5-7 there's onpropertychange event only, which works for DOM elements, but not for other objects.
The drawback of the method is you can only hook on requests to predefined set of properties, not on arbitrary property without any predefined name.
one sneaky way to do this is by extending the language itself.
step 1
define a custom indexing convention, let's call it, "[]".
var MyClass = function MyClass(n) {
this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
...
var foo = new MyClass(1024);
console.log(foo["[]"](0));
step 2
define a new eval implementation. (don't do this this way, but it's a proof of concept).
var MyClass = function MyClass(length, defaultValue) {
this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));
var mini_eval = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = eval(values[0]);
var i = eval(values[2]);
// higher priority than []
if (target.hasOwnProperty('[]')) {
return target['[]'](i);
} else {
return target[i];
}
return eval(values[0])();
} else {
return undefined;
}
} else {
return undefined;
}
} else {
return undefined;
}
};
mini_eval("foo[33]");
the above won't work for more complex indexes but it can be with stronger parsing.
alternative:
instead of resorting to creating your own superset language, you can instead compile your notation to the existing language, then eval it. This reduces the parsing overhead to native after the first time you use it.
var compile = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = values[0];
var i = values[2];
// higher priority than []
return `
(${target}['[]'])
? ${target}['[]'](${i})
: ${target}[${i}]`
} else {
return 'undefined';
}
} else {
return 'undefined';
}
} else {
return 'undefined';
}
};
var result = compile("foo[0]");
console.log(result);
console.log(eval(result));
You need to use Proxy as explained, but it can ultimately be integrated into a class constructor
return new Proxy(this, {
set: function( target, name, value ) {
...}};
with 'this'. Then the set and get (also deleteProperty) functions will fire. Although you get a Proxy object which seems different it for the most part works to ask the compare ( target.constructor === MyClass ) it's class type etc. [even though it's a function where target.constructor.name is the class name in text (just noting an example of things that work slightly different.)]
So you're hoping to do something like
var whatever = MyClassInstance[4];
?
If so, simple answer is that Javascript does not currently support operator overloading.
Have a look at Symbol.iterator. You can implement a user-defined ##iterator method to make any object iterable.
The well-known Symbol.iterator symbol specifies the default iterator for an object. Used by for...of.
Example:
class MyClass {
constructor () {
this._array = [data]
}
*[Symbol.iterator] () {
for (let i=0, n=this._array.length; i<n; i++) {
yield this._array[i]
}
}
}
const c = new MyClass()
for (const element of [...c]) {
// do something with element
}

Categories