定义后设置函数名称

时间:2016-07-15 15:34:53

标签: javascript function

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

var/let/const foo = function() {}
function bar() {}

foo的函数没有自己的名字,但是bar呢。

console.log(foo.name) --> ''
console.log(bar.name) --> 'bar'

定义函数后是否可以定义函数的名称?
所以做某事会使console.log(foo.name)返回除''

之外的其他内容

1 个答案:

答案 0 :(得分:5)

  

众所周知......

var/let/const foo = function() {}
     

...

     

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

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

但正如squint a comment中指出的那样,在ES2015 +中,可以通过Object.defineProperty设置函数的名称:

Object.defineProperty(foo, "name", {value: "aNameForFoo"});

您不能只分配给foo.name,因为name被定义为不可写。但它是可配置的(你可以用新的属性换掉属性),所以上面的工作原理。 (详细说明如何在the spec中定义。)

从ES2015开始,请注意Function#name ,因为它是一个非常小的功能,它是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"

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);
}

在正确实现ES2015 Function#name的浏览器上(撰写本文时极少数,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"

可以合理地认为该函数最终会成为Nifty.stuff.here.foofoo。这是后者。 (这个ES2015名称“推理”来自Google和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);
}

在支持这些内容的浏览器上输出:

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

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