zoukankan      html  css  js  c++  java
  • 闭包详解

    前言: 想要了解闭包,首先要了解另一篇作用域的文章。

    1.闭包是什么?

    js文件执行时会产生一个全局执行上下文,函数执行时会产生一个函数执行上下文。上下文创建时会生成变量对象。

    如果在本执行上下文值中访问其他执行下文中的变量对象,或者在其他执行上下文中访问本上下文中的变量,都会产生闭包。

    产生闭包的执行上下文不会销毁,不被垃圾回收机制自动回收。一直保留在内存中。

    缺点:

    闭包导致大量内存不能被js引擎的垃圾回收机制释放。从而造成网页性能问题。

    解决办法:

    使用块作用域。

    2. 闭包应用

    1. 监听函数和闭包

    function process(data) {
        // process
    }
    var someReallyBigData={/*data*/};
    process(someReallyBigData);
    var btn = document.getElementById('my_button');
    btn.addEventListener("click", function click(evt) {
        console.log('==clicked==');
    }, /*capture=*/false);
    // click函数是监听事件的回调,它拥有涵盖全局作用域的闭包。 
    // 正常process执行完成后,后面不再使用占用大量内存数据结构someReallyBigData,可以被回收掉。
    // 但是因为闭包不可回收,它还会继续保存这个数据结构。

    /********************解决方案***************************
    function process(data) { // process } { // 因为不在全局作用域中,完事就可以被销毁 var someReallyBigData={/*data*/}; process(someReallyBigData); } var btn = document.getElementById('my_button'); btn.addEventListener("click", function click(evt) { console.log('==clicked=='); }, /*capture=*/false);

    2. 块级作用域和闭包

    其实在定时器,事件监听,ajax请求等任务中的回调函数,其实都是在使用闭包。

    for(var i =1; i<=5; i++) {
        setTimeout(function timer() {
            console.log(i);
        }, 1000 * i);
    }
    // 这段代码本来想输入1-5,每秒一次,每次一个值
    // 实际情况是每秒输出一个值6,输入了5次。
    /**按照词法作用域分析,代码等同于下面*****/
    var i = 6;
    setTimeout(function timer() {
        console.log(i);
    }, 1000);
    setTimeout(function timer() {
        console.log(i);
    }, 2000);
    setTimeout(function timer() {
        console.log(i);
    }, 3000);
    setTimeout(function timer() {
        console.log(i);
    }, 4000);
    setTimeout(function timer() {
        console.log(i);
    }, 5000);
    因为timer是回调函数,所以,console中的i是最后获取。

    如果想要达到最初的目的,则需要每次取值的时候从自己的作用域取值,每次迭代的IIFE函数都有自己的作用域,最后取值的时候从各自的作用域取值。

    for(var i = 1; i<=5;i++) {
        (function IIFE(){
            var j = i;
            setTimeout(function timer() {
                console.log(j);
            }, 1000*j); 
        })()
    }
    // 等同于
    for(var i = 1; i<=5;i++) {
        (function IIFE(j){
            setTimeout(function timer() {
                console.log(j);
            }, 1000*j); 
        })(i)
    }

    let实现的闭包块作用域;每次迭代都是一个新的作用域。

    for(var i = 1; i<=5;i++) {  
        let j = i;  // 闭包的块作用域
        setTimeout(function timer() {
            console.log(j);
        }, 1000*j); 
    }
    // 等同于
    for(let i = 1; i<=5; i++) {
        setTimeout(function timer() {
            console.log(i);
        }, 1000*i);   
    }

    3. 函数柯里化

    函数柯里化的实现,本质上应用了闭包的概念。在函数作用域内部声明的函数,在函数作用域外执行

    function curry(fn, arr=[]) {
      const length = fn.length;
      return function(...args) {
        arr = arr.concat(args);
        if(arr.length < length) {
          return curry(fn, arr);
        }
        return fn(...arr);
      }
    }
  • 相关阅读:
    常见的arp欺骗
    ARP欺骗分析
    周总结
    周总结
    周总结
    win10下 修改host文件 新添域名不生效
    周总结
    周总结
    周总结
    周总结
  • 原文地址:https://www.cnblogs.com/lyraLee/p/11434901.html
Copyright © 2011-2022 走看看