zoukankan      html  css  js  c++  java
  • js——重点之闭包

      不同人站在不同的角度对闭包有不同的解释,分别有以下三种比较权威的解释:

        函数与其词法环境的引用共同构成了闭包。也就是说,闭包可以让你从内部函数访问外部函数作用域,在JavaScript中函数每次创建时生成闭包。——MDN

        函数可以记住并访问所有的词法作用域时,就产生了闭包。即使函数是在当前作用域外执行。——《你不知道的JavaScript(上卷)》

        有权访问另一个函数作用域中的变量的函数就是闭包。——《JavaScript高级程序设计(第三版本)》

      在我看来,浏览器加载页面会把代码放到栈内存中执行(ECStack),函数进栈执行会产生一个私有的上下文(EC),这个上下文能保护里面的私有变量(AO)不受外界干扰,并且如果当前上下文中的某些内容被上下文以外的内容所占用,当前上下文是不会出栈释放的,这样可以保存里面的变量和变量值,所以闭包可以看成是一种保存和保护内部私有变量的机制。

      下面可以通过几道题目深入的理解什么是闭包?闭包的作用?

    let x = 1;
    function A(y){
       let x = 2;
       function B(z){
           console.log(x+y+z);
       }
       return B;
    }
    let C = A(2);
    C(3);

      

      在上面的代码执行中我们要注意以下几点:

      1.VO和GO的区别和关联性

        区别:GO是全局对象,它是一个堆内存,里面存放的是浏览器提供的内置属性和方法,浏览器会让window指向GO,所以在浏览器端window代表的就是全局对象。每一个执行环境都是有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中, 这个变量对象称之为VO(Variable Object)。它们是不同的。

        关联:基于var/function在全局上下文中声明的全局变量根据映射机制也会给GO赋值一份,但就let/const等es6方式在全局上下文中创建的全局变量和GO没有任何关系。

      2.函数执行

        每一个函数执行都会形成一个私有的上下文,私有上下文形成后,会有一个存放私有变量对象的AO,它会进入ECStack执行。每一个执行上下文形成之后,代码执行之前,要进行变量提升 。var/function存在变量提升,let/const不存在变量提升。接下来要初始化作用域链、初始化this、初始化arguments、形参赋值、变量提升,最后才是代码执行。

      3.不销毁的上下文

        正常情况下,代码执行完成后,私有上下文会出栈释放,节约栈内存空间,但是如果当前私有上下文中的东西被上下文以外的事物所引用,则上下文不会在出栈释放,形成了不销毁的上下文。

    let x = 5;
    function fn(x) {
        return function(y) {
            console.log(y + (++x));
        }
    }
    let f = fn(6);
    f(7);
    fn(8)(9);
    f(10);
    console.log(x);

     

       真实项目中为了保证js的性能(堆栈内存的性能优化)应该减少闭包的使用。但是又因为闭包它可以保护私有变量不受外界的干扰,形成不销毁的栈内存,把一些值保存下来,方便后面代码调取使用又不可避免的使用闭包。

      示例:选项卡

      html:

    var oTab = document.getElementById('tabBox'),
        navBox = document.getElementById('navBox');
    tabList = navBox.getElementsByTagName('li'),
        divList = oTab.getElementsByTagName('div');
    
    function changeTab(curIndex) {
        for (var i = 0; i < tabList.length; i++) {
            tabList[i].className = divList[i].className = '';
    
            tabList[curIndex].className = 'active';
            divList[curIndex].className = 'active';
        }
    }

      css;

    * {padding: 0; margin: 0;}
    ul {list-style: none;}
    #tabBox {box-sizing: border-box; 500px;margin: 100px auto;}
    #navBox {display: flex;position: relative;top: 1px;}
    #navBox li {box-sizing: border-box;padding: 0 10px;margin-right: 10px;line-height: 35px;border: 1px solid #999;}
    #navBox li.active {border-bottom-color: #fff;}
    #tabBox div {display: none;box-sizing: border-box;padding: 10px;height: 150px;border: 1px solid #999;}
    #tabBox div.active {display: block;}

      js:

    var oTab = document.getElementById('tabBox'),
        navBox = document.getElementById('navBox'),
        tabList = navBox.getElementsByTagName('li'),
        divList = oTab.getElementsByTagName('div');
    
    function changeTab(curIndex) {
        for (var i = 0; i < tabList.length; i++) {
            tabList[i].className = divList[i].className = '';
            tabList[curIndex].className = 'active';
            divList[curIndex].className = 'active';
        }
    }
    /* 
    for (var i = 0; i < tabList.length; i++) {
        tabList[i].onclick = function () {
            changeTab(i);
            //解释:
            //=> 事件绑定是“异步编程”,当触发点击行为,绑定的方法执行时,外层循环已经结束:方法执行产生私有作用域,用到变量i,这个i不是私有的变量,
         //按“作用域链”的查找机制,找到的是全局下的i(此时全局的i已经完成,成为循环最后的结果) } } */ // 解决方法: /* //1.自定义属性 for (var i = 0; i
    < tabList.length; i++) { tabList[i].myIndex = i; tabList[i].onclick = function () { changeTab(this.myIndex); //this:给当前元素的某个事件绑定方法,当事件触发,方法执行的时候,方法中的this是当前操作的元素对象 } } */
    //2.闭包 利用闭包机制,把后续需要的索引存储到自己的私有作用域中“闭包有保存作用” /* for (var i
    = 0; i < tabList.length; i++) { tabList[i].onclick = (function (n) { //让自执行函数执行,把执行的返回值return赋值给onclick var i = n; return function () { changeTab(i); //上级作用域:自执行函数形成的作用域 } })(i); } */
    //3.es6 它和闭包机制类似,es6中let创建变量时会形成块级作用域,当前案例中,每一轮循环都是会形成一个自己的块级作用域,把后续需要用到的索引i存储到自己的作用域中 /* for (let i
    = 0; i < tabList.length; i++) { tabList[i].onclick = function () { changeTab(i); } } */

      

     

      

  • 相关阅读:
    HTML(六)——表单验证、正则表达式、事件
    ADO。Net(二)——防止SQL注入攻击
    ADO.Net(一)——增、删、改、查
    【转】高性能服务器架构(High-Performance Server Architecture)
    【Python】使用Python将Shellcode转换成汇编
    【技术知识】恶意PDF文件分析-PDFdump的问题
    【逆向工具】IDA Python安装与使用
    【windows核心编程】HideProcess
    Virut.ce-感染型病毒分析报告
    【黑客免杀攻防】读书笔记11
  • 原文地址:https://www.cnblogs.com/davina123/p/12012036.html
Copyright © 2011-2022 走看看