美文网首页
关于闭包

关于闭包

作者: 晴雨稀兮 | 来源:发表于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

相关文章

  • 闭包介绍

    闭包 关于闭包的定义: A closure is the combination of a function an...

  • SwiftUI 里的 swift 闭包总结

    创建 UI 时的闭包使用 在 SwiftUI 里闭包出现的频率特别高,这里我重新梳理了下闭包的定义。 关于闭包 闭...

  • JavaScript - 闭包

    理解 关于闭包 答案: 用arguments.callee和闭包实现的函数封装 应用 利用闭包实现自动递增计数

  • 关于闭包

    闭包,官方对闭包的解释是:一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达...

  • 关于闭包!!!

    写这篇文章时的心情是十分忐忑的,因为对于我们今天的主角:闭包,很多小伙伴都写过关于它的文章,相信大家也读过不少,那...

  • 关于闭包

    闭包的定义: MDN javascriptkit 词法作用域 (lexical environment) 函数在执...

  • 关于闭包

    闭包的英文是closure,又称词法闭包(Lexical Closure)和函数闭包(Function Closu...

  • 关于闭包

    卡尔维诺中文站留言板这个帖子专门用作卡尔维诺中文站的留言板,欢迎大家留言和提问。...阮一峰2007-01-04T...

  • 关于闭包

    尾随闭包(Trailing Closures) * 如果函数需要一个闭包参数作为参数,且这个参数是最后一个参数,而...

  • 关于闭包

    1. 什么是闭包? 有什么作用 闭包: 作用:1.可以读取函数内部的变量:(如)0_1482243109358_Q...

网友评论

      本文标题:关于闭包

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