深入学习JavaScript之提升

 或许你在编写JavaScript时,会注意到这么一个现象,声明的出现与其作用域内的操作位置有微妙的关系,

 有人认为JavaScript引擎执行代码是从上到下一行一行执行的。实际上这不全对

例如:

   a=2;
       var a;
       console.log(a);

   如果按照JavaScript引擎是一行一行代码解释的话,先赋值再声明再输出,因为var a在a=2后面,理所当然认为a被重新赋值了,那么输出结果是undefined,但是很遗憾并不是,输出结果为2。

我们将它们之间的位置调换下会得到:

 console.log(a);
       var a;
          a=2;

  如果按照对之前代码进行的分析,那么此处会输出  "2"  或者这样思考,a未定义前就使用,那么此处会输出"ReferenceError"。     但是结果却是  "undefined"  ,为什么???

  这其实就是“先有鸡还是先有蛋的问题”即先声明在使用还是先使用在声明的问题

1.2编译器

  编译器在JavaScript中的作用是进行词法分析/语法分析以及生成代码。编译的一部分工作----在词法分析中产生词法域,这时候会得到词法域以及各类声明。

    包括函数和变量在内的所有声明在任何代码被执行之前首先被处理

  当看到"var a=2"时你以为这会是一句声明语句,其实不然,这在JavaScript看来是两种声明,一种是声明语句"var a;",另一种是赋值语句"a=2;",声明语句在编译时就已经完成,而赋值语句则要在原地等待执行。

     之前我们的代码它的执行过程如下

  //先编译再执行
  a=2;     //等待执行
   var a;    //编译时先执行此声明
   console.log(a);   

  如上所示,因为编译器首先是编译此代码随后再执行,所以先执行  var a  ;在执行  a=2 ;最后执行  console.log(a);所以输出为2。

  如果按照从上到下的执行顺序,那么上面代码可变为

  var a;   //先执行声明语句
   a=2;
   console.log(a);

那么,之前讲的这个代码片段

   console.log(a);
   var  a=2;

可变为

var a;

console.log(a);

   a=2;        

  在这里JavaScript将  var a=2  分解为  var a  和   a=2。var a  首先被执行,所以上升到第一位。

--------------------------------在上面例子中,var a  声明语句总是被提升到第一位,所以叫提升

   提升过程有两点需要注意的

①函数声明也是可以提升的,

   例如

   foo();         //foo()函数能够正常运行
   function foo(){    //函数声明发生了提升

    console.log(a);        //ReferenceError

         var a=2;

   }

  函数声明提升,所以第一行调用可以正常执行。

②提升只会提升声明,而不会提升逻辑(赋值等操作)

  如上面的  var a  被提升(是在foo(...)函数中的最上方,而不是整个程序),剩下  a=2  在原地。

  这时候我们稍加对上面程序进行修改,你就会发现问题

   foo();         //TypeError而不是ReferenceError
   var foo=function bar(){    //函数声明发生了提升

    console.log(a);        //ReferenceError

         var a=2;

   }

  var foo=function bar(){...}   中同样被分成两部分   var foo  以及  foo=function bar(){....},声明被提升到最顶部,赋值操作没有提升。

因此foo()调用没有发生ReferenceError错误。但是foo()并没有被赋值(如果它是一个函数声明而不是一个函数表达式,那么它就必须要赋值,此处它没有赋值,所以默认的值为undefined),foo()由于对undefined进行函数调用而导致非法操作,抛出TypeError。

  

1.3函数优先

  函数声明和变量声明假设都在同一个作用域中,那么是函数优先还是变量声明优先呢??

   我们看一个例子

 foo();    //1  证明是函数声明优先
      var foo;
      function foo(){
          console.log(1);
      }
      foo=function () {
          console.log(2);
      }

  在以上代码中,我们可以看到,输出结果为1,从代码执行上来看,如果是变量声明优先的话,那么foo()会输出2,如果是函数优先那么会输出1。

  这个代码可以化成这样的形式:

function foo(){
    
    console.log(1);
  
  };

   foo();
 
   foo=function(){

     console.log(2);     

     };

  注意,var foo  尽管出现在  function  foo(...)声明之前,但是它是重复的声明(因此被忽略了),因为函数声明会被提升到普通变量之前。---------------------这告诉我们,在一个作用域中重复定义是糟糕的,它可能会出现各种各样的问题!!!!

总结:

    作用域中的声明将会提升到顶部的过程------我们称之为提升。

     提升分为两种,一种是变量提升,一种是函数提升,当两者同在一个作用域中时,函数声明先提升,随后才是变量提升-------两者都是提升到当前作用域的顶部。

    提升的过程中只会提升声明,而不会提升操作语句,例   "var    a=2"   将会被分成两部分:" var   a"       "  a=2  ",当发生提升时只有  " var  a"被提升到当前作用域的顶部。  "a=2"将在原地等待执行。

    避免在同一个作用域中重复定义,特别是当普通的var声明和函数声明在一起时!!!

     
    

猜你喜欢

转载自blog.csdn.net/qq_41889956/article/details/83148592