javascript 之闭包、作用域链
执行环境与作用域
执行环境
执行环境都有一个关联的变量对象,环境中定义的变量和函数都是这个对象的属性和方法。
- 全局执行环境——window对象(web浏览器中)
- 局部(函数)执行环境
全局变量对象始终存在,而局部变量对象只在函数执行过程中存在。
作用域
分为全局和局部作用域
ES6有了块级作用域。
作用域链
当在一个环境中执行时,会创建变量对象的作用域链,作用域链保存在函数的内部属性 [[scope]]
中,保证对执行环境有权访问的变量和函数进行有序访问。
-
作用域链前端:当前执行环境的变量对象->包含环境的变量对象->下一层包含环境的变量对象......->全局环境变量对象
-
标识符解析时:从作用域链前端一级一级的搜索,向后回溯,没有找到就报错。
作用域链本质上是一个指针列表,指针指向变量对象。
Note:
函数环境中,活动对象为变量对象,初始仅包含arguments对象
延长作用域链: catch 和 with 语句
闭包
闭包就是在一个函数内部可以访问这个函数作用域中变量的【匿名函数】。
function animals(name){
return function(){
console.log(name);
};
}
var a = animals("cat");
a(); //cat
内部的匿名函数的作用域链包含了自己、外部函数变量对象和全局变量对象,即使外部函数运行结束,外部函数的作用域链销毁,其外部函数变量对象仍被匿名函数作用域链所引用,所以外部函数变量对象一直在内存里,在这个匿名函数销毁时,才会销毁它的作用域链所引用的变量对象。
闭包一些问题
- 闭包占用内存
- 闭包保存整个变量对象,使用闭包是引用到的都是同一个变量
- 闭包的 this 一般指向 window
- 内存泄漏:如果闭包中保存了HTML元素,那这个元素将无法被销毁
模仿块级作用域
function test(){
for(var i=0; i< 10; i++){
console.log(i);
}
console.log(i); //在这里还可以访问到 i
}
模仿块级作用域的语法示例:
function test(){
(function(){
/*块级作用域*/
for(var i; ...){...}
})();
console.log(i); //报错~
}
常用在全局作用域添加函数,避免全局作用域的变量和函数过多。
私有变量
用在构造函数上,为自定义类型创建私有变量、私有函数和特权方法
//
function Person(name){ //name和age是私有变量
var age = 10;
function myAge(){ //私有函数
return age;
}
this.getAge = function(){
return myAge(); //用特权方法访问私有函数和属性
}
this.getName = function(){ //特权方法
return name;
};
this.setName = function(newName){
name = newName;
};
}
//静态私有变量
(function(){
var name = "";
Person = function(value){
name = value;
};
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function(value){
name = value;
};
})();
var p1 = new Person("Sam");
console.log(p1.getName()); //Sam
var p2 = new Person("Ben");
console.log(p2.getName()); //Ben
console.log(p1.getName()); //Ben
模块模式
var add = (function(){
var count = 0;
return { //返回一个对象,定义公共接口访问私有变量和函数
increase: function(){ return count += 1; },
decrease: function(){ return count -= 1; },
value: function(){ return count; }
};
})();
add.increase(); //
console.log(add.value());