Dynamic composition of strings - javascript

Is it possible to somehow compose a string dynamically? I've read a bit about pass-by-value and pass-by-reference, and thus I'm creating all the strings as objects.
Example:
var foo = {str: 'foo'};
var bar = {str: foo.str + 'bar'};
var baz = {str: bar.str + 'baz'};
foo.str = 'fuu';
console.log(baz.str); //expected 'fuubarbaz', got 'foobarbaz
Thanks in advance!

Nah, when you define things statically like that, they're going to use the variable when it was called. You could do something like this with getters though:
let foo = {str: 'foo'};
let bar = {get str() { return foo.str + 'bar'; }};
let baz = {get str() { return bar.str + 'baz'; }};
foo.str = 'fuu';
console.log(baz.str); // properly outputs `fuubarbaz`
The reason why this works is the magic of getters; instead of defining the property statically, you're defining a function that gets called when trying to access the property. This way it can "react" to any downstream changes, because it's always dynamically generated.

It doesn't work like this, the concatenation foo.str + was executed only once, the plus sign is not a function that is called multiple times.
One way to do what you want is create an object with 3 strings and a method!:
const obj = {
a: 'foo',
b: 'bar',
c: 'baz',
show: function() {
return this.a + this.b + this.c;
}
};
console.log(obj.show());
obj.a = 'fuu';
console.log(obj.show());

Based on puddi's answer I came up with this:
console.clear()
var foo = {
// _str is the storage of str
_str: 'foo',
// getter of str, always called when accessing str in a read context
get str() {return this._str},
// setter of str, always called when accessing str in a write context
set str(str) {this._str = str}
};
// read context, so get str() of foo is called
console.log(foo.str) // "foo"
var bar = {
// define getter function of bar, calls getter function of foo
get str() {return foo.str + 'bar'}
};
// read context, so get str() of bar is called
console.log(bar.str) // "foobar"
var baz = {
// define getter function of baz, calls getter function of baz
get str() {return bar.str + 'baz'}
};
// read context, so get str() of baz is called
console.log(baz.str) // "foobarbaz"
// write context, so set str(str) of foo is called. foo._str is now 'fuu', was 'foo'
foo.str = 'fuu';
// read context, getter of baz is called which calls getter of bar which calls getter of foo which returns _str which has the value of 'fuu'
console.log(baz.str); // "fuubarbaz"
Alternatively you can user Object.defineProperty:
console.clear();
var foo = Object.defineProperty({}, 'str', {
enumerable: true,
get: () => this._property_str,
set: (str) => this._property_str = str
});
var bar = Object.defineProperty({}, 'str', {
enumerable: true,
get: () => foo.str + 'bar',
});
var baz = Object.defineProperty({}, 'str', {
enumerable: true,
get: () => bar.str + 'baz',
});
foo.str = 'foo'
console.log(foo.str) // "foo"
console.log(bar.str) // "foobar"
console.log(baz.str) // "foobarbaz"
foo.str = 'fuu';
console.log(baz.str); // "fuubarbaz"

Related

How to read this ES6 code utsing destructuring and initialization?

I ran into some code that looks like the following:
const {
foo = []
} = this.options
Assuming in this case that this.options is an JavaScript Object, how does this work? Does all of this.options get assigned to foo and if this.options is undefined, does foo just get initialized to an empty array? I found this code confusing because this.options is not an Array but is instead an Object of key/val pairs.
Sometimes it helps to just try things out. What you'd observe is that a default value is assigned to foo in case it is missing within the to be assigned object
function one() {
const options = {};
const {
foo = []
} = options;
console.log(foo);
}
function two() {
const options = {foo: 'bar'};
const {
foo = []
} = options;
console.log(foo);
}
function three() {
const options = {};
const {
foo = 'foo',
bar = 'bar',
baz = 'baz'
} = options;
console.log(foo, bar, baz);
}
one();
two();
three();
From MDN (emphesis mine ) :
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables.
Not all of this.options get assigned to foo, it's unpacking foo from the Object :
const options = {
foo: ['foo', 'bar'],
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
And foo = [] is there to be a fallback to have an empty array if this.options does not have a property foo :
const options = {
bar: 'hello world'
}
const {
foo = []
} = options;
console.log(foo);
If this.options is ` undefined, you'll get errors,
options is not defined
const {
foo = []
} = options;
console.log(foo);
Or:
Cannot destructure property foo of 'undefined' or 'null'.
const options = undefined;
const {
foo = []
} = options;
console.log(foo);
If you run it through babel you'll see that if this.options.foo exists then it will be bound to the name 'foo' in that scope and if it doesn't then foo is set to an empty array.
Here is an in-depths article for you to better understand ES6 Destructuring https://hacks.mozilla.org/2015/05/es6-in-depth-destructuring/
Destructuring assignment allows you to assign the properties of an
array or object to variables using syntax that looks similar to array
or object literals. This syntax can be extremely terse, while still
exhibiting more clarity than the traditional property access.
For your sample script the assignment are looking for an object with property "foo" from right side. If it can not find it, it will assign foo with default value as empty array.
If the left side is null or undefined, the operator will throw an error "Uncaught TypeError: Cannot destructure propertyfooof 'undefined' or 'null'."
// A regular JS object.
this.options = {
'a': 'a',
'foo': 'bar',
'z': 'z'
}
console.log('this.options.foo =', this.options.foo);
// this.options.foo === 'bar'
// Get 'foo' out of this.options. Becomes undefined if 'foo' doesn't exist in this.options.
const { foo } = this.options;
console.log('foo =', foo);
// foo === this.options.foo === 'bar';
const { nope } = this.options;
console.log('nope =', nope);
// typeof nope === 'undefined'
// Get 'zzz' if it exists, otherwise fall back to 1234.
const { zzz = 1234 } = this.options;
console.log('zzz =', zzz);
// zzz === 1234;
// zzz was set by its default fallback value.

Object returns own property and preserves methods

I have an object that wraps some data:
function Obj1() {
var _foo = 'bar'
this.obj2 = {
'b': 'c'
}
this.method = function() {
return _foo
}
}
var obj1 = new Obj1()
Now when I call console.log(obj1); I want it to show me object obj2 content. The trick is that I need to still be able to call obj1.method and get value of _foo. How do I do that if it's even possible?
My thought was that sth like getter will be suitable, but can't figure out where and how to assign one.
As far as I understood you're trying to hide method property. To achieve this, use Object.defineProperty. Function will not be logged because enumerable property is false by default which prevents property from showing in console.log for example.
function Obj1() {
var _foo = 'bar'
this.obj2 = {
'b': 'c'
}
Object.defineProperty(this.obj2, 'method', {
value: function() {
return _foo;
}
});
return this.obj2;
}
var obj1 = new Obj1()
console.log(obj1);
console.log(obj1.method());
if i understand correctly, you can use prototype
Example
function Obj1() {
this.obj2 = {
'b': 'c'
}
}
Obj1.prototype.method = function() {
return 'bar';
}
var obj1 = new Obj1();
//prints only properties
console.log(obj1);
//prints method result
console.log(obj1.method())
Since you calling new Obj1(). The result variable var obj1 is a class object and not a function, for you to get the value of obj2 you will have to call obj1.obj2 in your console log. If you want obj1 to hold the value of obj2. Then use the following code
function Obj1() {
var obj2 = {
'b': 'c'
}
return this.obj2;
}
var obj1 = Obj1();
console.log(obj1);
This will give you the required result in the console log, but the object will no longer be a class object and will have only the value of obj2.
Sticking to your original snippet a factory looks like a good option:
function factory() {
var foo = 'bar';
var props = { b: 'c'};
var proto = {
method: function() { return foo; }
};
var obj = Object.create(proto);
Object.assign(obj, props);
return obj;
}
var obj = factory();
console.log(obj); // {b: 'c'}
console.log(obj.method()) // 'foo'
You could even pass props as an argument to get a more flexible way of spawning objects with an "unenumerable" method accessing private members.

JS closure not updating null when passing an object

Given the code with a closure, why is make_function returning null when I pass it an object?
Plnkr: http://plnkr.co/edit/L3nNeiibTMdR6GEnyMOX?p=preview
$(document).ready(everything);
function everything() {
var null_start = null;
var object_start = { obj: null };
function make_function (arg) {
return function () {
d3.select('#console')
.append('p')
.text(JSON.stringify(arg))
.style('color', 'blue');
};
}
// Make a function with arg=null
var func = make_function(null_start);
d3.select('#console').append('p').text('Expecting null here');
func();
null_start = {
foo: 5,
bar: 42
};
d3.select('#console').append('p')
.html('Expecting {foo:5, bar:42} here <span style="color:red;">Why is this still null???</span>');
func();
// Make a function with arg={obj:null}
func = make_function(object_start);
d3.select('#console').append('p').text("Expecting {obj: null} here");
func();
object_start.obj = {
foo: 5,
bar: 42
};
d3.select('#console').append('p')
.html('Expecting {obj: {foo:5, bar:42}} here <span style="color:red;">if this one is updated?</span>');
func();
}
Arguments passed in JavaScript are not passed by reference, but by a special "reference-copy". In other words, when you pass null_start to make_function, a copy of the null_start variable is passed.
Changes to the null_start variable would not be reflected by the variable passed to the function.
This is the reason why creating another function with the new value of the variable works as intended in your example.
Note that when passing objects, changes to the object will be reflected by the original variable as well. This is because objects are mutable in JavaScript. For example:
function change (obj) { obj.foo = 'bar'; }
var x = {};
change(x);
console.log(x.foo); // 'bar'
This is because an object is merely a box of keys pointing to values. Keys inside the object point to values, which can be modified, and reflected upon by the outer-scope of the function. But if we pass the value directly, it will not work:
function change (val) { val = 'fff'; }
var x = {foo: 'bar'};
change(x.foo);
console.log(x.foo); // 'bar'
Hope that helps!

Each loop to find if function exists?

var a = {
b: {
aa: 'hello',
bb: 'you',
cc: 'guys'
}
}
var b = function(){
$.each(a.b, function(x){
if( $.isFunction(x) == 'function' ){
alert(x);
};
});
};
var aa = function(){
var dude = 'hi there';
}
b();
I have an each loop inside the b function.
What i would like to do is loop through the a.b values, and find if a function exists with it's name. In this case the only one that should trigger is 'aa' as function 'aa exists.
is the isfunction line correct? or would typeof work?
In this case the only one that should trigger is 'aa' as function 'aa exists.
$.each() is iterating members of the Object passed to it. And, currently, the function aa is not a member of the Object a.b.
$.isFunction() isn't returning true because every member of a.b has a String value:
aa: 'hello',
bb: 'you',
cc: 'guys'
If you want the function as a member of the Object, you'll need to set it as the value of one of its properties:
a.b.aa = function () {
var dude = 'hi there';
};
This will replace the 'hello' value with a reference to the function.
To reuse the property names to lookup globals, you can use them on the global object (window in browsers).
var key = 'aa';
var val = window[key];
var b = function () {
$.each(a.b, function (key) {
if (key in window && $.isFunction(window[key])) {
alert(key + ' is a global function.');
}
});
};
Example: http://jsfiddle.net/cAaMf/
Though, note the "No wrap - in <body>" in the options of the Fiddle. This will only work for globals as no other scope object can be accessed within code.

Referring to previously defined properties within an object literal

When using object constructors, properties can be directly assigned to the value of previously defined properties:
var foo = new (function() {
this.bar = 5;
this.baz = this.bar;
})();
alert(foo.baz) // 5
I would like to refer to a previously defined property within an OBJECT LITERAL:
var foo = {
bar : 5,
baz : bar
}
alert (foo.baz) // I want 5, but evaluates to undefined
I know that I could do this:
var foo = {
bar : 5,
baz : function() {
alert(this.bar); // 5
}
But I want to assign baz directly to a value rather than a function. Any ideas?
No, you won't be able to use any properties of the object literal before it has been created. Your closest option is probably to use a temporary variable like so:
var tmp = 5,
foo = {
bar : tmp,
baz : tmp
}
If you are free to use ECMAScript 5 features, you could write a getter function for the baz property that instead returns the value of bar:
var yourObject = {
bar: 5
};
Object.defineProperty(yourObject, 'baz', {
get: function () { return yourObject.bar; }
});
You can also just build a literal by parts:
var foo = {bar:5};
foo.baz = foo.bar;
If you need to fit this inside an expression (instead of through multiple statements) you can try abusing the comma operator or you can make a helper function:
(Warning: untested code)
function make_fancy_object(base_object, copies_to_make){
var i, copy_from, copy_to_list;
for(copy_from in copies_to_make){
if(copies_to_make.hasOwnProperty(copy_from)){
copy_to_list = copies_to_make[copy_from];
for(var i=0; i<copy_to_list.length; i++){
base_object[copy_to_list[i]] = base_object[copy_from];
}
}
}
}
var foo = make_fancy_object(
{bar: 5},
{bar: ["baz", "biv"]}
);
//foo.baz and foo.biv should be 5 now as well.

Categories

Resources