首页 > 技术文章 > 函数表达式中,函数名称在函数体内是只读的

xianshenglu 2017-12-25 18:22 原文

我们来看一个函数定义表达式:

var f=function a(){
    console.log(arguments.callee.name);//返回当前执行函数的 name
};
f();// a

可能有人觉得奇怪哈,很正常,因为一般我们不这么写,要么不带 a ,要么就直接用函数声明语句了,不过这种写法有它的优势,用来递归的时候很方便,详情 戳这里

这里主要来分析 a 的作用域的,上面的例子就为证明一点:

赋值给 f 的函数名称是 a ,不是 f

关键是,这个 a 的作用域问题,我们在 f 执行完后,访问 a:

image

报错了,证明 a 不是全局变量,实际上书上也是这么说的:

image

测试下,逻辑上没有问题:

image

不过我们不仅于此,我们在函数内给 a 重新赋值:

var f = function a() {
    a = 3;
    console.log(a);
};
f();

返回的是函数本身,说明赋值失败了:

image

严格模式下,报错,字面理解就是赋值给了常量(注意 , ES6 新增了常量 const,对于常量是不能重复赋值的):

image

这说明:

函数定义表达式中带函数名称的,函数名称只能作为常量在函数体内访问,不可以被重新赋值的,非严格模式下静默失败,严格模式下报错;

既然知道了这个,如果在函数体内依然要给 a 赋值也不是不行,换成变量就可以了;

var f = function a() {
    var a = 3;
    console.log(a);
};
f();//返回 3

当然这样就不能继续用 a 来访问原来的函数了,虽然还有其他办法,但是这样修改明显是不合适的,可读性不好;

另外,在函数声明语句中是可以的修改函数名称,比如:

function a() {
    a = 3;
    console.log(a);
}
a();//3,a 变成 3

但是,放在 IIFE 中,又不行了;

(function a() {
    a = 3;
    console.log(a);//函数,修改失败;
})()
a;//error not defined;

当然,var 仍然是可行的,

(function a() {
    var a = 3;
    console.log(a);//3 成功
})()

对于 IIFE 中执行失败的解释,我猜测:

() 强制 js 引擎 把里面的函数声明语句当成了表达式,那么这种情况下,这个效果不就等价于 带名称的函数定义表达式么?这个时候,函数名称应该也是局部变量且只能在函数内部访问,不可修改;

文档上是这么说的:

参考资料:

JavaScript权威指南-第6版

文档

https://stackoverflow.com/questions/24021489/variable-in-function-body-and-function-itself-have-the-same-name-javascript/24022076#24022076

https://stackoverflow.com/questions/15129504/why-are-anonymous-function-expressions-and-named-function-expressions-initialize

推荐阅读