Why can't I assign a new value to "this" in a prototype function?
Why can I do this:
Array.prototype.foo = function() {
this.splice(0, this.length);
return this.concat([1,2,3]);
}
But I can't do this:
Array.prototype.foo = function() {
return this = [1,2,3];
}
Both functions destroy the value of this and change it to [1,2,3]
but the second one throws the following error: Uncaught ReferenceError: Invalid left-hand side in assignment
I suspect it's because allowing assignment means I could potentially change the array to something else (like a string), but I'm hoping someone out there knows for sure and/or has a more detailed explanation.
Solution 1:
You're confusing objects with references.
An array is an object, when you use a literal like [1,2,3]
you're making a new array.
A variable name like this
or a
is a reference to an object. If it helps, imagine an object as a person, and the reference as their nickname. You can have more than one reference to the same object, for example:
var a = [1,2];
var b = a;
b.push(3);
alert(a.length); // Gives "3"
Imagine if you had a friend named Sarah. You also sometimes call her "Ace". If Sarah gets a haircut one day, Ace has a haircut too, because "Sarah" and "Ace" are both different names for the same person.
If you use a mutating array method like a.push
or a.splice
(concat
however is not one!), you change the existing Array object, and a
still refers to the same object. This is like getting a hair cut - you may look different, but you're still the same person.
When you assign a reference to a new value, with a = [1,2,3]
, you're creating a new array, and changing a
to refer to it. This is like finding a new friend with different hair, and deciding to call her Ace instead.
Now this
is a special name generated by Javascript. It's not a given name like Sarah, but more of a title, like "mother". Your mother can get a new haircut, but you can't get a new mother. Likewise, you can't change what this
refers to from inside a function.
Solution 2:
It's not permitted to assign a value to this
within a function. Suppose that you could do this, and your code looked something like:
Array.prototype.foo = function() {
return this = [1, 2, 3];
}
var a = ["beans", "rice"];
a.foo();
// a now points to an object containing [1, 2, 3]
Now, what if you did this:
var a = ["beans", "rice"];
var b = a; // b refers to the same object as a
b.foo();
// what does b refer to now? how about a?
The act of calling a function .foo()
on an object should not change the identity of the object. This would be very confusing for the caller if b
suddenly started referring to a different object than a
simply because some method was called.
Solution 3:
You are not allowed to re-assign this
. Because the this value associated with an execution context is immutable.
Solution 4:
Is it possible to change the value of this
? Not by assignment, you cannot assign a value to this
Your case:
Array.prototype.foo = function() { this.splice(0, this.length); return this.concat([1,2,3]); }
But I can't do this:
Array.prototype.foo = function() { return this = [1,2,3]; }
When you modify the prototype adding method foo()
, the first part makes the array become empty, equals to reassign its instance.
For example to var a = []
Then in the return value:
return this.concat([1,2,3]);
Remember that concat
does not destroys an array, it creates a new array and returns it
Given a = ['x']
and b = ['y']
, c = a.concat(b)
results in c = ['x','y']
, and a
and b
remain the same as they were
Now since you are doing this inside the array
a=['a','b','c']
then you call a.foo();
, inside a
will get equal a=[]
, with this.splice(0, this.length);
then will concatenate [ ]
with [1,2,3]
So if I say just a.foo()
, in fact nothing happens outside if I do not assign the result to something, just a remains empty.
If a
is assigned to something
c = a.foo()
then c
will be [1,2,3]
, and a
still empty [ ]
In the second part, this = [1,2,3]
It is possible to change the value of this?, not by assignment, you cannot assign a value to this