This is apparently a valid destructuring assignment despite qux depending on bar:
const { foo, bar, qux = () => bar } = myObject;
How does this work since the documentation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) does not cover how dependent assignments like the example above works.
qux: () => 'qux' means to declare const qux, whose value will be extracted as the property qux from myObject.
However, if myObject has no such property, the const is declared as if you'd just written const qux = () => bar. Therefore, the default value of qux is an arrow function.
Note that for the default to be used, the qux property must be absent or set to undefined in myObject. The default will not be used if qux in myObject is null or any other value.
Also note that this will work:
const { foo, qux = () => bar, bar, x=qux() } = {};
But this will throw ReferenceError: Cannot access 'bar' before initialization:
const { foo, qux = () => bar, x=qux(), bar } = {};
This is because when you do qux = () => bar, it's not attempting to access an undeclared variable yet. However, invoking qux() does attempt to access the bar variable, so the order is important.
Related
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.
The new object rest/spread syntax has some surprisingly nice applications, like omitting a field from an object.
Is there a (proposed) way to also assign to several properties of an object, the values from variables with the same names? In other words, a shorter way to say:
o.foo = foo;
o.bar = bar;
o.baz = baz;
Note: Without losing the existing properties of o, only adding to them.
Use Object.assign:
const o = { initial: 'initial' };
const foo = 'foo';
const bar = 'bar';
const baz = 'baz';
Object.assign(o, { foo, bar, baz });
console.log(o);
Note that both shorthand property names and Object.assign were introduced in ES6 - it's not something that requires an extremely up-to-date browser/environment.
Something similar that reassigns the reference to the object would be to initialize another object by spreading o and list foo, bar, baz:
let o = { initial: 'initial' };
const foo = 'foo';
const bar = 'bar';
const baz = 'baz';
o = { ...o, foo, bar, baz };
console.log(o);
const foo = 'foo';
const bar = 'bar';
const baz = 'baz';
const o = {foo, bar, baz};
console.log(o);
I'm learning javascript and want to define 2 classes inside an object. The second class is derived from the first class.
It looks like:
let foo = {
bar: (function () {
let bar_msg = '';
class bar {
constructor(msg) {
bar_msg = msg;
}
}
return bar;
}()),
baz: (function () {
let baz_msg = '';
class baz extends foo.bar {
constructor(msg) {
super();
baz_msg = msg;
}
}
return baz;
}())
};
Error message:
Uncaught ReferenceError: foo is not defined
Then, I've tried to split the classes to multiple parts:
let foo = {};
foo.bar = (function () {
let bar_msg = '';
class bar {
constructor(msg) {
bar_msg = msg;
}
}
return bar;
}());
foo.baz = (function () {
let baz_msg = '';
class baz extends foo.bar {
constructor(msg) {
super();
baz_msg = msg;
}
msg() {
return baz_msg;
}
}
return baz;
}());
let b = new foo.baz('hi!');
console.log(b.msg());
It works.
So, my question is: Why do I get the difference? I cannot show some google search results because I have no idea about the keywords.
An object is 'completely' defined when you declared it and assign it a value like so:
var foo = 'bar';
You may declare a variable but not assign it to any value:
var foo;
and when you try to access it, it will give you undefined. You may also assign a value to an undeclared variable:
foo = 'bar';
And the javascript will automatically create a global variable foo. If you try to access a variable that has never been declared or implicitly declared (e.g foo = 'bar'). It will throw an error;
console.log(iDoNotExist); // will throw an error!
In your first code, the class baz, could not locate foo in its own scope, so it will go to the global scope. But since it's also not available in the global scope and it has neither been declared explicitly nor implicitly, it will throw an error.
In the second code, you have explicitly declared foo and assign it to an object and added a property bar right after it. Any code written after foo is declared would be able to access it, including when you assign the property bar to foo, in which a class called baz tries to extend foo.bar. If the piece of code that tries to extend foo or foo.bar is written after the definition of foo. It will throw an error. See snippet below for example:
class baz extends foo {
constructor(msg) {
super();
baz_msg = msg;
}
msg() {
return baz_msg;
}
}
let foo = {};
let b = new foo.baz('hi!');
In summary, you get the difference because the accessibility of foo. In the first variable you try to access it before it is both neither explicitly nor implicitly defined, resulting in an error. In the second variable, you've declared and assigned it to a value and try to access it after, which is perfectly legal.
Can someone explain why if I add a property to foo, somehow bar also inherits that property when I do this:
var foo = bar = {};
foo.hi = 'hi';
console.log(bar); //Object {hi: "hi"}
How does this work? I'm setting properties on foo, not bar. I realized I passed the object to bar and then bar to foo, but I must be missing something here.
Integer assignment works differently and more sense (to me anyhow):
var foo = bar = 5;
foo = 4;
console.log(bar); // 5
Objects are passed by reference in JavaScript. Strings and number literals are not. In your code foo === bar, is the same object. You can simply declare the variables separately:
// Two different object instances
var foo = {};
var baz = {};
by doing foo = bar = {};, foo and bar are pointers to same object. so when you do:
foo.hi = 'hi';
It sets bar.hi also, because foo and bar point to the same object. To make it different, you should do:
var foo = {}, bar = {};
foo.hi = 'hi';
Consider this javascript code:
var bar = function () { alert("A"); }
var foo = bar;
bar = function () { alert("B"); };
foo();
When running this code I get "A". Is this behavior a part of javascript specification and can I rely on it?
In other examples, nothing was passed by value; everything was passed by reference.
bar and foo are BOTH pointers
All vars/handles to NON primitive objects in javascript are pointers; pointers ARE native to javascript, they are the default.
var bar = function () { alert("A"); } //bar is a pointer to function1
var foo = bar; //pointer copied; foo is now also a pointer to function1
bar = function () { alert("B"); }; //bar points to function2
foo(); //foo is still a pointer to function1
You will run into hidden errors and bugs if you think they are copies. Especially so if you work with complex objects. For example
function person(name){this.name = name}
var john = new person("john")
var backup = john
backup.name //john
john.name = "jack"
backup.name //jack, NOT john
To really COPY a non-primitive in javascript takes more work than just a = b.
For example:
function person(name){ this.name = name}
var john = new person("john")
var backup = new Object()
backup = JSON.parse(JSON.stringify(john))
backup.__proto__ = john.__proto__ //useful in some cases
john.name = "jack"
backup.name //john
Yes that is expected and by design.
Your question is basically: does foo reference bar as a pointer or reference would in another language?
The answer is no: the value of bar at the time of assignment is assigned to foo.
I'm a bit late here but I thought I'd give an answer anyways and flesh something out.
It's best not to think in terms of pointers and memory references when discussing the internals of JavaScript (or ECMAScript) when dealing with the specifications. Variables are environment records internally and are stored and referenced by name, not memory address. What your assignment statement is doing, internally and by design, is looking up the environment record name (either "foo" or "bar") and assigning the value to that record.
So,
var bar = function () { alert("A"); }
is assigning the environment record "bar" the value (anonymous function).
var foo = bar;
internally calls GetValue("bar") which retrieves the value associated with the record "bar" and then associates that value with the record "foo". Hence, afterwards the original value of bar can still be used as it's now associated with foo.
Because JavaScript references by string and not memory address is precisely why you can do things like this:
someObject["someProperty"]
which is looking up the value based on the property name.
You are assigning the value of an anonymous function to a variable not a pointer.
If you want to play with pointers, you can use objects that are passed by reference, not copy.
Here are some examples:
"obj2" is a reference of "obj1", you change "obj2", and "obj1" is changed. It will alert false:
var obj1 = {prop:true},
obj2 = obj1;
obj2.prop = false;
alert(obj1.prop);
"prop" points to a property that is not an object, "prop" is not a pointer to this object but a copy. If you change "prop", "obj1" is not changed. It will alert true:
var obj1 = {prop:true},
prop = obj1.prop;
prop = false;
alert(obj1.prop);
"obj2" is a reference to the "subObj" property of "obj1". if "obj2" is changed, "obj1" is changed. It will alert false:
var obj1 = {subObj:{prop:true}},
obj2 = obj1.subObj;
obj2.prop = false;
alert(obj1.subObj.prop);
Yes, there's nothing special about the fact that the variables are referring to functions, there's no aliasing involved.
var bar = 1;
var foo = bar;
bar = "something entirely different";
// foo is still 1
Yes, this is the correct behavior.
//create variable bar and assign a function to it
var bar = function () { alert("A"); }
//assign value of bar to the newly created variable foo
var foo = bar;
//assign a new function to the variable bar
//since foo and bar are not pointers, value of foo doesn't change
bar = function () { alert("B"); };
//call the function stored in foo
foo();
This is assigning a variable to an unnamed function, not a pointer to a function
Yes, you've created a pointer to the original "A" function. When you reassign bar, you're reassigning it, but you're still leaving any references to the old function alone.
So to answer your question, yes, you can rely on it.
Those are not function pointers (and there are no pointers in JS natively). Functions in JS can be anonymous and are first class objects. Hence
function () { alert("A"); }
creates an anonymous function that alerts "A" on execution;
var bar = function () { alert("A"); };
assign that function to bar;
var foo = bar;
assign foo to bar, which is the function "A".
bar = function () { alert("B"); };
rebind bar to an anonymous function "B". This won't affect foo or the other function "A".
foo();
Call the function stored in foo, which is the function "A".
Actually in languages where there are function points e.g. C it won't affect foo either. I don't know where you get the idea of getting "B" on reassignment.
void A(void) { printf("A\n"); }
void B(void) { printf("B\n"); }
typedef void(*fptr_t)(void);
fptr_t foo = A;
fptr_t bar = foo;
bar = B;
foo(); // should print "A"
I would just like to add this also works for pre-defined named functions as well:
function myfunc() { alert("A"); }
var bar = myfunc;
var foo = bar;
bar = function () { alert("B"); };
foo();
This will do the same thing, indicating that function names act like array names (pointers).
For each FunctionDeclaration f in code, in source text order do:
Let fn be the Identifier in FunctionDeclaration f.
Let fo be the result of instantiating FunctionDeclaration f as described in Clause 13.
Let funcAlreadyDeclared be the result of calling env’s HasBinding concrete method passing fn as the argument.
If funcAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing fn and configurableBindings as the arguments.
References
ECMAScript-5: Section 10.5