关于闭包

2020-03-19  本文已影响0人  晴雨稀兮

闭包的概念

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

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

闭包和匿名函数的区别

【包含闭包的一些特性】

// 闭包
// 此处将【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.函数的作用域链(理解函数的作用域对象对理解闭包有帮助)

闭包的使用

实现函数节流

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
上一篇 下一篇

猜你喜欢

热点阅读