定义后设置函数名称

Che*_*erd 6 javascript function

众所周知,我们可以使用和不使用名称来定义函数:

var/let/const foo = function() {}
function bar() {}
Run Code Online (Sandbox Code Playgroud)

哪个foo功能没有它自己的名字,但bar确实如此.

console.log(foo.name) --> ''
console.log(bar.name) --> 'bar'
Run Code Online (Sandbox Code Playgroud)

是否可以在定义函数后定义函数的名称?
所以做一些事情会让console.log(foo.name)别的东西回归''

T.J*_*der 5

众所周知......

var/let/const foo = function() {}
Run Code Online (Sandbox Code Playgroud)

...

foo的功能还没有自己的名字......

从ES2015 +开始,foo的功能确实有一个名字:foo.ES2015添加了许多函数获取名称的地方,即使使用"匿名"函数表达式定义也是如此.这包括简单的分配,属性分配(包括计算属性名称)等.搜索规范"SetFunctionName"以获取详细信息.

但正如斜视评论中指出的那样,在ES2015 +中,函数的名称可以通过Object.defineProperty以下方式设置:

Object.defineProperty(foo, "name", {value: "aNameForFoo"});
Run Code Online (Sandbox Code Playgroud)

您不能只分配到foo.name因为name被定义为不可写.但它是可配置的(你可以换一个新的属性),所以上面的工作.(详细说明如何在规范中定义.)

请注意,这Function#name是ES2015的功能,因为它是一个非常小的功能,它是JavaScript引擎开发人员的最低优先级之一.我相信Chrome 51是第一款具有正确实现规范的JavaScript引擎的浏览器.这个阶段会很快改变.

但正如Assimilater所指出的那样,如果你只是希望表达式创建的函数有一个名称而你不需要它在运行时定义,那么只需使用一个命名函数表达式(NFE):

var foo = function bar() {
    // Note ------^^^^
}
//console.log(bar);    // Would throw a ReferenceError, `bar` is not defined in this scope
console.log(foo.name); // "bar"
Run Code Online (Sandbox Code Playgroud)

NFE早在ES2015之前就已存在.他们曾经在某些引擎(早期的Safari,IE8和更早版本)中存在问题,但现代引擎(IE9 +)正确处理它们.

这是一个例子:

  • "推断"的名称
  • 更新的名称
  • 由NFE指定的名称

// "Inferred" name is foo
var foo = function() {
  throw new Error();
};
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

// Change it to "updatedName"
Object.defineProperty(foo, "name", {
  value: "updatedName"
});
try {
  foo();
} catch (e) {
  console.log(e.stack);
}

// Example of NFE
var foo2 = function bar() {
  // Note --------^^^^
  console.log("bar is defined within the function: " + bar.name);
  throw new Error();
};
//console.log(bar);     // Would throw an error, `bar is not
// defined here
console.log(foo2.name); // "bar"
try {
  foo2();
} catch (e) {
  console.log(e.stack);
}
Run Code Online (Sandbox Code Playgroud)

Function#name正确实现ES2015的浏览器上(在撰写本文时很少做,Chrome 51是第一个)并实现Error#stack,输出:

Error
    at foo (http://stacksnippets.net/js:15:9)
    at http://stacksnippets.net/js:18:3
Error
    at updatedName (http://stacksnippets.net/js:15:9)
    at http://stacksnippets.net/js:28:3
bar
bar is defined within the function: bar
Error
    at bar (http://stacksnippets.net/js:37:9)
    at http://stacksnippets.net/js:43:3

关于匿名表达式定义的函数的"推断"名称的注释.考虑:

let Nifty = {
  stuff: {
    here: {
      foo: function() { throw new Error(); }
    }
  }
};
let f = Nifty.stuff.here.foo;
console.log(f.name); // "foo"
Run Code Online (Sandbox Code Playgroud)

人们可以合理地认为这个功能最终会成为Nifty.stuff.here.foo,或者foo.这是后者.(这ES2015名"推断"出来的,在谷歌和Mozilla的努力,在他们的开发工具的功能提供有用的名称在一个时期,一个或另一个-我忘了-使用全名,但它似乎没有一直很受欢迎.)


或许值得注意:从ES2015开始,您可以通过计算属性名称创建时选择运行名称:

var name = "function" + Math.floor(Math.random() * 10000);
var obj = {[name]: function() { throw new Error();  }};
var foo = obj[name];
try {
  foo();
} catch (e) {
  console.log(e.stack);
}
Run Code Online (Sandbox Code Playgroud)

在支持这些东西的浏览器上输出:

Error
    at function3608 (http://stacksnippets.net/js:14:39)
    at http://stacksnippets.net/js:17:3

但是现在我知道你可以做到这一点Object.defineProperty,好吧,那似乎不那么聪明了.:-)