函数名
有一些情况,你的代码想要检视自己并询问某个函数的名称是什么。如果你询问一个函数的名称,答案会有些令人诧异地模糊。考虑如下代码:
function daz() {
// ..
}
var obj = {
foo: function() {
// ..
},
bar: function baz() {
// ..
},
bam: daz,
zim() {
// ..
}
};
在这前一个代码段中,“obj.foo()
的名字是什么?”有些微妙。是"foo"
,""
,还是undefined
?那么obj.bar()
呢 —— 是"bar"
还是"baz"
?obj.bam()
称为"bam"
还是"daz"
?obj.zim()
呢?
另外,作为回调被传递的函数呢?就像:
function foo(cb) {
// 这里的 `cb()` 的名字是什么?
}
foo( function(){
// 我是匿名的!
} );
在程序中函数可以被好几种方法所表达,而函数的“名字”应当是什么并不总是那么清晰和明确。
更重要的是,我们需要区别函数的“名字”是指它的name
属性 —— 是的,函数有一个叫做name
的属性 —— 还是指它词法绑定的名称,比如在function bar() { .. }
中的bar
。
词法绑定名称是你将在递归之类的东西中所使用的:
function foo(i) {
if (i < 10) return foo( i * 2 );
return i;
}
name
属性是你为了元编程而使用的,所以它才是我们在这里的讨论中所关注的。
产生这种用困惑是因为,在默认情况下一个函数的词法名称(如果有的话)也会被设置为它的name
属性。实际上,ES5(和以前的)语言规范中并没有官方要求这种行为。name
属性的设置是一种非标准,但依然相当可靠的行为。在ES6中,它已经被标准化。
提示: 如果一个函数的name
被赋值,它通常是在开发者工具的栈轨迹中使用的名称。
推断
但如果函数没有词法名称,name
属性会怎么样呢?
现在在ES6中,有一个推断规则可以判定一个合理的name
属性值来赋予一个函数,即使它没有词法名称可用。
考虑如下代码:
var abc = function() {
// ..
};
abc.name; // "abc"
如果我们给了这个函数一个词法名称,比如abc = function def() { .. }
,那么name
属性将理所当然地是"def"
。但是由于缺少词法名称,直观上名称"abc"
看起来很合适。
这里是在ES6中将会(或不会)进行名称推断的其他形式:
(function(){ .. }); // name:
(function*(){ .. }); // name:
window.foo = function(){ .. }; // name:
class Awesome {
constructor() { .. } // name: Awesome
funny() { .. } // name: funny
}
var c = class Awesome { .. }; // name: Awesome
var o = {
foo() { .. }, // name: foo
*bar() { .. }, // name: bar
baz: () => { .. }, // name: baz
bam: function(){ .. }, // name: bam
get qux() { .. }, // name: get qux
set fuz() { .. }, // name: set fuz
["b" + "iz"]:
function(){ .. }, // name: biz
[Symbol( "buz" )]:
function(){ .. } // name: [buz]
};
var x = o.foo.bind( o ); // name: bound foo
(function(){ .. }).bind( o ); // name: bound
export default function() { .. } // name: default
var y = new Function(); // name: anonymous
var GeneratorFunction =
function*(){}.__proto__.constructor;
var z = new GeneratorFunction(); // name: anonymous
name
属性默认是不可写的,但它是可配置的,这意味着如果有需要,你可以使用Object.defineProperty(..)
来手动改变它。