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