函数名

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

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(..)来手动改变它。

results matching ""

    No results matching ""