JavaScript高级程序设计(第3版)学习笔记7 js函数(2)


函数声明: function Identifier (参数列表(可选)){函数体}
函数表达式:function Identifier(可选)(参数列表(可选)){函数体}


除了函数表达式的标识符(函数名)是可选的之外没有任何区别,但我们也可以从中得知:没有函数名的一定是函数表达式。当然,有函数名的,我们就只能从上下文来判断了。

(2)从上下文区分,这个说起来简单,就是:只允许表达式出现的上下文中的一定是函数表达式,只允许声明出现的上下文的一定是函数声明。举一些例子:

复制代码 代码如下:


function fn(){};//函数声明
//function fn(){}(); // 异常,函数声明不能直接调用
var fn = function fn(){};//函数表达式
(function fn(){});//函数表达式,在分组操作符内
+function fn(){console.info(1);}();//1,函数表达式,出现在操作符+之后,因此可以直接调用,这里,也可以使用其它的操作符,比如new
new function fn(){console.info(2);}();//2,函数表达式,new操作符之后
(function(){
function fn(){};//函数声明
});


(3)区别:我们为什么要花这么大力气来区分函数声明和函数表达式呢?自然就是因为它们的不同点了,他们之间最大的不同,就是声明会提升,关于声明提升,在前面基础语法的那一篇文章中,曾经对全局作用域中的声明提升做过讨论,我们把那里的结论复习一下:

A、引擎在解析时,首先会解析函数声明,然后解析变量声明(解析时不会覆盖类型),最后再执行代码;

B、解析函数声明时,会同时解析类型(函数),但不会执行,解析变量声明时,只解析变量,不会初始化。

在那里也举了一些例子来演示(回忆一下),不过没有同名称的声明例子,这里补充一下:

复制代码 代码如下:


console.info(typeof fn);//function,声明提升,以函数为准
var fn = '';
function fn(){
}
console.info(typeof fn);//string,由于已经执行了代码,这里fn的类型变为string
try{
fn();//已经是string类型,不能调用了,抛出类型异常
}catch(e){
console.info(e);//TypeError
}
fn = function(){console.info('fn');};//如果想调用fn,只能再使用函数表达式赋值给fn
fn();//fn,可以调用

console.info(typeof gn);//function
function gn(){
}
var gn = '';
console.info(typeof gn);//string


可以看出:不管变量声明是在前还是在后,在声明提升时都是以函数声明优先,但是在声明提升之后,由于要执行变量初始化,而函数声明不再有初始化(函数类型在提升时已经解析),因此后面输出时就成为String类型了。

上面第3行定义了一个函数,然后第7行马上调用,结果竟然不行!你该明白保持全局命名空间清洁的重要性了吧,要不然,你可能会遇到“我在代码中明明定义了一个函数却不能调用”这种鬼事情,反过来,如果你想确保你定义的函数可用,最好就是使用函数表达式来定义,当然,这样做你需要冒着破坏别人代码的风险。

还有一个问题,这里我们怎么确定变量类型是在初始化时候而不是在变量声明提升时候改变的呢?看下面的代码:

复制代码 代码如下:


console.info(typeof fn);//function
function fn(){
}
var fn;
console.info(typeof fn);//function


可以看到,声明提升后类型为function,并且由于没有初始化代码,最后的类型没有改变。

  关于函数声明和函数表达式,还有一点需要注意的,看下面的代码:

复制代码 代码如下:


if(true){
function fn(){
return 1;
}
}else{
function fn(){
return 2;
}
}
console.info(fn());// 在Firefox输出1,在Opera输出2,在Opera中声明提升,后面的声明会覆盖前面的同级别声明

if(true){
gn = function(){
return 1;
};
}else{
gn = function(){
return 2;
};
}
console.info(gn());// 1,所有浏览器输出都是1


在ECMAScript规范中,命名函数表达式的标识符属于内部作用域,而函数声明的标识符属于定义作用域。

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wddsgd.html