var exports = module.exports = {} in NodeJS - javascript

What I get from previous posts and articles is that the exports object is located on the global object. I came across this code which confused me:
let blue = 'blue'
let red = 'red'
var exports = module.exports = {
red,
blue
};
This code sets module.exports to a variable called exports which then gets set to an object which gets exported.
I am confused however by this syntax:
Example1:
var exports = module.exports = {}
How does this work exactly? Because normally in JS you can't assing a variable two times. For instance this gives an error:
Example2:
let foo = 5 = 4;
How does the code in example 1 give no error while the code in example 2 does?

let foo = 5 = 4;
Cause its parsed from right to left:
let foo = (5 = 4);
And 5 is not a variable, so you cant assign stuff to it. However it works with an identifier:
let bar;
let foo = bar = 5;

Your interpretation of what the line is doing is incorrect.
This code sets module.exports to a variable called exports which then gets set to an object which gets exported.
What is actually happening is that the value { red, blue } is being assigned to module.exports, and then that same value ({ red, blue }) is being assigned to exports.
In JavaScript and other languages with similar syntax (C, C++, C#, Java) someAssignableThing = someValue is treated as an expression, and you can use a = b as a sub-portion of other expressions and chain as many together as you want.
As an expression someAssignableThing = someValue equates to "assign someValue to someAssignableThing and evaluate to the value someValue".
So the statement:
a = b = c = d = e = 5;
would assign the value 5 to a, b, c, d, and e.
It is a syntax error to have something on the left side of the = that cannot be assigned a value and that's why you get an error in the second case (you cannot assign a value to 5).

Related

var hoisting and value between redeclaration

Run below code in Node environment. Running it in browser console doesn't allow to redeclare variable of var.
console.log(a);
var a = 5;
According to hoisting, the above code is going to look like this
var a = undefined;
console.log(a); // undefined
a = 5;
a variable is being hoisted to the top of the file. JS Engine allocates memory for this variable before the execution. The question is why below code consols 5 instead of undefined.
var a = 5;
console.log(a);
var a = 6;
I'm looking at this code and imagining that it's going to look like this:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
I'd like to be sure of the answer instead of guessing. The JS Engine is smart enough to see that a variable is already declared and is going to ignore the next var expression and rehoisting in such case? So the output should be looking like:
var a = 5;
console.log(a); // 5
a = 6;
So it's like:
JS Engine sees for the first time declaration (in this case along with initialization) of a variable so it's allocating memory.
JS Engine sees for the second time declaration of a variable but is going to ignore the hoisting because variable of given name is already in the memory.
Am I wrong in something?
Preface: In modern JavaScript, var should never be used. Use let or const.
The JavaScript engine handles var in two steps:
Upon entering the global scope or a function scope, it processes every var in the entire scope, defining variables for the them initialized wit the value undefined. If a variable is declared more than once with var, it's exactly as though it were declared once.
Then it starts the step-by-step execution of the code. In that step-by-step execution, any initializer on a var statement (the = 5 in var a = 5) is considered an assignment. So var a = 5 is treated exactly like a = 5 at this point.
So in your example:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
It's as though you had written this:
var a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
var a = 6;
or this:
var a;
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
var a;
or even this (but please don't! :-) ):
var a = 5;
var a = undefined;
console.log(a); // undefined
var a = 6;
var a;
That is, first all the variables declared with var are created (and only once), then the code runs as though any initializers on them were assignments.
This is not how let, const, and class declarations are handled (collectively: lexically-scoped declarations). First: Multiple declarations in the same scope are an error (including if one of them is with var and the other is with one of the lexically-scoped ones). Second: They're hoisted (in Step 1 above), but the hoisted binding¹ is uninitialized until the declaration is executed in the step-by-step code, at which point it's initialized (either with the value from the initializer, or with undefined if it's just e.g. let a;). The time between entry to the scope and the point the binding is initialized is called the Temporal Dead Zone. var doesn't have it because var variables are initialized when they're created (with the value undefined), but let, const, and class declarations do.
¹ The term binding is the general term for variable-like things. In the code:
function example(p) {
var v;
let l;
const c = 42;
function f() {
}
class C {}
}
the bindings created upon entering the example function's scope are p (a parameter), v (a var variable), l (a let variable), c (a const constant), f (the binding created by a function declaration), and C (the binding created by a class declaration). (Note: function and class expressions are handled slightly differently.)

Cannot set backgroundColor when assigned to a variable

Trying to set a background color to the body of the document, I am puzzled as for why the following code does not work (tested in Chrome):
var a = 'blue';
var b = document.getElementById('body').style.backgroundColor;
b = a; // Not working.
while this works fine:
var a = 'blue';
var b = document.getElementById('body').style;
b.backgroundColor = a; // works.
and this works too:
document.getElementById('body').style.backgroundColor = 'blue'; //works
Can someone explain why the first version does not work?
This is a classic pointer user error. I've made this mistake many many times.
var a = 'blue';
var b = document.getElementById('body').style.backgroundColor;
b = a; // Not working.
The above code doesn't work because the value of document.getElementById('body').style.backgroundColor is being copied to the identifier b. When you reassign b with the value of a you're not re-assigning the value of document.getElementById('body').style.backgroundColor.
That why thiis code works:
var a = 'blue';
var b = document.getElementById('body').style;
b.backgroundColor = a; // works.
because you're saving the value of style to the identifier b. b a complex type. Now when you reassign b.style you're also re-assigning document.getElementById('body').style because the identifier b holds the same reference as document.getElementById('body').style.
Let me try to break that down:
In javascript (and many other languages) when you assign a complex type (i.e. an object or an array) to an identifier, you're actually assigning a reference to "something" in memory. You can think of the identifier's value being an "address" instead of holding all the values of the complex type, and when you try to pull values out of the identifier using the obj.prop syntax, you're actually telling the program to go to the address and fetch the desired value.
Therefore, if any property in that "something" changes, the references (aka pointers) will also reflect that change:
const complexType = {
a: 'something'
}
const x = complexType;
const y = complexType;
console.log(`from x: ${x.a}`);
console.log(`from y: ${y.a}`);
complexType.a = 'something else';
// notice how they change
console.log(`from x again: ${x.a}`);
console.log(`from y again: ${y.a}`);
To contrast, simple/primitive types are always copied on assignment. This means that the identifier hold the full value instead of holding an address. That means whenever you assign an identifier to a simple/primitive value, that value will persist even when you change the original value.
// original value
let simpleType = 'something';
let a = simpleType;
let b = simpleType;
console.log(`from a: ${a}`);
console.log(`from b: ${b}`);
// re-assign
simpleType = 'something else';
// notice how they *don't* change
console.log(`from a again: ${a}`);
console.log(`from b again: ${b}`);
So to conclude, document.getElementById('body').style.backgroundColor returns a simple type. This simple type get copied on assignment. That's why you can't do this:
var a = 'blue';
var b = document.getElementById('body').style.backgroundColor;
b = a; // Not working.
Hope that helps!
var b = document.getElementById('body').style.backgroundColor;
Acts like a getter and returns the background color of the element with the ID of body. It doesn't hold a pointer to it the way you seem to be thinking. So b would contain a string like purple or whatever color you set.
Then you're doing b = a; which will just overwrite the value of b with a's value.

Javascript "Equal Sequence" meaning

Sometimes in the internet I see a syntax that is strange to me. Something like:
console.log = console.error = console.info = console.debug = console.warn = console.trace = function() {}
How does this "equal" sequence work?
Thanks.
An assignment operator assigns a value to its left operand based on the value of its right operand.
Consider:
a = b = c = d = 5;
The expression is resolved right to left so:
d = 5 and c = d (which is 5), b = c (5) and so on.
In your example those console methods are all being (re)defined as an empty function.
See: MDN: Assignment Operators for more info.
With assignments, the operations are resolved from right to left. So the right most value will be populated into all the preceding variables.
What you describe can be easily explained by analogy using a simpler example:
// Normal variable assignment
var a, b;
a = 15;
b = 15;
console.log("a: "+a+" , b: "+b);
// Assing the same value to two variables
var c, d;
c = d = 15;
console.log("c: "+c+" , d: "+d);
// Assign the same value to two variables and make it a function
var e, f;
e = f = function(){ console.log("Hi!"); };
// Call both of the variables' functions
e(); f();
Starting from variables a and b, you then go to c and d that are given the same value. The takeaway here is that you can assign the same value to two variables and the expression will be evaluated from right to left, so in effect it's like assigning the two variables' values separately. However, this does not mean that chaning one will change the other as well.
Finally, see what happens with e and f. These are assigned a function instead of a value, so you can then call them as if they were functions.
Short version: Expression gets resolved from right to left. The assignment is by value, not by reference, meaning that changing one of the variables' value will not affect the others. Finally, if you assign a function to your variables, you can then use their names to call the function that is their value.

Use of setter and Object.create();

I'm currently reading the series of books You don't know JS. In one of the books it's written :
If a foo is found higher on the [[Prototype]] chain and it’s a setter
(see Chapter 3), then the setter will always be called. No foo will be
added to (aka shadowed on) myObject, nor will the foo setter be
redefined.
Now I tried the following code:
var a = {
set t(tet) {
this._t_ = tet;
},
get t() {
return this._t_ ;
}
};
a.t = 5;
var b = Object.create(a);
b.t = 4;
console.log(a.t);
console.log(b.t);
For my suprise the logs print out 5 and 4 in that order. From what is written in the book I expect to see two 4 being printed, but it seems there is actually shadowing. Why is that ?
Thanks to the answer by Quentin, this is actually the code that demonstrates the functionallity I wanted :)
var a = {
t : undefined,
set t(tet) {
t = tet;
},
get t() {
return t ;
}
};
a.t = 5;
var b = Object.create(a);
b.t = 4;
console.log(a.t);
console.log(b.t);
The 4 and 5 are stored in _t_ on the two different objects, not in t.
t is still the setter and getter in both cases (although the value of this varies because it is called in the context of two different objects).

Multiple assignment confusion

I understand that the assignment operator is right associative.
So for example x = y = z = 2 is equivalent to (x = (y = (z = 2)))
That being the case, I tried the following:
foo.x = foo = {a:1}
I expected that the object foo would be created with value {a:1} and then the property x will be created on foo which will just be a reference to the foo object.
(This is actually what happens if I was to separate the multiple assignment statement into two separate statements foo = {a:1};foo.x = foo; )
The outcome was actually:
ReferenceError: foo is not defined(…)
So then I tried the following:
var foo = {};
foo.x = foo = {a:1};
Now I don't get the exception anymore but foo.x is undefined!
Why is the assignment not working as I expected?
Disclaimer: The 'duplicate' question seems to be very different to the one that I'm asking, as the issue there is that the variables that were created in the assignment were global, as apposed to variables created with the var keyword. That's not the issue here.
There's an important difference between associativity and order of evaluation.
In JavaScript, even though the assignment operator groups right to left, the operands are evaluated left to right before the actual assignments are performed (which do occur right to left). Consider this example:
var a = {};
var b = {};
var c = a;
c.x = (function() { c = b; return 1; })();
The variable c initially references a, but the right-hand side of the assignment sets c to b. Which property gets assigned, a.x or b.x? The answer is a.x because the left-hand side is evaluated first, when c still references a.
In general, the expression x = y is evaluated as follows:
Evaluate x and remember the result.
Evaluate y and remember the result.
Assign the result from step 2 to the result of step 1 (and return the former as the result of the expression x = y).
What happens with multiple assignments, as in x = (y = z)? Recurse!
Evaluate x and remember the result.
Evaluate y = z and remember the result. To do this:
Evaluate y and remember the result.
Evaluate z and remember the result.
Assign the result from step 2.2 to the result of step 2.1 (and return the former as the result of the expression y = z).
Assign the result from step 2 to the result of step 1 (and return the former as the result of the expression x = (y = z)).
Now let's look at your example, slightly edited:
var foo = {};
var bar = foo; // save a reference to foo
foo.x = (foo = {a:1}); // add parentheses for clarity
foo.x is evaluated before foo gets assigned to {a:1}, so the x property gets added to the original {} object (which you can verify by examining bar).
Edited the answer to make it simple
First of all you have to understand the differnce between Reference- and Value- Type.
var foo = {};
foo variable holds a Reference to an object in memory, lets say A
Now, there are two arts of accessors: Variable Accessor and Property Accessor.
So foo.x = foo = {a:1} can be understood as
[foo_VARIABLE_ACCESSOR][x_PROPERTY_ACCESSOR] = [foo_VARIABLE_ACCESSOR] = {a:1}
!!! Accessor chain is evaluated first to get the last accessor, which is then evaluated associative.
A['x'] = foo = {a:1}
Property Accessor are seperated into setters and getters
var foo = { bar: {} };
foo.bar.x = foo = {a:1}
Here where have decared two nested objects foo and bar. In memory we have then two object A and B.
[foo_VAR_ACCESSOR][bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> A[bar_PROP_GETTER][x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B[x_PROP_ACCESSOR] = [foo_VAR_ACCESSOR] = {a:1}
> B['x'] = foo = {a: 1}
Here you have little example
var A = {};
var B = {}
Object.defineProperty(A, 'bar', {
get () {
console.log('A.bar::getter')
return B;
}
})
Object.defineProperty(B, 'x', {
set () {
console.log('B.x::getter')
}
});
var foo = A;
foo.bar.x = foo = (console.log('test'), 'hello');
// > A.bar.getter
// > test
// > B.x.setter
Great question. The thing to remember here is that JavaScript uses pointers for everything. It's easy to forget this since it is impossible to access the values representing memory addresses in JavaScript (see this SO question). But realizing this is very important in order to understand many things in JavaScript.
So the statement
var foo = {};
creates an object in memory and assigns a pointer to that object to foo. Now when this statement runs:
foo.x = foo = {a: 1};
the property x is actually getting added to the original object in memory, while foo is getting assigned a pointer to a new object, {a: 1}. For example,
var foo, bar = foo = {};
foo.x = foo = {a: 1};
shows that if foo and bar are pointing to the same object initially, bar (which will still point to that original object) will look like {x: {a: 1}}, while foo is simply {a: 1}.
So why doesn't foo look like {a: 1, x: foo}?
While you are right in that assignments are right associative, you must also realize that the interpreter still reads from left to right. Let's take an in-depth example (with some bits abstracted out):
var foo = {};
Okay, create an object in memory location 47328 (or whatever), assign foo to a pointer that points to 47328.
foo.x = ....
Okay, grab the object that foo currently points to at memory location 47328, add a property x to it, and get ready to assign x to the memory location of whatever's coming next.
foo = ....
Okay, grab the pointer foo and get ready to assign it to the memory location of whatever's coming next.
{a: 1};
Okay, create a new object in memory at location 47452. Now go back up the chain: Assign foo to point to memory location 47452. Assign property x of the object at memory location 47328 to also point to what foo now points to--memory location 47452.
In short, there is no shorthand way to do
var foo = {a: 1};
foo.x = foo;

Categories