最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 作用域、执行上下文、闭包

    正文概述 掘金(自渡人)   2021-03-02   767

    相关内容

    • 作用域、作用域链
    • 执行栈、执行上下文
    • 闭包

    作用域和作用域链

    规定变量和函数的可使用范围叫做作用域。

    function fn1() {
      let a = 1;
    }
    
    function fn2() {
      let b = 2;
    }
    

    声明两个函数,分别创建量两个私有的作用域(可以理解为两个封闭容器),fn2 是不能直接访问私有作用域 fn1 的变量 a 的。同样的,在 fn1 中不能访问到 fn2 中的 b 变量的。一个函数就是一个作用域。

    作用域、执行上下文、闭包

    每个函数都会有一个作用域,查找变量和函数时,由局部作用域到全局作用域一次查找,这些作用域形的集合就成为作用域链。

    let a = 1
    function fn() {
      function fn1() {
        function fn2() {
          let c = 3;
          console.log(a);
        }
        // 执行 fn2
        fn2();
      }
      // 执行 fn1
      fn1();
    }
    // 执行函数
    fn();
    

    虽然上边看起来嵌套有点复杂,我们前边说过,一个函数就是一个私有作用域,根据定义,在 fn2 作用域中打印 a,首先在自己所在作用域搜索,如果没有就向上级作用域搜索,直到搜索到全局作用域,a = 1,找到了打印出值。整个搜索的过程,就是基于作用域链搜索的。

    执行栈和执行上下文

    执行上下文

    执行上下文分为三个,分别是:全局执行上下文、函数执行上下文、Eval函数执行上下文

    全局执行上下文

    这是默认或者说基础的上下文,任何不在函数内部的代码都在全局上下文中。它会执行两件事:创建一个全局 Window 对象(浏览器的情况下),并设置 this 的值等于这个全局对象。一个程序中只能有一个全局执行上下文。

    函数执行上下文

    每当一个函数被调用时,都会为该函数创建一个新的上下文。每个函数都有它自己的执行上下文,不过是在函数被调用时创建的。函数执行上下文可以有任意多个。

    Eval函数执行上下文

    执行在 eval 函数内部的代码也会有它属于自己的执行上下文。

    执行上下文的特点

    • 单线程,在主线程上运行
    • 同步执行,从上往下按顺序执行
    • 全局上下文只有一个,在关闭浏览器时会被弹出栈
    • 函数执行上下文没有数目限制
    • 函数没被调用一次,都会产生一个新的执行上下文环境

    执行栈

    执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)数据结构的栈,被用来存储代码运行时创建的所有执行上下文。

    当 JavaScript 引擎第一次遇到脚本时,它会创建一个全局的执行上下文并且压入当前执行栈。每当引擎遇到一个函数被调用,它会该函数创建一个新的执行上下文并压入栈的顶部。

    引擎会执行那些执行上下文位于栈顶的函数,当函数执行结束时,执行上下文会栈中弹出,控制流程到达当前栈中的下一个上下文。

    闭包

    什么是闭包

    函数执行,形成一个私有作用域,保护里面的私有变量不受外界的干扰,除了保护私有变量外,还可以保存一些内容,这样的模式叫做闭包。

    function fn() {
      let num = 1;
      let name = "jack";
      return function () {
        console.log(num++);
        return name;
      };
    }
    
    let f1 = fn();
    f1(); // 1
    f1(); // 2
    f1(); // 3
    f1(); // 4
    console.log(f1()); // 5 jack
    

    闭包的作用

    闭包的作用有两个:保存和保护

    保护

    • 团队开发时,每个开发者把自己的代码放在一个私有的作用域中,防止相互之间的变量命名冲突;把需要提供给别人的方法,通过 returnwindow.xxx 的方式暴露在全局下;
    • jQuery 的源码中也是利用了这种保护机制;
    • 封装私有变量;

    保存

    • 选项卡闭包的解决方案。我们经常在网页中使用选项卡,但是它存在一个问题,那就是索引引发的问题,其实和下边的经典面试题问题相同。
    var btnBox = document.getElementById("btnBox"),
      inputs = btnBox.getElementsByTagName("input");
    var len = inputs.length;
    for (var i = 0; i < len; i++) {
      inputs[i].onclick = function () {
        alert(i);
      };
    }
    

    此时运行程序,你会得出的结果都是 len 的数值。

    为什么会出现这种问题,我们如何解决呢?

    原因很简单,所有的事件绑定都是异步的,当触发点击事件,执行方法的时候,循环早就结束了。

    • 同步:JS 中当前这个任务没有完成,下面的任务都不会执行,只有等当前彻底执行完成,才会执行下面的任务;
    • 异步:JS 中当前任务没有完成,需要等一会在完成,此时我们可以执行下面的任务;

    解决方案

    当点击事件执行的时候,就会在私有作用域查找 i 的值,此时私有作用域没有 i ,就回去全局作用域查找,此时全局作用域的 i 已经被改变。所以说,要创建一个私有作用域的 i

    • 方式一:闭包的方式,用来保存私有的变量,但是闭包解决有又优点,也有缺点
      • 优点:通过创建私有作用域(闭包)方式解决,循环几次,就会创建几个私有作用域,然后每个私有作用域都会有一个私有变量 i,存的值分别是循环的值。
      • 缺点:生成多个不销毁的私有作用域(堆内存),对性能有一定的影响。
    var btnBox = document.getElementById("btnBox"),
      inputs = btnBox.getElementsByTagName("input");
    var len = inputs.length;
    for (var i = 0; i < len; i++) {
      (function(i) {
        inputs[i].onclick = function () {
          alert(i);
        };
      })(i)
    }
    
    • 方式二:使用自定义属性。我们给每个对象添加一个索引属性就可以了
    var btnBox = document.getElementById("btnBox"),
      inputs = btnBox.getElementsByTagName("input");
    var len = inputs.length;
    for (var i = 0; i < len; i++) {
      inputs[i].myIndex = i;
      inputs[i].onclick = function () {
        alert(this.myIndex);
      };
    }
    
    • 方式三:使用ES6
      • 终极解决方案,这是 ES6 中的知识,因为之前在 JS 中是没有块级作用域的概念的,到了 ES6 中就有了,let 声明的变量就可以更好的解决上述问题;
    var btnBox = document.getElementById("btnBox"),
      inputs = btnBox.getElementsByTagName("input");
    var len = inputs.length;
    for (let i = 0; i < len; i++) {
      inputs[i].onclick = function () {
        alert(i);
      };
    }
    

    闭包的内存回收机制

    内存回收机制就是不在用到的内存,我们系统就自动进行回收从而清理出空间供其他程序使用。那回收的规则是什么?作用域、执行上下文、闭包

    内部函数引用着外部的函数的变量,外部的函数尽管执行完毕,作用域也不会销毁。从而形成了一种不销毁的私有作用域。

    某一变量或者对象被引用着,因此在回收的时候不会释放它,因为被引用代表着被使用,回收器不会对正在被引用的变量或者对象回收的。

    大白话说什么是闭包,那就是在一个函数里边再定义一个函数。这个内部函数一直保持有对外部函数中作用域的访问权限。

    作用域、执行上下文、闭包

    函数执行,形成一个私有的作用域,保护里面的私有变量不受外界的干扰 ,除了保护私有变量外,还可以存储一些内容,这样的模式叫做闭包。

    经典面试题

    var num = 10;
    var obj = {
      num: 20
    };
    
    obj.fn = (function (num) {
      this.num = num * 3;
      num++;
      return function (n) {
        this.num += n;
        num++; 
        console.log(num);
      };
    })(obj.num);
    
    var fn = obj.fn;
    fn(5);
    obj.fn(10);
    
    console.log(num, obj.num);
    

    输出结果为:

    22
    23
    65 30
    

    起源地下载网 » 作用域、执行上下文、闭包

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元