JS闭包
2018-08-16 本文已影响0人
puxiaotaoc
一、变量的作用域
- 变量的作用域分为两种:全局变量和局部变量;
// 函数内部可以直接读取全局变量
var n = 1;
function fun1(){
alert(n);
}
fun1(); // 1
// 函数外部无法读取函数内的局部变量
function fun1(){
var n = 1;
alert(n);
}
alert(n); // n is not defined
// 函数内部声明变量的时候一定要使用var,否则实际上相当于声明了一个全局变量
function fun1(){
n = 1;
}
fun1();
alert(n); // 1
二、闭包的引入
- 匿名自执行函数实现
创建匿名自执行函数并立即执行他,由于外部无法引用它内部的变量,因此在执行完后很快会被释放,关键是这种机制不会污染全局对象;
(function(a){
console.log(a) // 3
}(3))
- 读取其他函数的内部变量,是将函数内部和函数外部连接起来的一座桥梁
// 在函数外部访问内部变量n
function fun1() {
var n = 1;
return function() {
alert(n);
}
}
var result = fun1();
result(); // 1
// getNameFunc的第一个()是属于方法调用,所以this绑定到了object对象,自然this.name为"My Object",但是闭包函数无法访问这个this,它只能访问到全局的this
// 当一个函数作为函数而不是方法调用的时候,这个this关键字引用全局对象
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return function() {
return this.name;
};
}
};
alert(object.getNameFunc()()); // The Window
// 并非闭包,并非一个函数内部的函数
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
return this.name; // My Object
// return name; // The Window
}
};
alert(object.name); // My Object
alert(object.getNameFunc());
var name = "The Window";
var object = {
name: "My Object",
getNameFunc: function() {
var _this = this;
return function() {
return _this.name;
}
}
};
alert(object.name); // My Object
alert(object.getNameFunc()()); // My Object
// 实现封装
// person之外的地方无法访问person内部的变量,只能通过提供闭包的形式来访问
var person = function() {
var name = "My Object";
return {
getName: function() {
return name;
},
setName: function(val) {
name = val;
}
}
}
var person1 = new person();
alert(person1.name); // undefined
alert(person1.getName()); // My Object
person1.setName('lili');
alert(person1.getName()); // lili
// 设置缓存
function db() {
var data = {};
return function(key, val) {
if (val === undefined) {
return data[key];
} else {
return data[key] = val;
}
}
}
var db = new db();
console.log(db('x')); // undefined
console.log(db('x',1)); // 1
console.log(db('x')); // 1
// 使用自执行函数设置缓存
var db = (function() {
var data = {};
return function(key, val) {
if (val === undefined) {
return data[key];
} else {
return data[key] = val;
}
}
})()
console.log(db('x')); // undefined
console.log(db('x',1)); // 1
console.log(db('x')); // 1
- 让一些变量的值始终保持在内存中
// 代码中的result实际上就是闭包fun2函数,result一共运行了两次,第一次的值是1,第二次是2,函数fun1的局部变量n一直保存在内存中,并没有在fun1调用后被自动清除,
// 因为fun1是fun2的父函数,而fun2被赋值给了一个全局变量,这导致fun2始终在内存中,而fun2的存在依赖于fun1,因此fun1也始终在内存中,不会在调用结束后被垃圾回收机制回收
// nAdd=function(){n+=1},因为没有在nAdd前面使用var关键字,所以nAdd是一个全局变量,而不是局部变量
// 其次,nAdd的值是一个匿名函数,而这个匿名函数本身也是一个闭包,所以nAdd相当于一个setter,可以在函数外部对函数内部的局部变量进行操作
function fun1() {
var n = 1;
nAdd = function() {
n += 1;
}
return function fun2() {
alert(n);
}
}
var result = fun1();
result(); // 1
nAdd();
result(); // 2
三、闭包的缺陷
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能会导致内存泄露,解决方法是,在退出函数之前,将不使用的局部变量全部删除;
- 闭包会在父函数外部,改变父函数内部变量的值,所以如果把父函数当作对象使用,把闭包当作它的公用方法,把内部变量当作它的私有属性,这时应小心不能随便改变父函数内部变量的值;