1

Based on my experience and the information in Why use named function expressions? and kangax.github.io: Named function expressions, it is clear why the two examples below result in the given output:

Example 1:

var f = function foo() {
  console.log(foo);
}

console.log( typeof foo === "undefined" ); // true
console.log( typeof f !== "undefined" );   // true
console.log( f.name );                     // foo

Example 2:

var obj = {
  fooA: function foo() {
    console.log('fooA -> ' + foo.toString());
  },

  fooB: function foo() {
    console.log('fooB -> ' + foo.toString());
  }
}

obj.fooA();  // the source of fooA
obj.fooB();  // the source of fooB
console.log(typeof foo === "undefined"); // true

But I'm try to figure out why the identifier is not available to the enclosing scope in the following example:

var obj = {
  foo() {
    console.log(typeof foo === "undefined"); // true
  }
}

obj.foo();
console.log(obj.foo.name); // foo

I guess it is defined somewhere, but information about this seems to be spread over the whole specs. The only clear statement I found was on MDN: Method definitions:

Note : The shorthand syntax uses named function instead of anonymous functions (as in …foo: function() {}…). Named functions can be called from the function body (this is impossible for anonymous function as there is no identifier to refer to). For more details, see function.

But in my option this contradicts the observation, because it says this kind of function is a named function, and therefore it should be accessible from the function body.

The function has a name, because obj.foo.name is foo, but foo is not accessible from inside of itself.

Is the observed behaviour correct with regard to the specs, and where is this defined in the specs?

Community
  • 1
  • 1
t.niese
  • 39,256
  • 9
  • 74
  • 101

2 Answers2

3

The function has a name, because obj.foo.name is foo, but foo is not accessible from inside of itself.

Yes. This is a new type of function introduced with ES6: the named anonymous function1. Uh.

The name property of an ES6 function is derived no more from the token between the function keyword and the opening parenthesis, but also from the variable or property name it got assigned to. The concise method definition

var obj = {
    foo() { … }
}

is semantically equivalent to

var obj = {
    foo: function() { … }
}

in ES6 - both create a function with a .name of "foo" (cp. §14.3.9 and §12.2.6.9).

This function is however not a named function expression that creates a foo constant in an intermediate closure scope that allows it to refer to itself.

If you want a self-reference in a function definition, you'd use this.foo.

Is the observed behaviour correct with regard to the specs

Yes. MDN seems to be faulty here again.

… and where is this defined in the specs?

Basically in the whole Chapter 14: Functions and Classes.

In particular, the named function expression behaviour is specified in §14.1.20, with emphasis on the funcEnv that can't be found in the evaluation of anonymous function expressions.

The .name property is created in the abstract SetFunctionName method that is called from all over the spec (do a search for it). Particularly interesting cases go together with an IsAnonymousFunction­Definition call that checks whether the expression does not contain a naming token.

1: I just made the term up. In case you didn't notice :-)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for your feedback. I'll take a close look at Chapter 14. Its less about that I want to do self referencing myself, but more about that I write a module for esformatter for code refactoring, so I want to be sure about that. – t.niese May 26 '16 at 13:38
  • 1
    I need to get used that `foo: function() { … }` will result in `foo.name` to be `foo`, but seems to be a logical step to keep all representations consistent. – t.niese May 26 '16 at 13:53
1

Short answer:

You need to use this.foo

({
   foo(x) {
     return x > 3 ? x : this.foo(x+1);
   }
 }).foo(1); // => 4

It's no different than if you had this:

({
   foo: function(x) {
     return x > 3 ? x : this.foo(x+1);
   }
}).foo(1); // => 4

Named functions can be called from the function body

Right, so if you named it, you could use it without this:

({
   foo: function foo(x) {
     return x > 3 ? x : foo(x+1);
   }
}).foo(1); // => 4

Is the observed behaviour correct with regard to the specs, and where is this defined in the specs?

This part I can't answer for you, sorry.


babel.js

If you transpile this using babel

let x = {foo() {}};

You will get

var x = {foo: function foo() {}};

So it looks like if you tranpsile your code, it would work properly because Babel will give names to the functions.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • Thank you for your feed back, so `{ foo() { } }` is not a named function like stated on MDN? I work an an esformatter extension for code refactoring so I really would like to know how it is defined in ES6. – t.niese May 26 '16 at 13:26
  • @t.niese I just did a quick test in babel and it does transpile `{foo(){}}` with a function name. Still not sure what the spec is tho, sorry :\ – Mulan May 26 '16 at 13:29
  • I also did the test on babel before posting :). If you write `let x = {foo() { console.log(typeof foo) }};` the output is a bit more complicate. – t.niese May 26 '16 at 13:32