JS作用域与词法分析
一、作用域&作用域链
JS的作用域是通过函数划分的,函数的作用域在定义阶段就已经确定:
1.在最外层函数和在最外层函数外面定义的变量拥有全局作用域
var name="amos"; function foo(){ var age=18; function inner(){ console.log(age); } inner(); } console.log(name); // amos //console.log(age); // Uncaught ReferenceError: age is not defined foo(); // 18 inner(); // Uncaught ReferenceError: inner is not defined
2.所有未定义直接赋值的变量自动声明为拥有全局作用域,例如:
变量age拥有全局作用域,而sex在函数外部无法访问到
var name="amos"; function foo(){ age=18; var sex="male" } foo(); console.log(age); // 18 console.log(sex); // sex is not defined
3.所有window对象的属性拥有全局作用域
一般情况下,Window对象的内置属性都拥有全局作用域。例如window.alert()、window,location、window.top等。
4.在函数内部定义的变量,拥有局部作用域
二、AO对象&词法分析
JS代码运行分为两个阶段:先进行词法分析,再执行。
一个函数被调用的一瞬间,产生一个活动对象AO(Active Object),词法分析会为该对象填充属性。
词法分析根据作用域链,从外到里进行分析,分为三步:
1.分析形参/实参
2.分析var声明的变量(注意,变量的值是在执行时候决定的)
3.分析函数声明
示例演示:
//-----**********************例1********************************* var s=12; function f(){ console.log(s); var s=12; // if s=12 console.log(s) } f(); // undefined // 12 //-----**********************例2********************************* var s=10; function foo(){ console.log(s); var s=5; console.log(s); function s(){console.log("ok")}// 函数的定于或声明是在词法分析时完成的,执行时已不再有任何操作 console.log(s); } foo(); // function s // 5 // 5 //-----***********************例3******************************** function bar(age) { console.log(age); var age = 99; var sex= 'male'; console.log(age); function age() { alert(123) }; console.log(age); return 100; } result=bar(5);
我们以最复杂的第三个例子来解析一下整个过程:
1、调用bar函数的瞬间,生成AO对象,
2、分析形参/实参:
1)函数声明时的形参,作为AO的属性,默认值undefined,即AO.age = undefined
2)接收实参5,给AO.age属性赋值,即AO.age = 5
3、分析变量声明:
1)如果AO还没有该属性,添加;如果有(相当于重新声明),不执行任何操作,原来的值不做任何修改
2)第3行,找到声明var age,判断已经存在AO.age,不作任何处理
3)第4行,找到声明var sex,添加AO.sex = undefined
4、分析函数声明:
1)如果AO还没有该属性,添加;如果有,则直接覆盖。
2)第7行,找到function age( )声明,而原来AO.age=5,直接被覆盖为AO.age = function age( )。
5、执行过程:
- 执行第2行 console.log(age)时,当前AO.age = function age( ) ,所以输出函数
- 执行第3行,age = 99 时,对AO.age赋值 99
- 执行第4行,sex = 'male'时,对AO.sex赋值 male
- 执行第5行,console.log(age)时,AO.age = 99 —— 输出99
- 注意!第7行 function.age( ) 函数声明,不进行任何操作,因为词法分析阶段已经完成了。
- 执行第11行,console.log(age)时,AO.age还是99——输出99
- foo函数返回100