Inner functions and the value of this.

# Jake Verbaten (14 years ago)

An inner function is a function declaration or expression inside a function.

Currently the default value of this is either the global object or undefined for strict mode. (When invoked normally).

Would it be possible to have the value of this default to the value of this in the outer function, i.e. the function it was declared in.

For example:

"method": function _method() { var cb = after(n, function() { // this is the value of this inside _method }); }

This would basically solve the var that = this and .bind(this) patterns used on anonymous functions.

I believe coffeescript has sugar for functions like this with =>.

Justification: this === undefined is not very helpful.

# Xavier MONTILLET (14 years ago)

Well the spec tends to move the other way by removing arguments.callee, caller and so on for security reasons... Plus in your example is given as a callback...

If you do this :

var o1 = { m1: function ( ) { // here, this is o1 o2.m2( function ( ) { // this function is declared in a function where this is o1 and invoked in a function where this is o2... } ); } }; var o2 = { m2: function ( f ) { // here, this is o2 f( ); } }; o1.m1();

I'm quite sure some people would expect one behavior and others would expect the other one... but it is sure that would be a source of hard-to-track bugs.

# Jake Verbaten (14 years ago)

Im suggesting the rule is value of this binds at location of function instantation.

I would expect an invocation of the function elsewhere to still have the this value of the other function (o1 not o2).

As long as people understand and follow the rule set it shouldnt be too confusing. Basically a function declaration/expression is automatically soft bound to the outer this.

On Oct 15, 2011 2:52 PM, "Xavier MONTILLET" <xavierm02.net at gmail.com> wrote:

Well the spec tends to move the other way by removing arguments.callee, caller and so on for security reasons... Plus in your example is given as a callback...

If you do this :

var o1 = { m1: function ( ) { // here, this is o1 o2.m2( function ( ) { // this function is declared in a function where this is o1 and invoked in a function where this is o2... } ); } }; var o2 = { m2: function ( f ) { // here, this is o2 f( ); } }; o1.m1();

I'm quite sure some people would expect one behavior and others would expect the other one... but it is sure that would be a source of hard-to-track bugs.

On Sat, Oct 15, 2011 at 2:54 PM, Jake Verbaten <raynos2 at gmail.com> wrote: >

An inner function is a ...

# Quildreen Motta (14 years ago)

On 15/10/11 11:47, Jake Verbaten wrote:

Im suggesting the rule is value of this binds at location of function instantation.

I would expect an invocation of the function elsewhere to still have the this value of the other function (o1 not o2).

As long as people understand and follow the rule set it shouldnt be too confusing. Basically a function declaration/expression is automatically soft bound to the outer this.

I think soft-bound functions would make more sense than the current `thisObject' rules, but it's perhaps too late to change the semantics now. ES.next is an evolutionary step, not a revolutionary one.

I get mixed feelings about this. On one hand, I'd really love to see the semantic quirks solved, on the other, I'm not sure legacy code should break with the new version — perhaps it should, but Brendan mentioned that browsers vendors won't be maintaining two different VMs, so there's that.

# Axel Rauschmayer (14 years ago)

With nested functions, one is confronted with "this" actually being two different things:

  1. Dynamic "this": An implicit parameter that every function gets. Note: this is not entirely accurate, but I find it the easiest way of making sense of what is going on.
  2. Static "this": A variable in an enclosing scope.

Given two nested functions:

function A() {
    function B() {
        this.foo();
    }
}

As you cannot predict at compile time whether B will be a method or not, you would have to determine dynamically whether "this" should look at the enclosing scopes or at the parameters. That is bound to cause performance problems.

Furthermore, "this" isn’t even a true parameter. It thus won’t be stored in an environment and can’t be found via the normal scope-related mechanisms. Note that even if "this" was a real parameter, you would need a way of indicating when you want to have it and when not (as in “will this function ever become a method?”), to avoid an outer "this" from being shadowed.

Solutions:

  • Python puts the receiver (the object) of a method call into the first parameter when a function is used as a method. If you call a function as a function, that parameter becomes explicit, obviating the need for Function.prototype.call().

  • Block lambdas [1] are a neat way out of the dilemma: they are never going to be methods and can thus always have static "this". If you want methods and dynamic "this", you use “real” functions. Maybe it even makes sense to forbid block lambdas being invoked as methods. This is a good solution for JS newbies, because they will normally automatically do the right thing.

  • I find CoffeeScript’s two arrows not helpful, because you still have to highly aware of what is going on.

"this" being undefined instead of the global object in non-method functions has another reason: If you implement a constructor function and forget to use "new" when you invoke it then you won’t accidentally (and silently) create global variables:

function Point(x, y) { this.x = x; this.y = y; } var pt = Point(3, 5); // problem: creates global vars in non-strict mode

[1] strawman:block_lambda_revival