作用域简单的说就是一个变量的可见范围。
废话少说,先上一段代码瞧瞧:
<script type="text/javascript">
var a = 10;
function f()
{
alert(a);
var a = 20;
}
f();
</script>
输出结果是什么呢? 答案是undefined, 呵呵,是不是没想到呢? why? 这就要说到作用域了。
下面听我慢慢道来。。
当一个函数被创建时,Js引擎会自动给该函数添加一个[[scope]]成员, 该成员我们看不见也摸不着,该成员的值为创建该函数时的词法环境对象, 又说到了词法环境对象,不清楚的可以去看前面js的解释与执行这篇文章哦。
当函数真正执行时,会创建一个词法环境对象le, le与scope有关联关系,le–>f.[[scope]]。
代码如下:
<script type="text/javascript">
function f(){ //创建时:f.[[scope]] == window
//执行时 le{x:undefined}-->f.[[scope]]
var x = 100;
function g(){//g.[[scope]] --> f.le{x:100}--->f.[[scope]]
//le--->g.[[scope]]
}
g();
}
f();
//此时形成了作用域链
g.le-->g.[[scope]]-->f.le-->f.[[scope]]==window
</script>
我们来分析下这段代码:
在预处理阶段,函数f已被创建,当创建的时候,js引擎会给函数f增加一个属性[[scope]],这个属性我们看不见也摸不着,他的值为创建函数f时的词法环境对象,在这里即window对象。
f.[[scope]] == window
当f被调用时,会创建自己的词法环境f.le:{x:undefined, g:指向函数的引用}, f.le执行f.[[scope]]即
f.le–>f.[[scope]]。
此时函数g被创建,同样的g.[[scope]] == f.le, 当g被调用时,g.le–>g.[[scope]],这样就形成了一个链条,即作用域链。
g.le–>g.[[scope]] == f.le–>f.[[scope]]==window
注意:如果使用new Function创建函数,则该函数的scope==window
<script type="text/javascript">
var x = 200;
function f(){ //创建时:f.[[scope]] == window
//执行时 le{x:undefined}-->f.[[scope]]
var x = 100;
var g = new Function("", "alert(x)"); //g.[[scope]] == window
g();
}
f();
//此时形成了作用域链
//g.le-->g.[[scope]]-->window
</script>
输出结果:200
作用域的本质:
当在某个函数中查找一个变量x时,首先在当前函数的词法环境中找,如果找不到则到父函数的词法环境中找,直到window对象。