函数名

有一些情况,你的代码想要检视自己并询问某个函数的名称是什么。如果你询问一个函数的名称,答案会有些令人诧异地模糊。考虑如下代码:

  1. function daz() {
  2. // ..
  3. }
  4. var obj = {
  5. foo: function() {
  6. // ..
  7. },
  8. bar: function baz() {
  9. // ..
  10. },
  11. bam: daz,
  12. zim() {
  13. // ..
  14. }
  15. };

在这前一个代码段中,“obj.foo()的名字是什么?”有些微妙。是"foo""",还是undefined?那么obj.bar()呢 —— 是"bar"还是"baz"obj.bam()称为"bam"还是"daz"obj.zim()呢?

另外,作为回调被传递的函数呢?就像:

  1. function foo(cb) {
  2. // 这里的 `cb()` 的名字是什么?
  3. }
  4. foo( function(){
  5. // 我是匿名的!
  6. } );

在程序中函数可以被好几种方法所表达,而函数的“名字”应当是什么并不总是那么清晰和明确。

更重要的是,我们需要区别函数的“名字”是指它的name属性 —— 是的,函数有一个叫做name的属性 —— 还是指它词法绑定的名称,比如在function bar() { .. }中的bar

词法绑定名称是你将在递归之类的东西中所使用的:

  1. function foo(i) {
  2. if (i < 10) return foo( i * 2 );
  3. return i;
  4. }

name属性是你为了元编程而使用的,所以它才是我们在这里的讨论中所关注的。

产生这种用困惑是因为,在默认情况下一个函数的词法名称(如果有的话)也会被设置为它的name属性。实际上,ES5(和以前的)语言规范中并没有官方要求这种行为。name属性的设置是一种非标准,但依然相当可靠的行为。在ES6中,它已经被标准化。

提示: 如果一个函数的name被赋值,它通常是在开发者工具的栈轨迹中使用的名称。

推断

但如果函数没有词法名称,name属性会怎么样呢?

现在在ES6中,有一个推断规则可以判定一个合理的name属性值来赋予一个函数,即使它没有词法名称可用。

考虑如下代码:

  1. var abc = function() {
  2. // ..
  3. };
  4. abc.name; // "abc"

如果我们给了这个函数一个词法名称,比如abc = function def() { .. },那么name属性将理所当然地是"def"。但是由于缺少词法名称,直观上名称"abc"看起来很合适。

这里是在ES6中将会(或不会)进行名称推断的其他形式:

  1. (function(){ .. }); // name:
  2. (function*(){ .. }); // name:
  3. window.foo = function(){ .. }; // name:
  4. class Awesome {
  5. constructor() { .. } // name: Awesome
  6. funny() { .. } // name: funny
  7. }
  8. var c = class Awesome { .. }; // name: Awesome
  9. var o = {
  10. foo() { .. }, // name: foo
  11. *bar() { .. }, // name: bar
  12. baz: () => { .. }, // name: baz
  13. bam: function(){ .. }, // name: bam
  14. get qux() { .. }, // name: get qux
  15. set fuz() { .. }, // name: set fuz
  16. ["b" + "iz"]:
  17. function(){ .. }, // name: biz
  18. [Symbol( "buz" )]:
  19. function(){ .. } // name: [buz]
  20. };
  21. var x = o.foo.bind( o ); // name: bound foo
  22. (function(){ .. }).bind( o ); // name: bound
  23. export default function() { .. } // name: default
  24. var y = new Function(); // name: anonymous
  25. var GeneratorFunction =
  26. function*(){}.__proto__.constructor;
  27. var z = new GeneratorFunction(); // name: anonymous

name属性默认是不可写的,但它是可配置的,这意味着如果有需要,你可以使用Object.defineProperty(..)来手动改变它。