ES6 的函数 name 属性

定义函数有各种各样的方式,在 JS 中识别函数就变得很有挑战性。此外,匿名函数表达式的流行使得调试有点困难,经常导致堆栈跟踪难以被阅读与解释。正因为此,ES6 给所有函数添加了 name 属性。

选择合适的名称

ES6 中所有函数都有适当的 name 属性值。为了理解其实际运作,请看下例——它展示了一个函数与一个函数表达式,并将二者的 name 属性都打印出来:

function doSomething() {
    // ...
}

var doAnotherThing = function() {
    // ...
};

console.log(doSomething.name);          // "doSomething"
console.log(doAnotherThing.name);       // "doAnotherThing"

在此代码中,由于是一个函数声明,doSomething() 就拥有一个值为 "doSomething"name 属性。而匿名函数表达式 doAnotherThing()name 属性值则是 "doAnotherThing",因为这是该函数所赋值的变量的名称。

匿名函数的名称属性在 FireFoxEdge 中仍然不被支持(值为空字符串),而 Chrome 直到 51.0 版本才提供了该特性。

名称属性的特殊情况

虽然函数声明与函数表达式的名称易于查找,但 ES6 更进一步确保了所有函数都拥有合适的名称。为了表明这点,请参考如下程序:

var doSomething = function doSomethingElse() {
    // ...
};

var person = {
    get firstName() {
        return "Nicholas"
    },
    sayName: function() {
        console.log(this.name);
    }
}

console.log(doSomething.name);      // "doSomethingElse"
console.log(person.sayName.name);   // "sayName"

var descriptor = Object.getOwnPropertyDescriptor(person, "firstName");
console.log(descriptor.get.name); // "get firstName"

本例中的 doSomething.name 的值是 "doSomethingElse",因为该函数表达式自己拥有一个名称,并且此名称的优先级要高于赋值目标的变量名。person.sayName()name 属性值是 "sayName",正如对象字面量指定的那样。类似的,person.firstName 实际是个 getter 函数,因此它的名称是 "get firstName",以标明它的特征;同样,setter 函数也会带有 "set" 的前缀(gettersetter 函数都必须用 Object.getOwnPropertyDescriptor() 来检索)。

函数名称还有另外两个特殊情况。使用 bind() 创建的函数会在名称属性值之前带有 "bound" 前缀;而使用 Function 构造器创建的函数,其名称属性则会有 "anonymous" 前缀,正如此例:

var doSomething = function() {
    // ...
};

console.log(doSomething.bind().name);   // "bound doSomething"

console.log((new Function()).name);     // "anonymous"

绑定产生的函数拥有原函数的名称,并总会附带 "bound" 前缀,因此 doSomething() 函数的绑定版本就具有 "bound doSomething" 名称。

需要注意的是,函数的 name 属性值未必会关联到同名变量。name 属性是为了在调试时获得有用的相关信息,所以不能用 name 属性值去获取对函数的引用。