JS闭包的作用
本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。 闭包可以用在许多地方。 它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。2009年8月30日学习Javascript闭包(Closure) - 阮一峰的网络日志
错误示例
<body>
<p class="myP">test</p>
<p class="myP">test2</p>
<p class="myP">test3</p>
</body>
目的: 输出当前点击节点的序号
var nodes = document.getElementsByClassName('myP');
var add_the_handlers = function(nodes){
var i
for(var i = 0; i < nodes.length; i++){
nodes[i].onclick = function(e){
console.log(i);
}
}
}
add_the_handlers(nodes);
输出结果: 节点总数 / 最后一个节点的序号
错误原因: 内部函数在使用外部变量时使用引用的方式而不是复制
引用和复制
在JS中,属于引用数据类型的有 对象、数组、函数,这些数据类型传的是址。
属于复制数据类型的有 复制:数字、布尔,这些数据类型传的是值。
传值的比较比较的是数值 而传址的比较比较的是引用,引用不同即使数值相同也不等
1 == 1; //true
1 === 1; //true
[0] == [0]; //false
[0][0] == [0][0]; //true
[0][0] === [0][0]; //true
[0].toString() == [0].toString(); //true
举例说明
var a = 1;
var b = a; //赋的是a的复制值
b ++;
alert(a); //"1" b的修改不影响a
var a = [1];
var b = a; //赋的是a的引用
b[0] ++;
alert(a); //"2" b的修改对a也有效
函数的参数
传值的传递:传给函数的是数值的一个复制,函数中对其的修改外部不可见
var a = 1;
var b = 2;
function change(a,b) {
var c = a;
a = b; //用新引用覆盖
b = c;
alert(a); //"2"
alert(b); //"1"
}
change(a,b);
alert(a); //"1"
alert(b); //"2"
传址的传递:传给函数的是数值的一个引用,函数中对其属性的修改外部可见,但用新引用覆盖其则在外部不可见
var a = [1, 2, 3];
var b = [5, 6];
function change(a,b) {
a[0] = 4; //对其属性的修改外部可见 外部输出结果从1,2,3变为4,2,3
var c = a;
a = b; //用新引用覆盖 外部输出结果不变
b = c;
alert(a); //"5,6"
alert(b); //"4,2,3"
}
change(a,b);
alert(a); //"4,2,3"
alert(b); //"5,6"
如果要使上述ab在函数外部互换,就需要通过作用域来实现。
作用域
var a = [1, 2, 3];
var b = [5, 6];
function change() {
var c = a;
a[0] = 4;
a = b;
b = c;
};
change();
alert(a); //"5,6"
alert(b); //"4,2,3"
因为js没有块级作用域,所以它在change里找不到变量a、b,所以会向上级寻找,操作的都是全局变量的引用。
闭包
在搞明白了复制、引用和作用域的概念后,我们就可以去更好的理解闭包了,还是用上面的例子
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p class="myP">test</p>
<p class="myP">test2</p>
<p class="myP">test3</p>
</body>
<script>
window.onload = function(){
var nodes = document.getElementsByClassName('myP');
// 通过立即执行函数每当i改变前,使内部函数获得外部变量i的复制并绑定事件。
// 示例1: 通过 匿名函数 立即执行来传递外部变量i
var add_handlers = function (nodes) {
var i;
for (i = 0, l = nodes.length; i < l; i ++) {
// 接收一个参数i
// 该参数为数字类型,传进去后是数字的复制,而非引用
nodes[i].onclick = function (i) {
// 返回函数给事件处理器,以实现点击后弹窗的操作
return function(){
// 此处的i值是传进来的i,属于函数的私有变量
alert(i);
}
}(i); // 通过(i)来使函数立即执行,同时通过(i)使其获得外部变量i的复制
}
};
// 示例2:通过 辅助函数 来传递外部变量i
var add_the_handlers = function(nodes){
var helper = function(i){
return function(){
alert(i); // 此处的i是函数形参的值,属于helper函数的私有变量
}
}
var i;
for(i = 0; i < nodes.length; i++){
// 通过立即执行函数helper(i)来将外部变量i的值的复制传递给了helper
nodes[i].onclick = helper(i)
}
}
add_the_handlers(nodes);
}
</script>
</html>
通过闭包,我们可以实现从内部函数拿到外部函数的变量。