`this`: methods versus functions

# Axel Rauschmayer (12 years ago)

Or are there other plans to get it solved? I would still love to see that happen, it’s a remarkably subtle source of errors. Could functions adopt the block-lambda semantics of picking up the this of the surrounding scope when not invoked as methods? It seems like that could work in strict mode where no one expects this to have a value.

No, if you call such functions via object.method() references then this binds to object. You can't break such compatibility only at runtime, and only some of the time.

Got it. I’m assuming that’s a performance issue?

In principle, one can envision: Bind this lexically by default, override with receiver when called as a function.

var obj1 = { makeFunction: function() { return function () { return this; }; } } var func = obj1. makeFunction(); console.log(func() === obj1); // true, lexical this by default

obj2 = { method: func } console.log(obj2.method() === obj2); // true, dynamic this overrides lexical this

Sorry for bringing up this issue again, I’m still a bit hazy as to what the arguments against such a solution are, especially after having seen the semantics of block lambdas.

# Brendan Eich (12 years ago)

On Nov 9, 2011, at 3:48 PM, Axel Rauschmayer wrote:

Or are there other plans to get it solved? I would still love to see that happen, it’s a remarkably subtle source of errors. Could functions adopt the block-lambda semantics of picking up the this of the surrounding scope when not invoked as methods? It seems like that could work in strict mode where no one expects this to have a value.

No, if you call such functions via object.method() references then this binds to object. You can't break such compatibility only at runtime, and only some of the time.

Got it. I’m assuming that’s a performance issue?

You could say that. If we inherit by default but it's a "soft binding", then the inner function has to carry that reference with it, but in a way that can be overridden.

We talked about lexical this for functions long ago (Jan. 2008? at Google anyway) and IIRC Mark found a subtler flaw.

# Brendan Eich (12 years ago)

On Nov 9, 2011, at 4:00 PM, Brendan Eich wrote:

On Nov 9, 2011, at 3:48 PM, Axel Rauschmayer wrote:

Or are there other plans to get it solved? I would still love to see that happen, it’s a remarkably subtle source of errors. Could functions adopt the block-lambda semantics of picking up the this of the surrounding scope when not invoked as methods? It seems like that could work in strict mode where no one expects this to have a value.

No, if you call such functions via object.method() references then this binds to object. You can't break such compatibility only at runtime, and only some of the time.

Got it. I’m assuming that’s a performance issue?

You could say that. If we inherit by default but it's a "soft binding", then the inner function has to carry that reference with it, but in a way that can be overridden.

We talked about lexical this for functions long ago (Jan. 2008? at Google anyway) and IIRC Mark found a subtler flaw.

But again, it's a runtime incompatible change, even ignoring performance. Code today may count on this == global in non-strict mode, or this === undefined in strict mode, for inner functions not called as methods.

Making such a runtime-incompatible change uses up one of my "five fingers of fate" and it's not to be done lightly.

# Mark S. Miller (12 years ago)

On Wed, Nov 9, 2011 at 4:00 PM, Brendan Eich <brendan at mozilla.com> wrote:

On Nov 9, 2011, at 3:48 PM, Axel Rauschmayer wrote:

We talked about lexical this for functions long ago (Jan. 2008? at Google anyway) and IIRC Mark found a subtler flaw.

I think my original example was smaller and more elegant. But the following is adequate to demonstrate the problem:

function Outer(secret) { "use strict";

this.v = secret;
this.w = secret * 2;
this.x = secret * 3;

this.InnerPoint = function(x, y) {
  this.x = x;
  this.y = y;
};
this.InnerPoint.prototype = {
  getX: function() { return this.x; },
  getY: function() { return this.y; }
};

}

Alice does: var outer = new Outer(mySecret); var innerPoint = new outer.InnerPoint(3,5); bob(innerPoint); // passed innerPoint to Bob, who Alice does not trust.

Today, Bob, receiving innerPoint, has no way to obtain Alice's secret. Given your proposal, Bob could do

(1,innerPoint.getX)() / 3;

Today, if Bob does that, the getX call fails when it tries to evaluate undefined.x.

# Axel Rauschmayer (12 years ago)

Got it. I’m assuming that’s a performance issue?

You could say that. If we inherit by default but it's a "soft binding", then the inner function has to carry that reference with it, but in a way that can be overridden.

We talked about lexical this for functions long ago (Jan. 2008? at Google anyway) and IIRC Mark found a subtler flaw.

But again, it's a runtime incompatible change, even ignoring performance. Code today may count on this == global in non-strict mode, or this === undefined in strict mode, for inner functions not called as methods.

Making such a runtime-incompatible change uses up one of my "five fingers of fate" and it's not to be done lightly.

Agreed. The global object assumption is too prevalent in non-strict mode, so that one is out. Strict mode might still be OK. In any case, if we have both block lambdas and shorter method syntax (*) then everyone will automatically do the right thing in practically all cases. That would be really cool.

(*) I recently heard a story of someone being surprised by seeing the word “function” in object literals – “Isn’t that supposed to be a method? Why is it called a function, then?”

# Brendan Eich (12 years ago)

On Nov 9, 2011, at 4:15 PM, Mark S. Miller wrote:

On Wed, Nov 9, 2011 at 4:00 PM, Brendan Eich <brendan at mozilla.com> wrote: On Nov 9, 2011, at 3:48 PM, Axel Rauschmayer wrote:

We talked about lexical this for functions long ago (Jan. 2008? at Google anyway) and IIRC Mark found a subtler flaw.

I think my original example was smaller and more elegant.

I remember longer, but could be misremembering. Anyway, your example here is great,

# Andreas Rossberg (12 years ago)

On 10 November 2011 01:15, Mark S. Miller <erights at google.com> wrote:

On Wed, Nov 9, 2011 at 4:00 PM, Brendan Eich <brendan at mozilla.com> wrote:

We talked about lexical this for functions long ago (Jan. 2008? at Google anyway) and IIRC Mark found a subtler flaw.

I think my original example was smaller and more elegant. But the following is adequate to demonstrate the problem:   function Outer(secret) {     "use strict";     this.v = secret;     this.w = secret * 2;     this.x = secret * 3;     this.InnerPoint = function(x, y) {       this.x = x;       this.y = y;     };     this.InnerPoint.prototype = {       getX: function() { return this.x; },       getY: function() { return this.y; }     };   } Alice does:   var outer = new Outer(mySecret);   var innerPoint = new outer.InnerPoint(3,5);   bob(innerPoint); // passed innerPoint to Bob, who Alice does not trust. Today, Bob, receiving innerPoint, has no way to obtain Alice's secret. Given your proposal, Bob could do   (1,innerPoint.getX)() / 3; Today, if Bob does that, the getX call fails when it tries to evaluate undefined.x.

I must be missing something here. Are you assuming that "new outer.InnerPoint(3,4)" would somehow receive outer' asthis' instead of a fresh object? Why would that be the case with the "soft binding" described by Axel? Or is this only a counter example for "hard" lexical binding of `this'?

# Axel Rauschmayer (12 years ago)

getX() is designed for a dynamic this (i.e. this is an instance of InnerPoint). My proposal would allow an external party to switch to lexical this (=== function Outer), simply by invoking it as a (non-method) function. I can see how this would be bad.

# Axel Rauschmayer (12 years ago)

Clever (took me a moment to figure out what the comma operator does here...). And makes sense: You would not want an invoker to have that kind of power.

If it wasn’t so negative for performance, I probably would apply curryThis() to all of my methods: var obj = { method: function (self, arg) { // additional argument self someFunction(..., function() { self.otherMethod(arg); }); }.curryThis(), // introduce an additional argument otherMethod: function (arg) { ... } } Function.prototype.curryThis = function () { var f = this; return function () { var a = Array.prototype.slice.call(arguments); a.unshift(this); return f.apply(null, a); }; };

# Andreas Rossberg (12 years ago)

On 10 November 2011 14:49, Axel Rauschmayer <axel at rauschma.de> wrote:

getX() is designed for a dynamic this (i.e. this is an instance of InnerPoint). My proposal would allow an external party to switch to lexical this (=== function Outer), simply by invoking it as a (non-method) function. I can see how this would be bad.

Ah, OK, never mind. I think I was actually misreading your proposal.

Thanks.

# Axel Rauschmayer (12 years ago)

Any suggestion for improvement is highly welcome. But it seems like we can’t do better than a construct that declares statically, how its this should be passed to it. The cool thing about block lambdas is that you do the right thing without thinking about it. That’s why, if we got arrow syntax instead, I would opt for only having the fat arrow (given that we already have a shorter syntax for methods inside object literals).

I wonder if it made a difference if thiswas always stored in an environment (instead of an execution context). Then block lambdas could find them via the scope chain.

# Andreas Rossberg (12 years ago)

On 10 November 2011 15:23, Axel Rauschmayer <axel at rauschma.de> wrote:

I wonder if it made a difference if thiswas always stored in an environment (instead of an execution context). Then block lambdas could find them via the scope chain.

If I understand you correctly, then yes, this is definitely possible in principle, and in fact corresponds to the standard model of objects as straightforward records-of-closures (closing over this'). But you could not use prototypes directly anymore, because you would need to close their methods overthis' as well when you construct an object. IOW, this would require a more class-style approach to inheritance.

Of course, you can use that technique today, by simply never using this', and instead bind your ownself' explicitly. But obviously, current engines will make object creation more costly that way.

# Axel Rauschmayer (12 years ago)

I wonder if it made a difference if thiswas always stored in an environment (instead of an execution context). Then block lambdas could find them via the scope chain.

If I understand you correctly, then yes, this is definitely possible in principle, and in fact corresponds to the standard model of objects as straightforward records-of-closures (closing over this'). But you could not use prototypes directly anymore, because you would need to close their methods overthis' as well when you construct an object. IOW, this would require a more class-style approach to inheritance.

I don’t understand. Can you give an example? I thought that simply turning this into a parameter (under the hood, like a hidden first parameter that all functions have) would not change anything:

obj.method(arg1, arg2) => obj.method<obj>(arg1, arg2)

func(arg1, arg2) => func<undefined>(arg1, arg2)

(The hidden parameter is in angle brackets.)

# Andreas Rossberg (12 years ago)

On 10 November 2011 15:58, Axel Rauschmayer <axel at rauschma.de> wrote:

I wonder if it made a difference if thiswas always stored in an

environment (instead of an execution context). Then block lambdas could find

them via the scope chain.

If I understand you correctly, then yes, this is definitely possible in principle, and in fact corresponds to the standard model of objects as straightforward records-of-closures (closing over this'). But you could not use prototypes directly anymore, because you would need to close their methods overthis' as well when you construct an object. IOW, this would require a more class-style approach to inheritance.

I don’t understand. Can you give an example? I thought that simply turning this into a parameter (under the hood, like a hidden first parameter that all functions have) would not change anything:

No, that's how it works right now. The alternative is to lexically close all methods over self at construction time:

function Point(x, y) { var self = this self.x = x self.y = y self.move = function(dx, dy) { self.x += dx; self.dy += dy } }

function ColorPoint(x, y, color) { var self = this Point.call(self, x, y) self.color = color self.recolor = function(c) { self.color = c } }

As said, this doesn't play well with prototype inheritance. You have to put all methods that refer to self on the object itself. But "inner constructors" are straighforward and safe.