当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activation object)作为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。
var color = "blue";
function changeColor(){
if (color === "blue"){
color = "red";
} else {
color = "blue";
}
}
changeColor();
alert("Color is now " + color);
在这个简单的例子中,函数 changeColor()的作用域链包含两个对象:它自己的变量对象(其中定义着 arguments 对象)和全局环境的变量对象。可以在函数内部访问变量 color,就是因为可以在这个作用域链中找到它。
延长作用域链(catch、with、call、apply)
虽然执行环境的类型总共只有两种——全局和局部(函数),但还是有其他办法来延长作用域链。这两个语句都会在作用域链的前端添加一个变量对象:try-catch 语句的 catch 块; with 语句。
对with 语句来说,会将指定的对象添加到 作用域链中。对 catch 语句来说,会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
例如:
function buildUrl() {
var qs = "?debug=true";
with(location){
var url = href + qs;
}
return url;
}
这里with接受对是location对象,那么locatiion对象中对属性方法就被添加到了函数到作用域前端。因此函数buildUrl可以引用Location的href(实际上上location.href)
call()和apply() 这两种方法通常用于扩展函数的作用域。
apply(x,y) 接受两个参数,第一个函数上函数作用域;第二个参数上一个数组,它包含函数所要调用的参数,可以用数组实例或者arguments对象。
例如:
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 传入 arguments 对象,包含num1,num2
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 传入数组
}
alert(callSum1(10,10)); //20
alert(callSum2(10,10)); //20
这样,callSum1和callSum2就可以调用本来在全局中定义的sum 函数了。
call(x,y,z,...) call接受多个参数,除了第一个参数和apply一样上指定函数的作用域,其余的参数都是调用的参数,用逗号分开。
例如
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20
使用call和apply扩展函数作用域上很方便的。例如
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
使用call为函数sayColor指定作用域,当指定的作用域为o时,本来的alert(this.color)指向的就是对象o中的color。
除此之外,还有bind方法。它只接受一个参数,指定函数的作用域。例如:
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue