美文网首页
JavaScript变量提升深入浅出

JavaScript变量提升深入浅出

作者: 3ef698de5362 | 来源:发表于2018-11-10 19:59 被阅读0次

    沉淀之前

    本人从事前端开发工作已经一年多了,在一家国企的信息技术部门工作,自己认为对前端技术有一定了解了,于是就想跳出围城看看外面的大千世界,找一家互联网公司开挑战一下自我。面试了头条,百度,美团这些一线互联网公司,面完之后才知道自己的是井底的蛙儿,不知道天地的广袤无垠。为了更好的督促自己,让自己的学习有迹可循,同时也能更好的和各位兄弟姐妹们交流就写一写博客。这是我第一次在简书上写,写的不好还请多批评指正。

    1.JavaScript基本概念回顾

    [1] 首先JavaScript是一种弱类型、动态的、解释型语言。
    弱类型:类型检查不严格,偏向于容忍隐式类型转换。
    强类型:类型检查严格,偏向于不容忍隐式类型转换。
    动态类型:运行的时候执行类型检查
    静态类型:编译的时候就知道每个变量的类型
    解释型:程序不需要编译,程序在运行的时候才翻译成机器语言,每执行一次都要翻译一次,因此效率比较低,但跨平台性好。
    编译型:程序在执行之前需要一个专门的翻译过程,把程序编译为机器语言的文件,运行时直接使用编译的结果就好了。
    编程语言:具有逻辑性和行为能力,这是主动的。
    脚本语言:不需要编译,可以直接使用,由解释器来负责解释。

    [2] 执行环境及作用域
    执行环境定义了变量或函数有权访问的的其他数据,决定了他们各自的行为。全局执行环境是最外围的一个执行环境,根据ECMAscript所实现的宿主环境不同,表示执行环境的对象也不一样。在WEB浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是window对象的属性和方法创建的。每个函数都有自己的执行环境。当执行流入一个函数时,函数的环境就会被推入一个栈中,而在函数执行后,栈将其弹出,把控制权返回给之前的执行环境。

    [3] JavaScript没有块级作用域
    上面说道JavaScript中只有全局作用域和局部作用域(函数作用域)。在其他类C的语言中,由花括号封闭的代码块都有自己的作用域。JavaScript没有块级作用域,所以if 和 for等语句定义的变量都是会存在于外部的执行环境中的。

    2.变量提升深入浅出

    什么是变量提升呢?
    变量提升包含两部分:变量提升和函数提升。
    变量提升是把变量提升提到函数顶部。需要说明的是,变量提升只是提升变量的声明,并不会把赋值也提升上来。
    函数提升是把整个函数提到前面去。函数声明形式能被提升,函数表达式不能。
    function foo(){console.log(1)} //函数声明
    var foo = function(){ console.log(1) } //函数表达式不能被提升,
    [1] JavaScript 代码解析原则
    首先JavaScript引擎在读取js代码时会进行两个步骤,第一个步骤是解释,第二个步骤是执行。解释就是先通篇扫描所有的Js代码,然后把所有声明提升到顶端,第二步是执行。第一步的的编译就包含变量提升。
    [2] 声明变量
    使用var声明的变量会自动添加到最近的环境中。在函数内部,最接近的环境就是函数内部的局部环境(with语句就不说了)。如果初始化变量时没有使用var声明,该变量就会自动被添加到全局环境中。示例如下:

    function add(num1, num2){
        var sum = num1+ num2;
        return sum;
    }
    var result = add(10, 20)
    alert(sum)
    

    编译时过程如下:这个主要是在函数内部的局部作用域的变量提升

    function add(num1, num2){
        var sum; //先提升
        sum = num1 + num2; //后赋值
        return sum
    }
    var result;
    result = add(10, 20)
    alert(sum)  //由于 sum是函数内的局部变量,函数外部访问不到,因此会导致错误。
    

    如果上述示例做如下修改

    function add(num1, num2){
        sum = num1 + num2  // 初始化变量时没有使用var声明,该变量就会自动添加到全局环境中。
        return sum;
    }
    var result = add(10, 20); // 30
    alert(sum); // 30  全局变量可以访问到
    

    [3] 查询标识符
    在查询某个环境中为了读取或者写入而引用一个标识符是,必须通过搜索来确定该标识符实际代表什么。当搜索过程从作用域的前端开始,向上逐级查询与给定匹配的标识符。如果在局部环境中找到了该标识符,搜索过程停止,变量就绪。如果在局部环境中没有找到该变量名,则继续沿作用域向上搜索。搜索过程将一直追溯到全局环境的变量对象。查询标识符过程示例如下:

    var color = "blue";                     var getColor = function(){
    function getColor(){                        return color;
        return color;           编译         }
    }                                        var color;
    alert(getColor());                       color = blue;   alert(getColor())
    

    搜索过程:return color -> getColor -> color -> window 结果为blue

    3. 变量提升实战

    [1] 牛刀小试
    1.函数声明的提升优先于变量声明的提升

     var scope = 'global';
    function f(){
        console.log(scope);
        var scope = 'local';
        console.log(scope);
    }
    

    编译后

    var f = function(){
          var scope;
          console.log(scope);  //在函数的局部作用域中,定义了没有赋值,输出undefined
          scope  = 'local';
          console.log(scope)  //赋值后,输出local
    }
    var scope;
    scope = 'global'
    

    (2)局部变量定义时,通过var声明和不用var声明直接使用带来的问题

    !function(){
        var t = 1;
        function hello(){
          t = 2;
          console.log(t);
      }
      hello(); // 2
      console.log(t); // 2
    }
    !function(){
      var t=1;
      function hello(){
        var t=2;
        console.log(t);
      }
      hello(); // 2
      console.log(t); // 1
    }();
    

    [2] 直击面试

    // 头条+百度面试题
    function Foo(){
        getName = function(){alert(1)}; 
        return this;
    }
    Foo.getName = function(){ alert(2); };
    Foo.property.getName = function(){ alert(3); };
    var getName = function() { alert(4); };
    function getName() { alert(5); };
    
    Foo.getName(); // ?
    getName(); // ?
    Foo().getName(); // ?
    getName(); // ?
    new Foo.getName(); // ?
    new Foo().getName(); // ?
    new new Foo().getName(); // ?
    

    这里牵扯到一些JavaScript面向对象设计原型链继承的问题,今天先不做介绍,会有一章专门介绍JavaScript面向对象的程序设计。js编译过程如下:

    var Foo = function(){
        getName = function(){ alert(1); }; //注意这里没有var声明会添加到全局
        return this
    }
    var getName = function(){ alert(5);};
    var getName;
    Foo.getName = function(){ alert(2); };
    Foo.property.getName = function(){ alert(3);};
    getName = function(){ alert(4); }
    
    Foo.getName(); // alert(2)
    getName(); // == this.getName()  所以是  alert(4)
    Foo().getName(); // alert(1)
    getName(); // alert(1)
    new Foo.getName(); // alert(2)
    new Foo().getName(); // alert(3)
    new new Foo().getName(); // ?
    

    解题开始:
    第一题,第二题看到解析后的变量提升就可以直接得出答案了
    (1)Foo.getName()

    Foo.getName = function(){ alert(2); };
    

    (2)getName()

    全局getName函数:getName = function() { alert(4) }
    

    (3)Foo().getName();

    Foo()这样调用就是把构造函数当成普通函数在调用
    里面的this指向了window对象
    所以里面的return this,就相当于把window对象返回了
    var res = Foo(); res就是接受的window对象
    getName = function(){alert(1);};
    这个代码是给全局getName重新赋值了一个function!所以调用的时候是1了
    Foo().getName() == window.getName() == function(){alert(1);};
    

    (4)getName();

    由于上一步代码已经将getName值改变,所以getName =  function (){alert(1)}
    

    (5)new Foo.getName();

    new 构造函数();
    下面的代码就是把Foo.getName整体当做了一个构造函数
    使用new关键字就做了三件事情
    var obj = {}; obj.__proto__ = Foo.getName.prototype; Foo.getName.call(obj);
    所以调用构造函数就是调用Foo.getName里面的alert语句就会被执行
    Foo.getName = function(){alert(2);}
    

    (6)new Foo().getName();

    上面的代码可以拆解成
    var obj = new Foo();
    obj.getName();
    由于obj中病没有getName属性,所以就要到原型Foo.prototype中去查找
    Foo.prototype.getName = function(){alert(3);};
    

    (7)new new Foo().getName()

    上面的代码可以拆解成
    var obj = new Foo();
    var func = obj.getName
    同(6)题由于obj中没有getName属性,所以他将在原型Foo.prototype中查找
    Foo.prototype.getName = function(){ alert(3); };
    所以 func = function(){ alert(3); };
    所以 new func()输出为 alert(3);
    

    小结

    变量提升现阶段总结到此结束,第一次写博客写的不好,还望大家多多批评指正。里面还有很多概念没有详细讲解例如函数闭包,原型链的继承会在后面的博客中继续总结,谢谢大家。

    相关文章

      网友评论

          本文标题:JavaScript变量提升深入浅出

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