关于变量和函数名的理解

var f = function g(){ return 23; };typeof g();//报错
这里如何理解?
我的理解是:g()代表了调用函数,结果为什么不是number呢?
请大神讲解

阅读 2.6k
3 个回答

var x = function y(){//...};这种形式,被《JavaScript忍者秘籍》叫做内联命名函数,它有个特点,就是这里的x是对外可见的,但y是被x“金屋藏娇”的,也就是说,y受限于x的作用域、对外不可见。

所以建议改成:

var f = function g(){
    return 23;
};
console.log(f);

(如果直接logtypeof f();这样,控制台会打出个“function”,貌似意义不大……当然直接console.log(typeof f());打个“number”也ok~)

var f = function g(){ return 23; };typeof g();//报错

会有这段代码的来由,主要是IE8前的浏览器并不会报错,而是呈现你要的结果,也就是'number'字符串。

其他楼主的回答并是正确的,我只是来补充一些的。


JS中对于函数的声明语法在标准中就有两种不同的语法样式,其一称为函数声明,另一个是函数表达式,实际上标准中也写得很简略,像下面这样,上面的是函数声明下面则是函数表达式,opt记号代表的是可选的,也就是可有可无的意思:

FunctionDeclaration :
function Identifier ( FormalParameterList opt ){ FunctionBody }

FunctionExpression :
function Identifier opt ( FormalParameterList opt ){ FunctionBody }

所以在函式表达式这种样式,函数的识别名是可以不需要有的,但它因为是个表达式,可以赋给另一个变量当值来看待,所以才会有像var f = function(){ return 23 }的这种写法。

要分辨何者是函数声明又何者是函式表达式,最简单的方式就是看语句的开头,以function作为该语句开头的函数语句,应该就是函数声明函式表达式不会以function开头,所以下面的几例都是函式表达式:

var a = function() {
    return 3;
}
 
var a = function bar() {
    return 3;
}
 
(function sayHello() {
    alert("hello!");
})();

而为何会有两种不同的函数语法样式,是这两种样式的用处不同。最明显的例子是函数声明有特殊的提升(hoisting)特性,在同一作用域中的函数声明会先被提升到此作用域的最上面,所以函数声明可以写在代码文档的后面,但可以在代码文档的上面使用。此外,函数声明只能在函数中块级或整个应用的全局区中使用,它没办法在其他的块级中使用,例如像if、for等的花括号({})中。而在某些情况下,当需要把函数整体作为一种值,用来当其他函数的传参或返回时,例如回调函数的语法结构,就是要使用函式表达式的样式才可以达到。当然,基本上这两者看起来好像都是长得一样,实际上在执行阶段的运作并不相同。

不过,本题的重点是在于有名称的函数表达式,也就是所谓的"具名函数表达式"(Named function expressions,NFE),这个函数的识别名,它的作用域到底是在什么地方,答案是在函数的主体(FunctionBody)内部。原因当然它只是个原本就可有可无的"代理"函数名,真正的这函数识别名称是被赋值的那个变量识别名。

正常情况下,你只能在函数表述式中的主体中使用这个"代理函数名",这也是符合标准的规定,如下面的例子:

var f = function foo(){
  return typeof foo; 
};
typeof foo; // "undefined"
f(); // "function"

那么又为何要使用这个"代理函数名",不是可有可无的吗?

因为这个名称在调试时,可以明确地在呼叫堆叠中看到,如果是不加这名称,也就是"匿名函数表达式"在调试时是看不准是呼叫什么的。这使得调试时多了一些便利,所以它会被用在这个情况下。

话又说回上面所说的在IE8之前的版本,怎么又能用这个"代理函数名",像一般的函数声明一样了?

因为以IE8来说,它里面的JS引擎并不是现在的标准ECMAScript规范,而是JScript 5.8。

IE8并没有设计这个封闭作用域,来界定出函数表达式的作用域,而且,在IE8中认为这种有"具名函数表达式",相等于函数声明。最厉害的一点是,IE8还会认为这两个函数(被赋值的变量与这个代理名)是两个不同的函数物件,例如下面的例子:

var f = function foo(){
  return 23; 
}

alert(f === foo); //false

所以,本题的解答是因为具名函数表达式的这个代理函数名,被限制在函数表达式内的作用域才能使用,所以会造成浏览器报错,但在IE8以下的IE浏览器则是可以正常运作。

以上的资料主要参考Named function expressions demystifiedFunction Declarations vs. Function Expressions

具名函数表达式的函数名字本身,作用于只在其函数本身之中,而没有在外部。

推荐问题
宣传栏