作用域链
要学习闭包,首先要了解作用域链
作用域链:环境对象中定义的变量,会放到作用域中,形成一个链式结构。
定义三个变量 a b c 使其在不同的作用域
var a = 10;
function F() {
var b = 20;
//内部函数:函数内部定义的函数
function N() {
var c = 30;
return a + b + c;
}
return N();
}
//验证N中是否都能使用所有的链上变量
console.log(F());//60
在变量 c 的作用域中,可以调用到 a b 在 b 的作用域中,可以调用到 a
但是反向就不能实现,a 的作用域中无法调用 b c ,b的作用域中 无法调用 c
如果我们想在变量作用域中访问其子作用域中定义的变量,要怎样实现呢?闭包的使用能解决这个问题
利用闭包突破作用域链
首先来看闭包的概念
闭包的原理就是在被访问的变量a的作用域中创建一个函数,将需要访问的变量当做新创建的函数返回值,再将函数定义给最外层的变量b,就能通过执行变量b来访问到变量a
原理如图:
代码如下:
//第一种写法
var a = 10;
function F() {
var b = 20;
function N(){
return b
}
return N;
}
var M = F();
console.log(M());
// 原理一样,写法不一样,第二种写法
var inner;
var a = 10;
function F() {
var b = 20;
function N(){
return b
}
inner = N;
}
F();
console.log(inner());
不过需要注意:
闭包会常驻内存,使用需要谨慎
闭包的概念清楚了,下面来讲一下闭包的应用
闭包的应用1
循环中的闭包
我们可以试着将函数里循环中的每一个变量拿出来。
敲一下代码:
function F() {
var arr = [];
for (var i = 0; i < 3; i++) {
arr[i] = function(){
return i;
};
}
return arr;
}
var str = F();
console.log(str[0](), str[1](), str[2]());//3 3 3
咦?什么鬼,怎么出现了三个一样的数
分析一下,不难发现,arr[i]中保存的是 i , i 是相当于直接定义在 F 函数的作用域中,执行完 F 函数 i 的值直接就已经变为 3 ,这样,我们就应该在循环的时候,就把数组里的值保存下来,这样就对了
闭包本身不保存上一作用域中变量的值,当使用时是去上面的作用域中查找相同名字变量,使用最近那一层作用域中的变量
修改代码如下:
function F() {
var arr = [];
for (var i = 0; i < 3; i++) {
arr[i] = (function(x){
return function(){
return x;
};
})(i);
}
return arr;
}
var str = F();
console.log(str[0](), str[1](), str[2]());//0 1 2
也有原理图,在此:
这样就能保存到我们想要的数据了
闭包的应用2
getter 和 setter
有时候,为了变量的安全考虑,我们一般会不让变量直接被访问到
就可以利用闭包来实现
看看代码:
var setNum;
var getNum;
(function(){
var num = 0;
//setter方法
setNum = function(x) {
//安全的考虑
if (typeof x === "number") {
num = x;
}
};
//getter方法
getNum = function() {
return num;
};
})();
console.log(num);//0
setNum(100);
console.log(getNum());//100
这样的话,直接访问 num 是访问不到的,同时要修改 num 的值也不能直接修改,通过闭包的方法,我们还能控制赋给变量新赋值的类型
闭包的应用3
迭代器
迭代: 一个一个寻找下一个目标
知道遍历简单的数组,以后便利的数据结构可能比较复杂
使用“下一个是谁”的想法去遍历
代码如下:
var arr = [1,2,3,4,5];
function setUp(x) {
var i = 0;
return function (){
if(x.length == i-1){
i = 0;
}
return x[i++];
}
}
var next = setUp(arr);
console.log(next());//1
console.log(next());//2
console.log(next());//3
console.log(next());//4
console.log(next());//5
console.log(next());//undefined
console.log(next());//1
console.log(next());//2
这样就实现了迭代的方法,一次一次找到下一个值