1.闭包的理解
1.怎么产生闭包: 当一个嵌套的内部函数引用了外部函数的变量(函数)时。
2.产生的条件:(1)函数嵌套
(2)内部函数引用了外部函数的数据和变量;
3.什么是闭包:存在于嵌套函数中,包含了被引用变量(函数)的对象。
4.常见的闭包:(1)将函数作为另一个函数的返回值
function fn1() {
var a = 2;
function fn2() {
a++; //存在于嵌套函数中,包含了被引用变量(函数)的对象
console.log(a);
}
return fn2();
}
(2)将函数作为实参传递给另外一个函数调用
function showDelay(msy,time) {
setTimeout(function () {
alert(msy);
},time)
}
showDelay("aaa",2000);
5.闭包的作用:(1)延长局部变量的生命周期(使用函数内部的变量在函数执行完后,任然存活在内存中);
(2)让函数外部可以操作(读/写)函数内部的数据;
6.闭包的生命周期:产生:在嵌套内部函数定义完的时候
死亡:嵌套的内部函数成为垃圾对象时候;
7.闭包的应用:自定义js模块
(定义js模块:具有特定功能的js文件,将所有的数据和功能封装在一个函数内部,只向外暴露n个对象或者方法)
(1)
function doModule() { //自定义的js模块
var msg = "ZhangJie";
function doSometing() {
console.log(msg.toUpperCase());
}
function doOtherthing() {
console.log(msg.toLowerCase());
}
return { //向外暴露
doSomething:doSometing,
doOtherthing:doOtherthing
}
}
var d = new doModule();
d.doOtherthing();
d.doSomething();
(2)
(function (w) {
var msg = "ZhangJie";
function doSometing() {
console.log(msg.toUpperCase());
}
function doOtherthing() {
console.log(msg.toLowerCase());
}
w.Module ={ //向外暴露
doSomething:doSometing,
doOtherthing:doOtherthing
}
})(window);
引入js文件在使用
Module.doOtherthing();
Module.doSomething();
8.缺点:函数执行完,函数内部的局部变量没有释放,占用的时间会变长,容易造成内存泄漏
9.解决方法:能不使用闭包就不使用
即使释放 赋值为null
10.常见的面试题
(1)
var name = "the window";
var obj = {
name:"my object",
getName:function () {
return function () {
return this.name; //注意this是谁
}
}
};
console.log(obj.getName()()); //the window
(2)
var name2 ="the window";
var obj2 = {
name2:"my obj2",
getName:function () {
var that = this;
return function () {
return that.name2;
}
}
}
console.log(obj2.getName()()); // my obj2
(3)
function fun(n,o) {
console.log(o);
return {
fun:function(m){
return fun(m,n)
}
}
}
var a = fun(0); //undefined
a.fun(1); //0
a.fun(2); //0
a.fun(3); //0
var b = fun(0).fun(1).fun(2).fun(3); //undefined 0 1 2
var c = fun(0).fun(1); //undefined 0
c.fun(2); //1
c.fun(3); //1
注意:闭包会存储值
常见需求: 给每一个按钮绑定点击事件,且按钮内获取当前为第几个按钮
1.错误方法 - 出现闭包现象1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<div id="box1">
<button>button1</button>
<button>button2</button>
<button>button3</button>
<button>button4</button>
<button>button5</button>
<button>button6</button>
<button>button7</button>
</div>
<script>
//4.1.有多个按钮,给每个按钮绑定点击事件
var buttons = document.querySelectorAll("button");
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function () {
console.log(i); //点击任意按钮打印数字7
}
}
</script>
</body>
</html>
解释: 最简单的理解 - for内,点击事件外,为一个作用域,在这个作用内,i可以为0,,1,2,3,4,5,6
- 点击事件内为一个作用域,点击事件内的数据是for循环已经执行完的数据,即式在循环完毕后,i变为7
比较绕一点的理解 同步异步执行的问题,for循环为同步执行,需要紧挨着步骤执行
2.如何去解决这种闭包 - 达到内部使用外部的i
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<div id="box1">
<button>button1</button>
<button>button2</button>
<button>button3</button>
<button>button4</button>
<button>button5</button>
<button>button6</button>
<button>button7</button>
</div>
<script>
var buttons = document.querySelectorAll("button");
// 利用立即执行函数
for (var i = 0; i < buttons.length; i++) {
(function (i) {
buttons[i].onclick = function () {
buttons[i].style.display = 'none';
}
})(i)
}
</script>
</body>
</html>
原理解释 - 利用立即执行函数将值传递到点击事件内
3.利用let进行解决
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
</style>
</head>
<body>
<div id="box1">
<button>button1</button>
<button>button2</button>
<button>button3</button>
<button>button4</button>
<button>button5</button>
<button>button6</button>
<button>button7</button>
</div>
<script>
let buttons = document.querySelectorAll("button");
for (let i = 0; i < buttons.length; i++) {
buttons[i].onclick = function () {
buttons[i].style.display = 'none';
}
}
</script>
</body>
</html>
原理解释 - let会将值进行保存 - 推荐使用,方法简单好理解