闭包是指 有权访问另一个函数作用域中的变量 的函数。
简单的来说 就是 一个函数在执行过程中 返回另一个函数/对象 (引用类型 一般都是函数)
如何形成闭包?
1. 在函数内容部,返回一个引用类型(数组,对象,函数,以函数为主)
2. 返回的引用类型(数组,对象,函数,以函数为主),使用函数中的局部作用域变量
3. 在函数的外部,有变量来引用 引用类型
function fn(){
var a=1;
return function(){
return a++;
}
}
var f1 = fn();
console.log(f1());
console.log(f1());
1, 函数关联的活动对象 不会被销毁
优点: 空间中的内容,永远存在
缺点: 会占用大量的内存空间,造成内存泄露
2, 可以从函数外部调用,使用函数内部的数据
优点: 调用数据更加方便
缺点: 容易泄露数据信息,不安全
3, 保护私有变量(减少全局变量的使用)
优点: 私有变量,不会被销毁
缺点: 私有变量存储占用空间
匿名函数自调用
for (var i = 0; i < liList.length; i++) {
// fn(0)
(function (i) { // i 形式参数 局部变量
var li = liList[i];
// 页面加载时 此处只做事件绑定
li.onclick = function () { // 当我点击li的时候 页面已经加载完毕
// li 函数本身没有变量 li,i ( 向外跳一层找父作用域 )
li.style.background = "red";
console.log(li, i);
}
})(i);
}
// 函数防抖
document.onclick = (function () {
var flag = false;
return function () {
if (flag) {
return false;
}
flag = true;
setTimeout(function () {
console.log(111111);
flag = false;
}, 1000)
}
})()
(https://blog.csdn.net/qappleh/article/details/80311443)
作用域: 代码在执行过程中的有效区域.
作用域链: 在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问
每个函数运行时都会产生一个执行环境,而这个执行环境怎么表示呢?js为每一个执行环境关联了一个变量对象。环境中定义的所有变量和函数都保存在这个对象中。 (全局GO对象, 函数AO对象);
全局执行环境是最外围的执行环境,全局执行环境被认为是window对象,因此所有的全局变量和函数都作为window对象的属性和方法创建的。
js的执行顺序是根据函数的调用来决定的,当一个函数被调用时,该函数环境的变量对象就被压入一个环境栈(开辟一个执行空间)中。而在函数执行之后,栈将该函数的变量对象弹出,把控制权交给之前的执行环境变量对象。
举例说明
var scope = "global";
function fn1(){
var a = 1;
var b = 2;
return scope;
}
function fn2(){
return scope;
}
fn1();
fn2();
执行情况演示:
了解了环境变量,再详细讲讲作用域链。
当某个函数第一次被调用时,就会创建一个执行环境(execution context)以及相应的作用域链,并把作用域链赋值给一个特殊的内部属性([scope])。然后使用this,arguments(arguments在全局环境中不存在)和其他命名参数的值来初始化函数的活动对象(activation object)。当
以上面的代码为例,当第一次调用fn1()时的作用域链如下图所示:
可以看到fn1活动对象里并没有scope变量,于是沿着作用域链(scope chain)向后寻找,结果在全局变量对象里找到了scope,所以就返回全局变量对象里的scope值。
作用域链升级应用之闭包
function outer(){
var scope = "outer";
function inner(){
return scope;
}
return inner;
}
var fn = outer();
fn();
outer()内部返回了一个inner函数,当调用outer时,inner函数的作用域链就已经被初始化了(复制父函数的作用域链,再在前端插入自己的活动对象),具体如下图:
一般来说,当某个环境中的所有代码执行完毕后,该环境被销毁(弹出环境栈),保存在其中的所有变量和函数也随之销毁(全局执行环境变量直到应用程序退出,如网页关闭才会被销毁)
但是像上面那种有内部函数的又有所不同,当outer()函数执行结束,执行环境被销毁,但是其关联的活动对象并没有随之销毁,而是一直存在于内存中,因为该活动对象被其内部函数的作用域链所引用。
具体如下图:
outer执行结束,内部函数开始被调用
outer执行环境等待被回收,outer的作用域链对全局变量对象和outer的活动对象引用都断了
像上面这种内部函数的作用域链仍然保持着对父函数活动对象的引用,就是闭包(closure)
ter执行结束,内部函数开始被调用
outer执行环境等待被回收,outer的作用域链对全局变量对象和outer的活动对象引用都断了
像上面这种内部函数的作用域链仍然保持着对父函数活动对象的引用,就是闭包(closure)