当代码在在一个环境中执行时,会创建变量对象的一个作用域链(scope chain),作用域链是用来保证对变量和函数的有序访问。
作用域链的前端,始终是当前执行代码所在的变量对象。作用域链中的下一个变量对象来自外部环境,再下一个变量来自于下一个外部环境,一直延续带全局执行环境。全局执行环境的变量对象始终是作用域链中的最后一个对象。
标识符解析就是沿着作用域链一级一级地搜索标识符的过程。
如例子所示:
var animal = "dog"; function changeAnimal() { if (animal == "dog") { animal = "cat"; } else { animal = "dog"; } } changeAnimal(); console.log("Now Animal: " + animal);
在例子中,函数 changeAnimal() 的作用域链包含两个对象:它自己的变量对象(其中定义着 arguments 对象)和全局环境的变量对象。可以在函数内部访问 color ,就是因为可以在这个作业域链中找到它。
JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.
看一下下面这个列子,结果是什么?
function mammals() { var kind = "dog"; var canine = function () { console.log("It's a " + kind); } return canine; } function animal(wild) { var kind = wild; var func = mammals(); func(); } animal("wolf");
当调用 animal 的时候, scope chain是由: {window活动对象(全局)}->{animal的活动对象} 组成.
在刚进入animal函数体时, app的活动对象有一个arguments属性, 俩个值为undefined的属性: kind和func. 和一个值为’wolf’的属性wild;
此时的scope chain如下:
当调用 mammals 的函数体时,此时的 mammals 的 scope chain为:
注意到, 此时的作用域链中, 并不包含 animal 的活动对象。
在定义 canine 函数的时候, canine 函数的[[scope]]为:
从 mammals 函数返回以后,在体内调用 canine 的时候, 发生了标识符解析, 而此时的sope chain是:
因为scope chain中,并不包含 mammals 活动对象. 所以, kind 标识符解析的结果应该是 mammals 活动对象中的 kind 属性, 也就是 "dog"。
所以运行结果是:
It's a dog