函数在运行阶段之前还存在一个非常重要的阶段叫做词法分析阶段,该阶段主要分几步做了几件事:
第一步:分析参数
第二步:分析var变量声明
第三步:分析函数声明
具体步骤:
-
在函数运行前,生成Active Object(活动对象AO)。
-
把收到的形参作为AO属性,属性值为实参值。
-
分析var变量声明。如 var age。
如果AO上没有age属性,则在AO上添加对应的属性,值为undefined,如AO = {age: undefined}
如果AO上存在该属性,则不作任何操作
-
分析函数声明,如function foo () {}。则把函数赋给AO.foo属性;如果AO.foo已存在则会被覆盖。
1.词法分析例子
function a(b) {
console.log(b); //[Function: b]
function b() {
console.log(b); //[Function: b]
}
b()
}
a(1)
分析以上词法分析阶段以及运行阶段如下:
词法分析阶段
-
AO = {}
-
分析参数 AO = {b: undefined},接收实参 AO = {b: 1}
- 分析var变量:没有
- 分析函数声明 AO = {b: function () {console.log(b)}}
运行阶段
- console.log(b) 从AO中找b,发现b是个函数
- b() //由作用域找向外层函数中的b
稍微修改下代码,思考如下例子
function a(b) {
console.log(b); //1
b = function () {
console.log(b); //[Function: b]
}
b()
}
a(1)
词法分析阶段
- AO = {}
- 分析参数 AO = {b: undefined} -> {b: 1}
- 分析var变量声明 没有
- 分析函数声明 没有。注意:b = function () {}是赋值过程,在执行期执行
运行阶段
- console.log(b) 从AO中找b,找到,并打印1
- b = function () {console.log(b)} ,b进行赋值操作后,AO = {b:function () {console.log(b)}}
- b(),执行函数b,里面有console.log(b),b会沿着作用域向外找,找到AO中的b,即是自己
2.函数声明与函数表达式
//函数声明
function a () {}
//函数表达式
var a = function () {}
函数声明与函数表达式的最重要的区别就是是否会在词法分析阶段进行解析。函数声明会在词法分析阶段被解析,而函数表达式会先以变量的形式在词法分析阶段解析,然后在执行期进行赋值。
a() // a
function a() {
console.log('a');
}
b() //b is not a function
var b = function () {
console.log('b');
}
//自己思考下
所以词法分析阶段是变量声明提升以及函数声明提升的根本原因
3.一个有趣的函数
//一个有趣的函数
(function (a, undefined) {})('我是a')
上面是一个立即执行的匿名函数,有两个形参,但是为什么外部只传入一个参数?
其实这主要是因为undefined不是js中的保留关键字,所以undefined可以作为变量名使用,这样操作会造成一个结果,就是使用typeof进行undefined判断时产生影响。所以在函数中第二个形参是undefined但是又不传实参,所以结果就是undefined = undefined(理解起来就是变量名undefined的值是undefined)。在这个函数中就可以避免undefined带来的问题。