这篇文章,来继续谈谈Javascript闭包的剩余问题。因为在上一篇文章中关于JS闭包(JS闭包系列1)主要简单的示例代码直观的观察了下闭包,这篇文章就来从理论上好好地分析分析JS闭包的有关概念。
首先应该知道的是JS中的作用域链(scope chain)概念:
(1)定义
每个人都有自己的生活环境,代码也一样,JS中的代码都有自己的“执行环境”。当代码在一个环境中定义时(函数调用之前),就会生成变量对象的作用域链。这个链条,从当前环境中的变量对象开始,上溯至父环境(包含环境)中的变量对象,再到父环境的父环境中的变量对象···一直上溯到全局执行环境的变量对象。想象着一条锁链,从最下面开始爬,一直爬到最上顶端。每一个节点,代表着这一层级环境中的变量对象。
需要注意的是,作用域链是根据 函数定义 时的位置确定的,而不是在调用时确定。这也叫做 词法作用域 。
chain(2)作用
作用域链用来保证那些有权访问不同执行环境的代码,在对不同环境里的变量和函数访问时的有序性以及确定性。
(3)应用
当解析一个标识符时,是沿着作用域链一级一级地上溯(回溯)搜索标识符的过程。如果找不到标识符,就会产生错误。
还是直接看代码吧:
var color = "blue"; //全局作用域
function changeColor(){
if(color === "blue"){ //寻找`color`,此作用域(函数内)没有,则上溯到父级作用域,(这里是全局)找到了
color = "red";
}else{
color = "blue";
}
}
changeColor();
alert("Color is now " + color);
在这个例子中,函数changeColor的作用域链包含两个对象:它自己的变量对象(每个函数都有的arguments对象),以及全局环境中的变量对象。
再来看一个多层作用域链示例:
var weibing = "在"; //最外层守卫
var dajiangjun = "不在"; //2号大将军
function chengqiang(){ //城墙内
var dajiangjun= "在"; //1号大将军
function gongdian(){ //宫殿内
var zaixiang = "不在"; //宰相
if(zaixiang === "在")
console.log("把宰相找来!");
else if(dajiangjun === "在")
console.log("把大将军找来!");
else
console.log("把卫兵找来!");
}
return gongdian();
}
chengqiang();
结果可想而知:
皇帝召见层级
想象这样一个场景:皇帝身居寝宫,寂寞了,想要招纳美女。那么首先想到的是宰相,他可以直接召见宰相,让宰相给自己办事。假如宰相不在,那么他可以召见大将军(职级低于宰相),让大将军给自己干;碰巧,大将军也去喝酒了,那就直接召见卫兵。在这个例子中,皇帝找到大将军了。但是有人会问,这段代码里,有两个dajiangjun
啊!对啊,可是,皇帝他老人家是从自己身边找人的。最外边的那个2号大将军,虽然也在,但是他没上朝,去考察去了。(没在城墙内),所以就召见1号大将军喽。而且自然而然,在城墙外边的人(例如普通老百姓),当然是见不到宰相和皇帝的啦。
例如在上面代码下面加如下代码:
function chixinwangxiang(){ //痴心妄想
alert("把"+zaixiang+"给爷叫出来!");
}
结果是:
痴心妄想
在城墙外边,你肯定是见不到一人之下,万人之上的宰相大人啦!
(4)注意
因为在JS中,作用域里没有以花括号包围的“块级作用域”概念,看一个例子:
if(true){
var a = 1;
}
console.log(a); //输出1
表面上看来, a
在if语句里面定义着,那么console语句应该是访问不了的,然而事实恰恰相反,因为在JS中,if或者for语句的花括号,根本不是独立作用域,只有函数的花括号才起作用。
总结:现在知道了作用域链的概念,而你应该知道的是,闭包就是由作用域链引起的。下一节,就要带着作用域链的思维来思考闭包,那样就简单多了。因为你会发现那是顺其自然。
网友评论