zoukankan      html  css  js  c++  java
  • JavaScript 之 预编译 作用域,作用域链

    第一次写博客,本来是学习jQuery遇到闭包问题,发现并没有理解闭包,发现闭包牵扯的知识点太多。复习了一遍(发现自己该记住的全忘了)写在博客里,自己也是小白,希望大神们指点迷津,必将感激不尽。
    我们知道JavaScript有两个特点单线程,解释型语(翻译一行,解释一行)。但其实翻译一行,解释一行是最后一部才这样做,在这之前会有一个语法分析:通篇扫描看有没有语法错误,但不执行,通篇扫描之后,会进行 预编译 然后 -->解释一行,执行一行。这就是我们所说的js运行三部曲:语法分析     预编译     解释执行
     
    没错,预编译开始啦!
    test()                        //VM129:1 Uncaught ReferenceError: test is not defined
     
    console.log(a)         // VM118:1 Uncaught ReferenceError: a is not defined
     

    test();     //456

    function test(){

      console.log(456);
    };
     
    console.log(a);      //undefined
    var a = 123;
    上面四段代码当我们执行前两个的时候报错,浏览器告诉我们 test 和 a 没有被定义,而后两段代码并没有报错,这就是预编译。
    在学习预编译的时候我们总是记住一句话:函数声明整体提升,变量    声明提升。也就是说预编译会把整个函数放在最最前面。而变量 声明提升是什么意思呢?
    var a = 123;其实是变量声明和赋值的两个过程;1)var a;     2)a = 123;预编译只把声明提升在最前面
     
    console.log(a); //undefined
    var a = 123;
    --->
    var a;
    console.log(a); //undefined
    a = 123;
    --------------------------
    test(); //456
    function test(){
    console.log(456);
    }
    --->
    funciton test(){
    console.log(456);
    }
    test(); // 456
    但是光记住这两句话并不能解决所有的问题。
    看一下下面的
    console.log(a);
    function a(){
    }
    var a = 123;
    想一下打印的是什么?
    居然是
    ƒ a(){
    }
    再看看下面的更复杂的
    console.log(a);
    function a(a){
    var a = 234;
    var a = function(){
    }
    a();
    }
    var a = 123;
    这个打印出来是什么呢?
    ƒ a(a){
    var a = 234;
    var a = function(){
    }
    a();
    }
    这是为什么呢?
    下面来讲一下预编译: imply global 暗示全局变量:即任何变量。如果变量未经申明就赋值,此变量就为全局对象(window)所有。
    a = 123;如果 var a = b = 123;在函数里a就是函数内部变量,b则是全局变量。
    一切声明的全局变量,全是window的属性(window 就是全局的域):var a = 123;-----> window.a = 123;
    使用var声明的变量会自动被添加到最接近的环境中。
    预编译发生在函数执行的前一刻。
    预编译四部曲:
    1.创建AO对象/活动对象(activation object)(执行期上下文)
    2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
    3.将实参值和形参统一
    4.在函数体里面找到函数声明,值赋予函数体
    由此我们便知道上面的那两个例子打印的为什么是那样的。
    下面我们来看下更复杂的
    function fun(a) {
      console.log(a);
      var a = 123;
      console.log(a);
      function a() { }
      console.log(a);
      var b = function () { }
      console.log(b);
      function d() { }
    }
    fun(1);
    -->
    1.生成AO对象
    AO{
    }
    2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined
    AO{
      a: undefined;
      b:undefined;
    }
    3.将实参值和形参统一
    AO{
      a: 1;
      b:undefined;
    }
    4.在函数体里面找到函数声明,值赋予函数体
    AO{
      a: function a(){};
      b:undefined;
      d:function(){}
    }
    预编译结束
    函数执行 AO就像一个创库一样,函数执行期间里面的仓库也会变化
    AO{
      a: function a(){};
      b:undefined;
      d:function(){}
    }
    function fun(a) {
      console.log(a);      // ƒ a() { }
      var a = 123;
      console.log(a);      //123
      function a() { }
      console.log(a);      //123
      var b = function () { }
      console.log(b);      //ƒ () { }
      function d() { }
    }
    --------------------------
    function test(){
      console.log(b);
      if(a){
        var b = 100;
      }
      console.log(b);
      c = 234;
      console.log(c);
    }
    var a;
    test();
    a = 10;
    console.log(c);
    预编译
    全局GO
    GO{
    a:undefined;
    test:function test(){}
    }
    AO{
    b:undefined;
    }
    ------------
    执行函数
    GO{
    a:undefined;--->10
    test:function test(){}
    c:234
    }
    AO{
      b:undefined;
    }
    function test(){
      console.log(b); //undefined
      if(a){
        var b = 100;
      }
      console.log(b); //undefined
      c = 234;
      console.log(c); //234
    }
    var a;
    test();
    a = 10;
    console.log(c); //234
     
    作用域 作用域链
    function test(){};
    我们知道一个函数就像一个房子一样,这个房子形成单独的域,里面能看到外面的,外面的看不到里面的,我们可以把函数生成的空间叫做作用域那这个作用域到底是什么呢?
    这个作用域是因函数产生而产生的,每个对象都有属性和方法,函数(function)也是一种特殊的对象,函数可以有test.name test.prototype ...这些是可以访问的
    还有一些属性是不可以访问的隐式属性仅供JavaScript引擎处理比如[[scope]]:指的就是我们所说的作用域链,其中存储了执行期上下文的集合。
    为什么时集合呢?作用域链:是[[scope]]中所存储的执行期上下文的集合,这个集合呈现链式连接,我们把这种连接叫做作用域链。
    作用域链本质上是一个指向变量对象的指针列表,他只是引用,但不包含实际变量对象。
    test.[[scope]]这里面存的就是作用域。系统会根据内部的原理去定期调用scope。
    上面提到了执行期上下文(前面作用域也提到的AO对象就是这个):当函数执行的前一刻的时候,会创建一个称为执行期上下文的内部对象(AO activation object)。一个执行期上下文定义了一个函数执行时的环境
    函数每次执行时对应的上下文都是独一无二的 test(); test();一样的函数但是执行期上下文并不相同,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,他所产生的执行上下文会销毁。
    看一下下面的例子
    function a(){}
    var glob = 100;
    a();
     
    当a函数被定义 a.[[scope]]---> 0:GO{} 因为a函数在全局作用域里,所以他的第一位存的时GO
    当a执行执行 a.[[scope]]---> 0:AO{}
    1:GO{}
    ----------------------------------------------
    function a(){
      function b(){
        function c(){}
        c();
      }
      b();
    }
    a();
    a defined a.[[scope]]  ---> 0 : GO
    a doing a.[[scope]]   ---> 0 : a AO
                 1 : GO
    b defined b.[[scope]]  ---> 0 : a AO
                1 : GO
    b doing b.[[scope]]   ---> 0 : b AO
                1 : a AO
                2 : GO
    c defined c.[[scope]]  ---> 0 : b AO
                1 : a AO
                2 : GO
    b doing c.[[scope]]   ---> 0 : c AO
                1 : b AO
                2 : a AO
                 3 : GO
  • 相关阅读:
    Python中max()内置函数使用(list)
    linux命令总结mpstat命令
    linux命令总结vmstat命令
    linux命令总结free命令
    linux命令总结top命令
    linux命令总结之echo命令
    Nagios服务器端配置文件详解
    python之OS模块详解
    真正的inotify+rsync实时同步 彻底告别同步慢
    linux BASH shell下设置字体及背景颜色
  • 原文地址:https://www.cnblogs.com/jiaobaba/p/10420531.html
Copyright © 2011-2022 走看看