执行环境
执行环境是javascript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其它数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。
全局执行环境是最外围的一个执行环境。在WEB浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。对全局执行环境变量来说,变量对象是window对象,对函数来说,变量对象就是这个函数的活动对象(活动对象是在函数调用时创建的一个内部变量)。
每个函数都有自己的执行环境,当执行流进入一个函数时,函数的执行环境就会被推入一个执行环境栈中。而在函数执行之后,栈将执行结束的函数的执行环境弹出,把控制权返回给之前的执行环境。
作用域链
作用域链与一个执行环境相关,作用域链用于在变量中查找。
在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其它对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],由ECMA-262标准第三版定义,他就指向了这个函数的作用域链。作用域链中存储的是与每个执行环境相关 *变量对象 *(函数内部也是活动对象)。
函数创建的时候,这个时候作用域链中只有一个 变量对象 (window)
当执行下面的代码:
<script type="text/javascript">
function sum(num1, num2){
var sum = num1 + num2;
return sum;
}
var sum = sum(3, 4);
</script>
当调用 sum 函数时,会首先创建一个 “执行环境”,这个 执行环境 有自己的作用域链,这个作用域链初始化为 sum 函数的 [[scope]] 所包含的对象。然后创建一个 与这个执行环境相关的 变量对象( 活动对象 ) ,这个 变量对象 中存储了在这个函数中定义的所有参数、变量和函数。把 变量对象 存储在作用域中的顶端。 以后在查找变量的时候,总是从作用域链条的最内端开始查找,一直到作用域链条的顶端。
看下面的示意图:
说明:
- 在sum中访问一个变量的时候,总是从作用域链的最内层开始查找,如果找到就得到结果,如果找不到就一直查找,直到作用域链的最外端(顶端)。
- 因为在方法内的存在变量和函数的声明提前现象,所以函数一旦执行 函数的活动对象(变量对象)中总是保存了这个函数中声明的所有变量和函数。
- 如果在函数中又定义了一个内部函数(还没有执行),则这个时候内部函数的作用域,是包含了外部函数的作用域。 一旦内部函数开始执行则把自己的活动对象添加到了这个作用域的顶端。
闭包:
闭包是一种特殊的对象。它由两部分构成: 函数,以及创建该函数的环境 。环境由闭包创建时在作用域中的任何局部变量组成。它有权访问另一个函数作用域中的变量的函数。
广义上的闭包:
只要一个函数,访问这个函数外的变量,那么这个函数就是一个闭包
狭义上的闭包:
如果一个函数,访问了她的 外部函数的局部变量,那么这个函数就是一个闭包;
闭包的特点:闭包可以访问它的外部函数作用域内的任何的变量,而且访问到的
变量的值一定是最新的值,即使外部函数执行结束了,也能访问外部函数的局部变量
看下面的代码:
<script type="text/javascript">
function createSumFunction(num1, num2){
return function () {
return num1 + num2;
};
}
var sumFun = createSumFunction(3, 4);
var sum = sumFun();
alert(sum);
</script>
在上面的代码中,createSumFunction函数返回了一个匿名函数,而这个匿名函数使用了createSumFunction函数中的局部变量(参数),即使createSumFunction这个函数执行结束了,由于作用域链的存在,他的局部变量在匿名函数中仍然可以使用,这个匿名函数就是闭包。
闭包的应用
1.返回外部函数的局部变量
看下边的代码:
<script>
var a=10;
function foo() {
var b=10;
function f() {
console.log(b)//访问的是外部函数foo里边变量最新b的值 输出30
}
b=30;
return f
}
foo()()
</script>
console.log访问的是外部函数foo里边变量最新b的值 输出30
结论:闭包中使用的局部变量的值,一定是局部变量的最后的值。
2.闭包封装对象
封装一个能够增删改查的对象
<script>
function foo(){
var p = {
name: "李四",
age: 40
}
return {
getName : function (){
return p.name;
},
getAge : function (){
return 20;
},
setName : function (name){
p.name = name;//修改它的name属性
},
addAttr : function (key, value){
p[key] = value; //添加一个属性
},
getAttr : function (key){
return p[key];
}
}
}
var p = foo()
p.setName("abc")
console.log(p.getName());
p.addAttr("sex", "男");
console.log(p.getAttr("sex"));
网友评论