美文网首页
关于闭包

关于闭包

作者: 晴雨稀兮 | 来源:发表于2020-03-19 13:51 被阅读0次

    闭包的概念

    闭包就是当一个函数即使是在它的词法作用域之外被调用时,也可以记住并访问它的词法作用域。

    闭包是依赖于词法作用域编写代码而产生的结果

    闭包和匿名函数的区别

    【包含闭包的一些特性】

    • 闭包是有权访问另一个函数作用域中变量的函数,常见的创建方式,就是在函数内部创建另一个函数;
    • 立即执行函数是用一对圆括号包起来的函数,多用在模拟块级作用域,防止变量污染环境,或者是防止内存泄露,以为执行完之后,立即执行函数的执行环境都被销毁了。
    • 立即执行函数的作用域链不会在外层函数之外保留,而闭包会。
    • 只能取得外部函数的任何变量的最后一个有效值
    • 相同点,执行环境具有全局性,this对象通常指向window,但是可以通过call、apply进行改变
    // 闭包
    // 此处将【var 改成let】 可以实现匿名函数的效果
    function createFn() {
        var res = new Array();
        for(var i=0; i<5; i++) {
            res[i] = function() {
            console.log(this)
                return i;
            }
        }
        return res;
    }
    createFn()[0]();    // 输出5
    createFn()[1]();    // 输出5
    
    // 而用匿名函数的方式
    function createFn() {
        var res = new Array();
        for(var i=0; i<5; i++) {
            res[i] = (function(num){
                return function() {
                    return num
                }
            })(i)
        }
        return res;
    }
    createFn()[0](); // 0
    createFn()[1](); // 1
    createFn()[2](); // 2
    createFn()[3](); // 3
    createFn()[4](); // 4
    createFn()[5](); // undefined
    

    闭包中的this对象

    ==this对象是基于函数的执行环境绑定的==,在全局函数中,通常指向window,而当函数被作为某个对象的方法调用时,会指向这个对象;然而匿名函数的this对象,通常指向window,这里的window函数包含闭包。

    每个函数在调用时,都会自动获取两个特殊变量,arguments和this,内部函数在搜索这两个变量时,只搜索到内部函数自己的活动对象为止,因此永远不可能直接访问外部函数中的这两个变量。但是如果把外部作用域中的this保存在一个闭包能访问到的变量里,那么在闭包函数中,就可以访问外部函数的的this了。

    所以闭包还有一个特性,就是可以访问外部函数的this和argument。

    function fun(n,o) {
      console.log(o)
      return {
        fun:function(m){
          return fun(m,n);
        }
      };
    }
    
    var a = fun(0); a.fun(1); a.fun(2);a.fun(3);    // 分别输出 undefined,0,0,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
    

    关于this的指向

    var name = 'Window';
    var obj = {
        name: 'wangxiaoer',
        getName: function() {
            return function() {
                console.log(this.name);
            }
        }
    }
    
    console.log(obj.getName()());   // 输出'Window'
    ------------------------------------------------
    var name = 'Window';
    var obj = {
        name: 'wangxiaoer',
        getName: function() { 
            var _this = this;  
            console.log(this)
            return function() {
                console.log(_this.name);
            }
        }
    }
    console.log(obj.getName()());   // 输出'wangxiaoer',this指向obj,将this赋值给_this,然后在闭包中使用;
    
    

    关于内存泄露

    ==当某个函数被调用时,会创建一个执行环境和响应作用域链,使用arguments和其他命名参数的值来初始化函数的活动对象。但在作用域链中,外部函数的活动对象始终位于第二位,外部函数的外部函数的活动对象处于第三位,知道作用域链的终点为全局执行环境。在执行环境中,为读取和写入变量的值,就需要在作用链中查找。==

    ==一般来说,当函数执行完毕时,局部活动对象就会被销毁,内存中仅保存全局执行环境中的活动对象,but, 闭包是个例外。因为闭包在被执行时,里面还保存着外层函数的活动对象,因此会比其他函数占用更多的内存。==
    即外层函数已经销毁后,它的活动对象还存在与内存中,因为被闭包引用。

    解决方法:内部函数解除对外部函数活动对象的引用

    function outFn(args) {
        return function() {
            console.log(args);
            return args
        }
    }
    var createV = outFn(1);
    createV();
    createV = null;
    

    p.s.函数的作用域链(理解函数的作用域对象对理解闭包有帮助)

    • 1.创建函数outerFun()时,会创建一个预先包含全局变量对象的作用域链,保存在内部的[[Scope]]属性中。
    • 2.调用函数outerFun()时,为此函数创建一个执行环境。
    • 3.然后复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。
    • 4.此后,创建一个活动对象,推入执行环境作用域链的前端([0]位置)。
    • 此时执行环境的作用域链中包含两个变量对象:全局变量对象(第3步) 、 局部活动对象(第4步)。
    • 函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。
    • 函数执行完后,局部活动对象被销毁,内存中仅保存全局作用域。

    闭包的使用

    实现函数节流

    let reduce = (func, delay) => {
        let timer = null;
        return function(...args) {
          if(timer) {
            clearTimeout(timer);
          }
          timer = setTimeout(() => {
            console.log(this);
            func.apply(this, args);
          }, delay)
        }
    }
    
    let scrollFn = reduce(()=>{
    console.log('节流啦啦啦')
    // console.log(this);
    }, 1000)
    
    console.log(scrollFn);
    
    document.addEventListener('scroll', scrollFn ,true);
    

    实现单例模式

    function CreateDiv(html) {
        this.html = html;
        this.init();
    }
    CreateDiv.prototype.init = function() {
        let div = document.createElement('div');
        div.innerHTML = this.html;
        document.body.appendChild(div);
    }
    
    let singleton = (function() {
        let instance;
        return function(html) {
            if(!instance) {
                instance = new CreateDiv(html);
            }
            return instance;
        }
    })()
    
    let s1 = new singleton('single1');
    let s2 = new singleton('single2');
    

    实现模块封装

    var foo = (function () {
        var something = 'cool';
        var other = [1, 2, 3];
        
        function getSomething() {
            console.log(something);
        }
        function getOther() {
            console.log(other)
        }
        return {
            getSomething: getSomething,
            getOther: getOther
        }
    })()
    
    foo.getSomething(); // cool;
    foo.getOther();     // 1,2,3
    

    相关文章

      网友评论

          本文标题:关于闭包

          本文链接:https://www.haomeiwen.com/subject/igxlyhtx.html