JavaScript spread operator in arrow functions - javascript

This snippet of JavaScript code alerts 1 as answer. Can anyone please explain how this code executes?
const b = [1,2,3];
const f = (a, ...b) => a+b;
alert( f( 1 ) );

There are a couple of things going on here. The main one is that you're shadowing b, so the b outside the function isn't used within it. Instead, within it, you're creating a new array (because you've used a rest parameter, ...b) and assigning it to the b parameter. Since you call f with just one parameter, that array is empty. 1+[] is "1" because when either of the operands to + isn't a primitive (arrays aren't primitives), it's coerced to a primitive, and coercing an array to a primitive (indirectly) results in doing a .join(",") on the array. With a blank array, .join(",") is "". Then, since one of the operands is a string, the other operand (1) is coerced to string ("1") and it does "1"+"" which is, of course, "1". (Details on that last bit in the spec.)

f(1) is the same as 1 + []
f(1,2,3) is the same as 1 + [2, 3]
That's all...
The first line const b = [1,2,3]; is not used because the b in the lambda expression is the argument, not the constant declared before.

You can reference variables in a function call, however, when you define a function expression, the parameters name do not refer to any variables.
You'll get the expected result if you call the function like this:
alert(f(1, b));

It takes the rest parameters ... as an array b.
While this is empty, it is converted to an empty string and both operands are treated as string, because if one is a string, then it adds all values as string.
The result is '1'.
const b = [1, 2, 3];
const f = (a, ...b) => a + '';
console.log(typeof f(1));

Reproducing this in my browser's development tools console looks like this:
> b = [1,2,3]
> f = (a, ...b) => a+b
> f(1)
< "1"
// so it results in the string 1, why's that?
// lets try logging what a and b are in the function
> g = (a, ...b) => console.log("a=%o, b=%o", a, b)
> g(1)
< a=1, b=[]
// ah, so b is an array, which is what the spread operator does
// (gathers all remaining arguments into an array, so the question
// is then what does JavaScript return for the number 1 added to an empty array?
> 1 + []
< "1"
This behavior is one of the many quirks of JavaScript when using the + operator on different types.

Related

Puzzled with the workings of these variable assignments?

Tried to figure out the working logic behind the results. I couldn't.
Could someone please help explain how Javascript works the output of each of the 4 situations and the reason behind it?
I think the 1st and 2nd are similar situations.
Thanks
var num = (1, 2, 3); // num = 3 (last number returned, No error generated)
console.log(num)
y = (1, 2, 3); // y = 3 (last number returned, No error generated)
console.log(y)
z = 1, 2, 3; // z = 1 (first number returned, No error generated)
console.log(z)
// Uncomment the following to run: (gives an error)
//var m = 1, 2, 3; // Error: Unexpected number
Your first three snippets are all basically the same. They demonstrate usage of the Comma Operator.
The Comma Operator a, b first evaluates a and throws away the result, then evaluates b and returns the result. It is left-associative, so a, b, c is the same as (a, b), c.
Observe:
const foo = () => { console.log("foo"); return 23; };
const bar = () => { console.log("bar"); return 42; };
const qux = () => { console.log("qux"); return 99; };
console.log((foo(), bar()));
// foo
// bar
// 42
console.log((foo(), bar(), qux()));
// foo
// bar
// qux
// 99
Note that I could have written the three functions like this, but of course it doesn't make much sense to use the Comma Operator while trying to explain it:
const foo = () => (console.log("foo"), 23);
const bar = () => (console.log("bar"), 42);
const qux = () => (console.log("qux"), 99);
console.log((foo(), bar(), qux()));
// foo
// bar
// qux
// 99
In your first snippet, you have a Variable Declaration statement, which declares a variable, evaluates the initializer, and binds the value of the initializer to the variable. The initializer in this case is 1, 2, 3, which is equivalent to (1, 2), 3. So, it will evaluate 1, throw away the result, evaluate 2 return the result 2 as the result of evaluating the sub-expression 1, 2 but then immediately throw it away, evaluate 3 and return the result 3 as the result of the evaluation.
Therefore, it binds the value 3 to the variable num.
The second snippet is exactly the same, except it is an Assignment Expression, not a Variable Declaration, i.e. in Strict Mode, it will assign to a variable named y that was previously declared with let or var and error out with a static error otherwise. In Loose Mode, it will assign to a variable named y that was previously declared with let or var, or to a property named y of the global implicit object otherwise.
The evaluation of the right-hand-side of the assignment is exactly the same as above.
Snippet #3 uses again the Comma Operator. The Comma Operator has higher precedence than the Assignment Operator (the way ECMAScript is specified, it does not contain a simple operator precedence table, rather, the precedence is implicit in the grammar, so you'll either have to read the entire grammar or believe me), so your snippet #3 is equivalent to
((z = 1), 2), 3;
Again, it works just the same as snippets #1 and #2: first, the expression z = 1 is evaluated (which has the side-effect of assigning 1 to z) and the result is thrown away, then the expression 2 is evaluated and the result is thrown away, then the expression 3 is evaluated, and the result is returned.
So, the result of the entire expression is 3, but since that result is not assigned to anything, not returned, not printed, it is simply thrown away. All that is left is the side-effect that happened while evaluating the first expression, namely assigning 1 to z.
The last expression is different, because it does not demonstrate the use of the Comma Operator. It is, again, a Variable Declaration, just like snippet #1. A Variable Declaration basically is a list of comma-separated identifiers with optional initializers. The reason you get an error here, is that 2 is not a valid identifier for a variable.
Observe:
var n = 1, o, p;
// Declares variables `n`, `o`, `p`, initializes `n` to `1`.
console.log(n, o, p);
// 1 undefined undefined
Now, for comparison, your snippet #4 is ecactly the same:
var m = 1, 2, 3;
// Declares variables `m`, `2`, `3`, but `2` and `3` are not legal names.
The major difference between this snippet #4 and the other three is that the comma means a completely different thing here. In snippets #1 to #3, it is the Comma Operator, whereas in snippet #4, it is part of the Variable Declaration syntax.
There are other places where the comma can mean something different, for example parameter lists (function foo(a, b, c) or (a, b, c) => bla) or argument lists (foo(1, 2, 3)). In both cases, the comma is not the Comma Operator but part of the parameter list or argument list syntax to separate the parameters or arguments.
Observe:
console.log(1, 2, 3);
// 1 2 3
console.log((1, 2, 3));
// 3
In var num = (1, 2, 3); num is being set to the value of the final expression in the set. It's like a shorthand to evaluate multiple expressions and return the last value.
y = (1, 2, 3); is the same thing when you're at top level scope.
z = 1, 2, 3; is three expressions, z = 1, 2, and 3. z is set to 1, but if you logged the output of that line, you'd get 3.
var m = 1, 2, 3; is a syntax error because in this situation, the interpreter is expecting a single var pattern like var m = 1, n = 2, o = 3.

Why is JavaScript passing by reference? [duplicate]

This question already has answers here:
Why isn't this object being passed by reference when assigning something else to it?
(4 answers)
Closed 9 years ago.
var a = [3, 4, 5];
var b = [6, 7, 8];
function why() {
b = a;
b[0] = 1;
alert(a[0] + ' ' + b[0]);
}
why();
The result is a[0]=1, b[0]=1;. It seem likes JavaScript is passing by reference?
But in this case:
var a = [3, 4, 5];
var b = [6, 7, 8];
function why() {
b = a;
b = [1, 2, 3];
alert(a + ' ' + b);
}
why();
The result is a=[3,4,5] and b = [1,2,3]. Why is it passing by value?
How to avoid passing by reference?
The value of a non primitive variable, in JavaScript like in most object languages, is a reference to the object.
In the second case, you're not changing the array, but the reference that is stored in b.
If you want to copy your array, use
var c = a.slice();
Then c and a will evolve independently.
Because that's how variables work.
In the first case, you're setting both variables to the same instance of one array, then modifying that array through b (b[0] = 1). You could also modify the same array through a, but the point is b and a point to the same array; b does not "point" to a.
In the second, you're setting both to the same instance of one array (b = a) but then setting b to an instance of an entirely new, different array (b = [1,2,3]). You're changing the array that b points to, you're not affecting a because, as I said, b didn't point to a, it pointed to the same value as a.
The variables b and a are pointing to an object. When you do b = // something, you're changing where the variable points to. You're not changing the underlaying object.
In the first code, b = a means that b and a are now pointing to the same object. Therefore, when you make a change to one, it's reflected in the other.
However, in the second example, you're first pointing b to the same object as a, but then pointing b to a new array ([1,2,3]). The fact it was pointing to a for a short amount of time is irrelevant.
Both cases are pass-by-value. JavaScript is always pass-by-value, there is no way to pass by reference. In particular, the value being passed is always a pointer to an object. (Actually, primitives are passed by value directly, but the difference can only be observed for mutable values and primitives are immutable, so it doesn't make a difference.)
In the first case you are mutating the object the pointer points to. But the reference doesn't get changed, only the object the reference points to. The reference still points to the same object, the object just looks different than it did before.
This specific case of pass-by-value-where-the-value-is-a-pointer is sometimes called call-by-object-sharing, call-by-object or call-by-sharing, and is the way argument passing works in pretty much all object-oriented languages: Smalltalk, Python, Ruby, Java, C# (by default, you can pass-by-reference by explicitly specifying the ref modifier) etc.
In first case are changing one value of array item. b now equals to a so a[0] and b[0] gives same value.
In second case even though you made b point to a, you assigned a new object to b. so again b and a are two different objects ...so different values.
After all these great Answers, there is not really more to say, so heres an explanation based on the ECMAScript 5 specification.
Also a deepclone function at the end of the Answer.
As defined in the ES5 specification,
11.13.1 Simple Assignment ( = )
The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:
Let lref be the result of evaluating LeftHandSideExpression.
Let rref be the result of evaluating AssignmentExpression.
Let rval be GetValue(rref).
Throw a SyntaxError exception if the following conditions are all true:
Type(lref) is Reference is true
IsStrictReference(lref) is true
Type(GetBase(lref)) is Environment Record
GetReferencedName(lref) is either "eval" or "arguments"
Call PutValue(lref, rval).
Return rval.
So whats happening when we reach point 3 and rref is an Object, is
§8.7.1 (section 4b of In GetValue(V) //V==rref is the interesting point)
4.
If IsPropertyReference(V), then
(b) Return the result of calling the get internal method using base as its this value, and passing GetReferencedName(V) for the argument.
Now at this point rval holds the reference to the Object, which then gets put in at 5.
Where §8.7.2 (again 4b of PutValue(V,W) //V==lref , W==rval is the interesting part) comes into play.
Note: W is atm the reference to the Object you want to assign and not the value
4.
Else if IsPropertyReference(V), then
(b) Call the put internal method using base as its this value, and passing GetReferencedName(V) for the property name, W for the value, and IsStrictReference(V) for the Throw flag.
As you can see in your case the value of b which is atm a reference to the Object [6, 7, 8] gets replaced with the result of, so to speak GetValue(rref), which is a reference to [1, 2, 3];
However
Apparently you are looking for a deep clone function
Here is one.
Object.defineProperty(Object.prototype, "clone", {
value: function (deep) {
var type = Object.prototype.toString.call(this).match(/^\[object (.+?)\]$/)[1];
if (type !== "Object") {
return this.valueOf;
}
var clone = {};
if (!deep) {
for (var prp in this) {
clone[prp] = this[prp];
}
} else {
for (var prop in this) {
if (typeof this[prop] !== "undefined" && this[prop] !== null)
clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone((typeof deep == "boolean" ? deep : (deep - 1))));
else
clone[prop] = "";
}
}
return clone;
},
enumerable: false
});
Object.defineProperty(Array.prototype, "clone", {
value: function (deep) {
var clone = [];
if (!deep) clone = this.concat();
else this.forEach(function (e) {
if (typeof e !== "undefined" && e !== null)
clone.push((typeof e !== "object" ? e : e.clone((deep - 1))));
else
clone.push("");
});
return clone;
},
enumerable: false
});
var a = [1, [2, { a: 3 } ], 4];
var b = a.clone(Infinity);
a[1][1]["a"] = "cloned";
console.log(a[1][1]["a"], b[1][1]["a"]); //"cloned" , 3
And a Demo on JSBin
To use it just call .clone(levelOfDeepness) on an Object or Array
Note: I used the Objects prototype for simplicities sake as i can call directly .clone of Object and Array elements while cloning them (gives a performance boost over the type checking variant in a single function)

The difference between assert.equal and assert.deepEqual in Javascript testing with Mocha?

I'm using Mocha to test a small module in my Express.js application. In this module, one of my functions returns an array. I want to test whether or not the array is correct for a given input. I am doing so like this:
suite('getWords', function(){
test("getWords should return list of numbers", function() {
var result = ['555', '867', '5309'];
assert.equal(result, getWords('555-867-5309'));
});
});
When this runs, I get the following assertion error:
AssertionError: ["555","867","5309"] == ["555","867","5309"]
However, when I change my test to an assert.deepEqual, the test passes fine. I was wondering if it was a case of == vs ===, but if I enter
[1,2,3] === [1,2,3]
into the node.js command line, I still get false.
Why do arrays not compare the way other values do (e.g. 1 == 1)? and what is the difference between assert.equal and assert.deepEqual?
Why do arrays not compare the way other values do (e.g. 1==1)
Numbers, strings, booleans, null, and undefined are values, and are compared as you might expect. 1 == 1, 'a' == 'a', and so on. The difference between === and == in the case of values is that == will attempt to perform type conversion first, which is why '1' == 1 but not '1' === 1.
Arrays, on the other hand, are objects. === and == in this case do not signify that the operands are semantically equal, but that they refer to the same object.
what is the difference between assert.equal and assert.deepEqual?
assert.equal behaves as explained above. It actually fails if the arguments are !=, as you can see in the source. Thus it fails for your arrays of numbers strings because although they are essentially equivalent, they are not the same object.
Deep (aka structural) equality, on the other hand, does not test whether the operands are the same object, but rather that they're equivalent. In a sense, you could say it forces objects to be compared as though they're values.
var a = [1,2,3]
var b = a // As a and b both refer to the same object
a == b // this is true
a === b // and this is also true
a = [1,2,3] // here a and b have equivalent contents, but do not
b = [1,2,3] // refer to the same Array object.
a == b // Thus this is false.
assert.deepEqual(a, b) // However this passes, as while a and b are not the
// same object, they are still arrays containing 1, 2, 3
assert.deepEqual(1, 1) // Also passes when given equal values
var X = function() {}
a = new X
b = new X
a == b // false, not the same object
assert.deepEqual(a, b) // pass, both are unadorned X objects
b.foo = 'bar'
assert.deepEqual(a, b) // fail!

Something = Something = Something... What?

I'm reading some code and I see a comparison that's laid out like this:
a = b = c
Seeing as how searching Javascript about equal or comparison yields remedial results, anyone care to explain what's going on?
EDIT: These are all objects or object properties that we're talking about here, should have specified.
DOUBLE EDIT: This is inside of an Object.defineProperties() block.
= is an operator. It takes two arguments: a variable reference and an expression. It assigns the value of the expression to the variable, and returns the assigned value.
As a result, you can chain them and it equates to this:
a = (b = c)
In other words, assign b to the value of c, then assign that value to a also.
a = b = c is just shorthand expression for:
b = c;
a = b;
if(a = b) will always return true because it is assigning, instead of comparing. To compare, the statement should read: if(a == b).
The = operator associates right-to-left and evaluates to the value that was assigned.
So this:
a = b = c;
Means this*:
b = c;
a = c;
* Unless you're dealing with properties.
That is not a comparison. It is an assignment of the value of c to the b and a variables.
Assignment works right to left, so it is an assignment of c to b first. Then the return value of that operation is assigned to a.
The return value of an assignment operation is the value that was assigned, so a will get the same value assigned to b.
It equates this;
b = c; a = b;

Are elements of Javascript arrays processed in a defined order?

For example:
var a = [];
function p(x) { a.push(x); }
[[p(1),p(2)],p(3),[p(4),[p(5)]],p(6)]
a == [1,2,3,4,5,6] // Always true?
Is 'a == [1,2,3,4,5,6]' defined behavior? Can it be relied upon?
Are elements of Javascript arrays processed in a defined order?
Yes they are.
Is 'a == [1,2,3,4,5,6]' defined behavior? Can it be relied upon?
No, the equals operator performs referential equality when comparing object.
Short answer: "Yes".
Longer answer: Your question is actually about JavaScript statements in general and not Arrays. The code you posted ([[p(1),p(2)],p(3),[p(4),[p(5)]],p(6)]) is not an Array, it is a statement that returns an Array whilst also populating an Array. JavaScript statements are executed according to the rules of operator precedence.
Your question is not very clear, what do you mean by "processed"?
var a = [];
That is an array literal, it assigns a reference to an empty array to a.
function p(x) { a.push(x); }
Each time push() is called, a new element is added to a at index a.length (i.e. it is always added after the highest index).
[[p(1),p(2)],p(3),[p(4),[p(5)]],p(6)]
That is an array literal that is equivalent to:
a = [];
a[0] = [p(1),p(2)];
a[1] = p(3);
a[2] = [p(4),[p(5)]];
a[3] = p(6);
The following expression:
a == [1,2,3,4,5,6]
is always false, since arrays are objects and objects are never equal to any other object. You can do:
var a = [0, 1];
var b = a;
a == b; // true since a and b reference the same array
But
var a = [0, 1];
var b = [0, 1];
a == b; // false since a and b reference different arrays

Categories