深入理解函数声明与函数表达式
日常中,用到它们最大的一个区别点就是,函数声明能够提升到作用域顶端,即使在调用之后在声明函数也不会抛出错误,除此之外,我很少区分它们,直到有一天我纠结同时使用函数什么与函数作用域的时候才彻底深入研究,再回顾一下函数体的结构。
function fn(){}; 关键字 函数标识符 括号 函数体
这里面唯一可以省略的就是函数标识符,但是规范中明确表示函数声明不能省略标识符,所以当看到一个没有标识符的函数可以大胆的认为这是一个函数表达式;
- 函数声明必须要有函数标识符(没有将报错)
- 函数表达式允许没有函数标识符
照着上面的法则,明显还不够,因为有些时候依然还不能区分它们,就 像下面这几个例子:
+function fn(){}; new function NewFn(){}(); var fnx = function fnn(){}; //试着打印一下它们 fn(); NewFn(); fnn();
打印上面的结果,发现 fn,NewFn,fnn 都没有定义,因为它们都不是函数声明,而是函数表达式。
函数表达式的一个特性,在执行到该表达式的时候定义函数,而这些都是执行语句,所以它们定义函数都在执行过程中定义。
它只是执行了这条语句但是并没有声明,所以外部无法访问到它的标识符,但需要值得关注的是,这个标识符能够在它的函数体内访问,它的作用就跟 arguments.callee 一样
因此,我认为
- 在语句中的函数定义是函数表达式
- 只有 function 关键字在顶端的时候才是函数声明
- 函数表达式中函数的标识符只能在该函数体内访问
总结上面几点就基本能判断出该函数是函数声明还是函数表达式。
往外扩展
- 在函数表达式后面跟一个括号表示立即执行(函数声明不允许立即执行,它有声明提升的特性)
(function(){}()) //函数表达式立即执行 (function(){})() //函数表达式立即执行 !function(){}() //函数表达式立即执行 ...
任何函数表达式都可以立即执行,所以你看到任何立即执行函数它们都是函数表达式。
- 函数声明提升
函数执行中,体内的变量声明层次有明显的不同,它们的顺序如下:
- 变量声明提升
- 函数参数植入到函数体内
- 函数声明提升
可以试着执行下面这种场景
var fun = function fn(param){ console.log(param) var param = "678"; //根据变量声明提升的特性,console.log打印出来的应该是undefined function param(){ } //根据函数声明提升的特性,console.log打印出来的应该是该函数体 } fn(2); //传入的参数是2,如果不出意外console.log打印出来的应该是2
根据这段代码,很快就能分辨出它们在函数执行时声明的顺序。