JavaScript | 彻底搞懂JS闭包

闭包是js开发惯用的技巧,什么是闭包?

闭包指的是:能够访问另一个函数作用域的变量的函数

简单说:闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
    return inner    // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}

匿名函数与闭包的关系,闭包是站在作用域的角度上来定义的
因为 inner 访问到 outer 作用域的变量,所以inner就是一个闭包函数。
虽然定义很简单,但是有很多坑点,比如this指向、变量的作用域,不注意的话可能就造成 内存泄露

为什么闭包函数能够访问其他函数的作用域 ?
从堆与栈的角度看待JS函数

栈内存:基本数据类型( Number 、Boolean、Undefined、String、Null)
堆内存:对象类型的变量

var num = 10 //num是基本数据类型
Var num2 = {name:'Jack'}  //num2是对象


当num2 = {name:'Tom'} 时,堆 里边就有一个新的对象{name:'Tom'},原来的{name:'Jack'} 就会被程序垃圾引擎回收掉,节约内存空间

JS函数
function fn() {
            var a = 1
            var b = 2
            console.log(a)
            var p = function () {
                console.log(b)
            }
            return p    
        }
        fn()
        var p2 = new fn()
        p2()

当程序在调用某个函数时,做了以下的工作:准备执行环境,初始函数作用域链和arguments参数对象

注:arguments参数对象:是所有(非箭头)函数中都可用的局部变量。你可以使用arguments对象在函数中引用函数的参数。
此对象包含传递给函数的每个参数,第一个参数在索引0处

当程序执行完var p2 = new fn(),其实fn的执行环境并没有被销毁,因为他里面的变量a仍然被被inner的函数作用域链所引用

当程序执行完p(), 这时候,fn和p的执行环境才会被销毁调;

由于闭包会携带包含它的函数的作用域,因为会比其他函数占用更多内容,过度使用闭包,会导致内存占用过多

注意的是闭包看似简单,但是又有很多的细节与坑点

坑点一:引用的变量可能发生变化
function fn() {
      var arr= [];
      for (var i = 0; i<10; i++){
        arr.[i] = function () {
            console.log(i)
            }
    }
     return arr
}

我们认为误认为 输出的结果为0,1,2...,10,但并不是
因为每个闭包函数访问变量i是fn执行环境下的变量i,
随着循环的结束,i已经变成10了,所以执行每个闭包函数,结果打印10, 10, ..., 10

解决办法:
function fn() {
      var arr= [];
      for (var i = 0; i<10; i++){
        arr.[i] = function (num) {
            return function(){
                 console.log(i)
                }
            }(i)
    }
     return arr
}
坑点二:this指向问题
var object = {
     name: ''object",
     getName: function() {
        return function() {
             console.info(this.name)
        }
    }
}
object.getName()()    // underfined
// 因为里面的闭包函数是在window作用域下执行的,也就是说,this指向windows
坑点三:内存泄露问题
function  showId() {
    var el = document.getElementById("app")
    el.onclick = function(){
      aler(el.id)   // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
}

    // 改成下面
    function  showId() {
    var el = document.getElementById("app")
    var id  = el.id
    el.onclick = function(){
      aler(id)   // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
    }
    el = null    // 主动释放el
}

猜你喜欢

转载自www.cnblogs.com/pp-yang/p/12354239.html