「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」
在JavaScript中,函数就是值。JavaScript 中的每个值都有一种类型,那么函数是什么类型呢?你知道【函数表达式】是什么,那么你了解什么是【命名函数表达式】吗?一起来看看吧!
函数的属性
在 JavaScript 中,函数就是对象。你可以把函数想象成可被调用的“行为对象(action object)”,函数不但可以调用,还可以当做对象来处理:增/删属性,按引用传递等。
🎨 示例:假如有以下这样一个函数
function sayHi() {
console.log("Hi");
}
📖 如果想要获取函数名字应该怎么做呢?
我们上面说了,函数就是对象,对象就有属性,而函数对象就包含了一些便于使用的属性,如以下示例就是要要了函数对象的属性name。
除了name,函数还有一个内置属性length,它会返回函数入参的个数。
💥 注:我们之前有提到过Rest参数...👉传送门,它是不会被length计算到的。
除了上面【函数对象】自带的两个属性,我们还可以自定属性。
🎨 如以下示例 ,给函数新增了一个start属性,并且赋值为一个函数,直接gameLoop.start()即可执行这个函数。
应用实例
函数可以自定义属性,有什么实际意义呢?
其实很多知名的 JavaScript 库都充分利用了这个功能,可以利用这个功能,创建一个“主”函数,然后给它附加很多其它“辅助”函数。这样一个库就只会有一个全局变量,可以减少对全局空间的污染,降低了命名冲突的可能性。
例如:lodash 库创建一个 _ 函数,然后为其添加了 _.add、_.keyBy 以及其它属性。
所以,一个函数本身可以完成一项有用的工作,还可以在自身的属性中附带许多其他功能。
命名函数表达式
我们已经说过什么是【函数表达式】👉传送门
【函数表达式】示例
let sayHi = function() {
console.log( "Hello" );
};
📖那么【命名函数表达式(NFE,Named Function Expression)】又是什么呢?
很简单,来看一个示例就明白了
let sayHi = function func(who) {
console.log(`Hello, ${who}`);
};
可以看到我们在普通函数表达式里,又加了一个名字func,这样它就是一个【命名函数表达式】了。
📖 那么给【函数表达式】加名字的目的又是什么?
回答这个问题之前,我们先来看看一般函数调用自身是怎么写的
let sayHi = function(who) {
if (who) {
console.log(`Hello, ${who}`);
} else {
sayHi("Guest"); //调用自身
}
};
在大多数情况下我们这样做并没有什么问题,但是值得注意的是sayHi 的值可能会被函数外部的代码改变。
这段代码,我们把函数sayHi赋值给了welcome,随后修改了sayHi函数,可以发现welcome调用报错了。
这是因为该函数从它的外部词法环境获取 sayHi。没有局部的 sayHi 了,所以使用外部变量。而当调用时,外部的 sayHi 是 null。所以调用就直接报错了。
但是给函数表达式添加了名字之后再来看
可以看到函数正常运行了,这是因为名字 func 是函数局部域的。它不是从外部获取的(而且它对外部也是不可见的)。规范确保它只会引用当前函数。
外部代码仍然有该函数的 sayHi 或 welcome 变量。而且 func 是一个“内部函数名”,可用于函数在自身内部进行自调用。
🍇 所有综上所述,我们现在可以来回答为什么要给【函数表达式】加名字,因为它有这两个特殊的地方
✅ 它允许函数在内部引用自己。
✅ 它在函数外是不可见的。
注:【命名函数表达式】依然是一个函数表达式。在
function后面加一个名字"func"没有使它成为一个函数声明,因为它仍然是作为赋值表达式中的一部分被创建的。函数依然可以通过sayHi()来调用.
这里所讲的“内部名”特性只针对函数表达式,而不是函数声明。对于函数声明,没有用来添加“内部”名的语法。
参考资料:Function object, NFE
🎨【点赞】【关注】不迷路,更多前端干货等你解锁
往期推荐