闭包的概念
闭包函数:声明在一个函数中的函数,叫做闭包函数。
闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回了之后。
闭包是有权限访问其他函数作用域的局部变量的一个函数
由于在JS中,变量的作用域属于函数作用域,在函数执行后作用域就会被清理、内存也随之被收回,但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数—也就是闭包,便拥有了访问上级作用域中的变量的权限,即使上级函数执行完后,作用域内的值也不会被销毁。
闭包的特点
- 让外部访问函数内部变量成为可能;
- 局部变量会常驻在内存中;
- 可以避免使用全局变量,防止全局变量污染;
- 会造成内存泄漏(有一块内存空间被长期占用,而不被释放)
- 函数内部嵌套函数
- 闭包的
this
指向window
闭包的创建
闭包就是可以创建一个独立的环境,每个闭包里面的环境都是独立的,互不干扰。**闭包会发生内存泄漏,每次外部函数执行的时候,外部函数的引用地址不同,都会创建一个新的地址。**但凡是当前活动对象中有被内部子集引用的数据,那么这个时候,这个数据不删除,保留一根指针给内部活动对象。
闭包内存泄漏为:key = value
,key
被删除了value
常驻内存中;局部变量闭包升级版(中间引用的变量)=> 自由变量。
闭包解决了什么问题
在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
由于闭包可以缓存上级作用域,那么就使得函数外部打破了“函数作用域”的束缚,可以访问函数内部的变量。
以平时使用的 AJAX 成功回调为例,这里其实就是个闭包,由于上述的特性,
回调就拥有了整个上级作用域的访问和操作能力,提高了极大的便利。
开发者不用去学钩子函数来操作上级函数作用域内部的变量了。
闭包的应用场景
闭包随处可见,一个 Ajax 请求的成功回调,一个事件绑定的回调方法,一个 setTimeout 的延时回调,或者一个函数内部返回另一个匿名函数,这些都是闭包。简而言之,无论使用何种方式对函数类型的值进行传递,当函数在别处被调用时,都有闭包的身影。
结论:闭包找到的是同一地址中父级函数中对应变量最终的值。
demo1:
function funA () {
var a = 10; // funcA的活动对象之中
return function () { // 匿名函数的活动对象
alert(a)
}
}
var b = funA()
b() // 10
demo2:
function outerFn() {
var i = 0;
function innerFn() {
i++;
console.log(i)
}
return innerFn;
}
var inner = outerFn() // 每次外部函数执行的时候, 都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
inner()
inner()
inner()
var inner2 = outerFn()
inner2()
inner2()
inner2()
// 1 2 3 1 2 3
demo3:
var i = 0
function outerFn() {
function innerFn() {
i++
console.log(i)
}
return innerFn
}
var inner1 = outerFn()
inner1()
inner1()
inner1()
var inner2 = outerFn()
inner2()
inner2()
inner2()
// 1 2 3 4 5 6
demo4:
function fn() {
var a = 3;
return function() {
return ++a;
}
}
alert(fn()()); // 4
alert(fn()()); // 4
demo5:
function outerFn() {
var i = 0;
function innerFn() {
i++;
console.log(i)
}
return innerFn
}
var inner1 = outerFn()
var inner2 = outerFn()
inner1(); // 1
inner2(); // 1
inner1(); // 2
inner2(); // 2
demo6:
(function(){
var m = 0;
function getM() {
return m;
}
function setA(val) {
m = val;
}
window.g = getM;
window.f = setA;
})();
f(100);
console.log(g()) // 100 闭包找到的是同一地址中父级函数中对应变量最终的值
demo7:
function a() {
var i = 0;
function b() {
alert(++i);
}
return b;
}
var c = a();
c(); // 1
c(); // 2
demo8:
function f() {
var count = 0;
return function() {
count++;
console.info(count)
}
}
var t1 = f()
t1();
t1();
t1();
// 1 2 3
demo9:
var add = (x) => {
var sum = 1;
var temp = (x) => {
sum = sum + x;
return temp;
}
temp.toString = () => {
return sum
}
return temp;
}
alert(add(1)(2)); // 3
alert(add(1)(2)(3)); // 6
demo10:
<body>
<ul>
<li>Lorem ipsum dolor sit.</li>
<li>Est illo vitae porro!</li>
<li>Enim magnam tempora sed.</li>
<li>Assumenda, optio. Ex, incidunt?</li>
</ul>
</body>
<script>
var lis = document.getElementsByTagName("li");
for(var i = 0; i< lis.length; i++) {
(function(i) {
lis[i].onclick = function() {
console.log(i)
}
})(i); /// 事件处理函数中闭包的写法
}
// 0 1 2 3
</script>
demo11:
const m1 = () => {
var x = 1;
return function () {
console.log(++x)
}
}
m1()(); // 2
m1()(); // 2
m1()(); // 2
var m2 = m1()
m2(); // 2
m2(); // 3
m2(); // 4
demo12:
var fn = (function () {
var i = 10;
function fn() {
console.log(++i);
}
return fn;
})()
fn();
fn();
demo13:
function love1() {
var num = 223;
var me1 = function () {
console.log(num)
}
num++;
return me1;
}
var loveme1 = love1()
loveme1(); // 224
demo14:
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);
c.fun(2);
c.fun(3); // undefined 0 1 1
demo15:
function fn() {
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function () {
return i
}
}
return arr;
}
var list = fn();
for (let i = 0, len = list.length; i < len; i++) {
console.log(list[i]())
}
// 5 5 5 5 5
demo16:
function fn() {
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = (function (i) {
return function () {
return i;
}
})(i)
}
return arr;
}
var list = fn();
for (let i = 0, len = list.length; i < len; i++) {
console.log(list[i]())
}
// 0 1 2 3 4