4

I found this answer: How to modify "this" in javascript

But I did not like it. Surely there must be a way to somehow modify this?

Here is an example:

(function()
{

    var a = function(v)
    {
        v += 10;

        a = function()
        {
            console.log(this);
            this += 5; // How to "unlock" 'this'?
            console.log(this);
        }.bind(v)

        //a();
    }

    a(0);
    a();

})();

Fiddle: https://jsfiddle.net/ahvonenj/mz19aku6/

If you comment out the this += 5 part, the console says that this is Number {[[PrimitiveValue]]: 10}. So I do not see a reason why a Number (PrimitiveValue) type could not be increased by 5 and printed out. What am I missing here?

Is there a way to "break out" of the engine thinking this as this and somehow forcefully convert this to what it says it is - Number in this case?

Maybe by somehow making a function that accepts a to-be-converted this as an argument and returns a new function which has the supplied this rebound with bind?

Note: I am not looking for doing anything practical with a possible solution. I am just curious from a technical standpoint or something.

Edit: So a user named Oriol told me that this is not actually a Number object with a value of 10, but Instead a Number literal. This apparently means that this += 5 is almost the same as writing 10 += 5 in this case. I tried checking out out like so:

(function()
{

    var a = function(v)
    {
        v += 10;

        a = function()
        {
            console.log(this);
            var p = this + 5; // How to "unlock" 'this'?
            console.log(p);
        }.bind(v)

        //a();
    }

    a(0);
    a();

})();

So var p = this + 5 is now basically the same as writing var p = 10 + 5, which is what we expected.

Community
  • 1
  • 1
Piwwoli
  • 1,570
  • 1
  • 13
  • 32
  • 1
    nope, you can't. Can you explain, why you want this? – Grundy Oct 15 '15 at 16:06
  • The `this` binding can't be altered. Use `var foo = this` and then modify `foo`, or create an inner closure with the customized `this`. – Oriol Oct 15 '15 at 16:07
  • @Oriol How is the engine protecting it, if it says `this` is a `Number`? – Piwwoli Oct 15 '15 at 16:09
  • The issue is not as much with `this` but the fact that `Number` is immutable. So even if you could somehow get `this` to point to the new variable, it still wouldn't work. – JJJ Oct 15 '15 at 16:09
  • @Juhana Interesting... So could I bind a function with some mutable type then? – Piwwoli Oct 15 '15 at 16:10
  • @Grundy Just looking for interesting ways to do stuff in JavaScript and to see how far the language / engine flexes. :) – Piwwoli Oct 15 '15 at 16:12
  • @Piwwoli Sure, if `this` mutable, you can change its properties (e.g., this happens all the time, whenever `this` is `window`), but you can never change `this` to a different value within a function. – apsillers Oct 15 '15 at 16:12
  • 2
    I _think_ it's covered by [this part](http://www.ecma-international.org/ecma-262/5.1/#sec-10.3) of the spec, namely that ThisBinding is a State Component of an Execution Context, and it goes on to say _"It is impossible for an ECMAScript program to access an execution context."_ and therefore, presumably, the State Components - ie they can't be modified from code. – James Thorpe Oct 15 '15 at 16:12
  • 4
    You just confusing with `this` - it not a variable, it a keyword, refers to context. Possibly if you see spec it would be more clear: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-execution-contexts – Grundy Oct 15 '15 at 16:13
  • Note the `this` value is not a number object with [[PrimitiveValue]] set to 10. It's a number literal instead. (In strict mode) – Oriol Oct 15 '15 at 16:15
  • @JamesThorpe and Grundy: Thanks for the spec links, those should explain this further. – Piwwoli Oct 15 '15 at 16:18
  • `this` is not a variable, you cannot modify it. The `Number` thing is because variable `v` was bound to the function, and context should be an object so implicit auto-boxing happened. You could find more in ECMAScript language spec. – Leo Oct 15 '15 at 16:18
  • @Oriol So if `this` is a number literal (in this case), does it basically mean that `this += 5` is the same as writing `10 += 5` out of the blue? – Piwwoli Oct 15 '15 at 16:19
  • 1
    @Piwwoli Not exactly the same, but somewhat. – Oriol Oct 15 '15 at 16:22
  • @Oriol I tested it out a bit and edited the question accordingly. :) It does seem like that writing `var p = this + 5` at least acts like writing `var p = 10 + 5`, so the Number literal part should be correct at least. – Piwwoli Oct 15 '15 at 16:28
  • @Claies, in js, `this` - not always _object's reference to itself_ :-) – Grundy Oct 15 '15 at 16:52

4 Answers4

4

You can think of this as an implicit, immutable, value accompanying a function call.

There are a number of ways it can be set, the most common being that a call of the form o.f() sets this to o within f. Of course, this can also be set with call and apply and bind, and in other ways as well.

this is not like other function parameters. If were a normal parameter, its value could in theory be changed. Instead, it represents a value, a sort of lexical placeholder for the value of this. In other words, if I call

f.call(5)

and then write

function f() { console.log(this); }

You can think of the this as being lexically replaced by the value 5. So if I tried to write

function() { this += 5; }

you can think of this as being the equivalent of

function() { 5 += 5; }

which is obvious nonsense. I cannot replace the value 5 with something else. By the same token, if I said

o = {
  f: function() { this = o2; }
};

and then call o.f(), the function is essentially rewritten as

function() { { f: ... } = o2; }

which is equal nonsense. I cannot re-assign the value of an object.

Since this is a value, not a parameter, it cannot be assigned to or changed. It is not a valid left-hand-side for an assignment, any more than 42 is. We cannot change the value of the value named 42. We cannot change the value of the value named this.

The reason for this is that is the specification. In theory, one can imagine a version of JavaScript where this was mutable. There is no definitive reason why this should not be possible. After all, no matter where it comes from, within the function it is simply a value. It would be confusing, and could result in unexpected behavior, but there is no reason why it could not work.

The primary reason this is not supported is simply that that is not how the language is defined. If your question is "why is the language defined that way", then that is something for language historians to answer. However, one can imagine good reasons for this, involving intuitive code behavior.

Consider the following code:

o = {
  f: function() { 
    this.doSomething();
    this = o2;
    this.doSomething();
};

It is going to be extremely hard for normal human beings to understand such code. The same two this.doSomething() calls do two different things due to the intervening change to this. This alone is a more than adequate reason to prohibit changing the value of this. All the more so since there is no logical reason for needing to do this, since I can replace the last two lines with a simple o2.doSomething().

Just to be clear, of course when we talk about "changing this", even if we could do that, which we can't, we couldn't possibly mean somehow changing the outside value this refers to. We all know that in the following case:

function a(x) { x = 2; }
var y = 1;
a(y);

The change to x within a() is purely local, and can't possibly affect y. The same thing would apply equally to this of course.

One more note on objects wrapping primitives. We know we can write

o5 = Object(5);

to get an object wrapping the number 5. Now I can define a method on this object, as in

o5.f = function() { console.log(this); };

and calling o5.f() will log

Number {[[PrimitiveValue]]: 5}

So since this object "is" a number, certainly in this case I should be able to say

o5.f = function() { this++; };

But no. this is not the number 5; it's an object wrapping 5. Those are two very different things. The function above is essentially equivalent to saying

o5.f = function() { Object(5)++; }

which yields

Uncaught ReferenceError: Invalid left-hand side expression in postfix operation

Now I can do arithmetic on the object, such as Object(5) + 1, because JS will coerce the object to its underlying primitive before adding one. But I cannot re-assign to this object any more than any other. And I cannot modify the primitive number underlying the object. Once the wrapper object is created, the primitive value underlying it is locked in forever.

So the answer to your question is "no, there is no way to change this". Which is not to say there are not other ways to do other things to accomplish whatever it is you want to accomplish.

2

Actually the problem here is that when you binding the function to a number then the this is getting changed to number and now when you are doing the assignment operation this += 5; the left hand side is not a variable where we can store the new value as its a primitive number instance. so to overcome that what you can do is you can store the this value in another variable say me and then do the operation as following:

(function() {

    var a = function(v) {
        v += 10;

        a = function() {
            var me = this;
            console.log(this); //logs Number {[[PrimitiveValue]]: 10}
            me += 5; 
            console.log(me); //logs 15
        }.bind(v)

        //a();
    }

    a(0);
    a();

})();
Raju Bera
  • 902
  • 7
  • 14
2

I don't know any language that allows you to alter this.

you might want to think of this as an address:
my house "sits" on Sesame street 10. of course, I can invite designers who can change who my Sesame-street-10-house looks like, but can I suddenly make Sesame street 10 turn into Sesame street 15? of course not. I can't just rip an entire piece of land and pass it 5 houses ahead.

going back to programing context, this (eventually) is interpreted to the memory block your object sits in. you can change that object which sits on the specified memory block, but you can't just change the block address.

David Haim
  • 23,138
  • 3
  • 38
  • 70
1

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures

Simple types (except objects and arrays) is immutable. You can't modify it and save in source variable.

function foo() {
    this.val+=10;
}

a=function(obj) {
    a=foo.bind(obj);
}

But you can wrap it into object. See: http://jsfiddle.net/5dhm8fsn/

Tarwirdur Turon
  • 673
  • 5
  • 15