javascript 预解析

  预解析:浏览器

 

  所谓的预解析就是:在当前做用域中,JavaScript代码执行以前,浏览器首先会默认的把全部带var和function声明的变量进行提早的声明或者定义。闭包

1.声明和定义函数

  var num = 24;
 这行简单的代码实际上是两个步骤:声明和定义。
  • 声明:var num; 告诉浏览器在全局做用域中有一个num变量了,若是一个变量只是声明了,可是没有赋值,默认值是undefined。
  • 定义:num = 12; 定义就是给变量进行赋值。

2.var声明的变量和function声明的函数在预解析的区别spa

  var声明的变量和function声明的函数在预解析的时候有区别,code

   var声明的变量在预解析的时候只是提早的声明,function声明的函数在预解析的时候会提早声明而且会同时定义。ip

   也就是说var声明的变量和function声明的函数的区别是在声明的同时有没同时进行定义。作用域

 

3. 预解析只发生在当前的做用域io

  程序最开始的时候,只对window下的变量和函数进行预解析,只有函数执行的时候才会对函数中的变量很函数进行预解析。console

    console.log(num);    //  undefined
    var num = 24;
    console.log(num);    //  20

    func(100 , 200); 
    function func(num1 , num2) {
     var total = num1 + num2;
     console.log(total);  //  300
    }

    第一次输出num的时候,因为预解析的缘由,只声明了尚未定义,因此会输出undefined;第二次输出num的时候,已经定义了,因此输出24。function

    因为函数的声明和定义是同时进行的,因此func()虽然是在func函数定义声明处以前调用的,可是依然能够正常的调用,会正常输出300。

 

   做用域链

  

  1.先理解如下三个概念:

  1. 函数里面的做用域成为私有做用域,window所在的做用域称为全局做用域;
  2. 在全局做用域下声明的变量是全局变量;
  3. 在“私有做用域中声明的变量”和“函数的形参”都是私有变量

  在私有做用域中,代码执行的时候,遇到了一个变量,首先须要肯定它是否为私有变量,若是是私有变量,那么和外面的任何东西都没有关系,若是不是私有的,则往当前做用域的上级做用域进行查找,若是上级做用域也没有则继续查找,一直查找到window为止,这就是做用域链。

 

  

  2.当函数执行的时候,首先会造成一个新的私有做用域,而后按照以下的步骤执行:

  1. 若是有形参,先给形参赋值;
  2. 进行私有做用域中的预解析;
  3. 私有做用域中的代码从上到下执行

  函数造成一个新的私有的做用域,保护了里面的私有变量不受外界的干扰(外面修改不了私有的,私有的也修改不了外面的),这也就是闭包的概念。

  

    console.log(total); 
    var total = 0;
    function func(num1, num2) {
     console.log(total); 
     var total = num1 + num2;
     console.log(total);
    }
    func(100 , 200);
    console.log(total);

  以上代码执行的时候,第一次输出total的时候会输出undefined(由于预解析),当执行func(100,200)的时候,会执行函数体里的内容,此时func函数会造成一个新的私有做用域,按照以前描述的步骤:

  1. 先给形参num一、num2赋值,分别为100、200;
  2. func中的代码进行预解析;
  3. 执行func中的代码

  由于在func函数内进行了预解析,因此func函数里面的total变量会被预解析,在函数内第一次输出total的时候,会输出undefined,接着为total赋值了,第二次输出total的时候就输出300。 由于函数体内有var声明的变量total,函数体内的输出total并非全局做用域中的total。

    最后一次输出total的时候,输出0,这里输出的是全局做用域中的total。

 

  console.log(total); 
  var total = 0;
  function func(num1, num2) {
   console.log(total); 
   total = num1 + num2;
   console.log(total);
  }
  func(100 , 200);
  console.log(total);

3.全局做用域下带var和不带var的区别

在全局做用域中声明变量带var能够进行预解析,因此在赋值的前面执行不会报错;声明变量的时候不带var的时候,不能进行预解析,因此在赋值的前面执行会报错。

  console.log(num1);    // undefined 
  var num1 = 12;    

  console.log(num2);    // 报错 : num2 is not defined.
  num2 = 12;      

至关于给window增长了一个num2的属性名,属性值是12;
  至关于给全局做用域增长了一个全局变量num1,可是不只如此,它也至关于给window增长了一个属性名num,属性值是12;

  num2 = 12;var num1 = 12;

问题:在私有做用域中出现一个变量,不是私有的,则往上级做用域进行查找,上级没有则继续向上查找,一直找到window为止,若是window也没有呢?

        获取值:console.log(total); --> 报错 Uncaught ReferenceError: total is not defined.

     设置值:total= 100; --> 至关于给window增长了一个属性名total,属性值是100

 

  function fn() {
   // console.log(total); // Uncaught ReferenceError: total is not defined
   total = 100;
  }
  fn();
  console.log(total);

 

  注意:JS中,若是在不进行任何特殊处理的状况下,上面的代码报错,下面的代码都再也不执行了!

 

预解析中的一些变态机制:

  1 无论条件是否成立,都要把带var的进行提早的声明

    if (!('num' in window)) { 
     var num = 12;
    }
    console.log(num); // undefined

   JavaScript进行预解析的时候,会忽略全部if条件,由于在ES6以前并无块级做用域的概念。
以上示例会先将num预解析,而预解析会将该变量添加到window中,做为window的一个属性。
那么 'num' in window 就返回true,取反以后为false,这时代码执行不会进入if块里面,num也就没有被赋值,
最后输出为undefined。
console.log(num)

 

  2 只预解析“=”左边的,右边的是值,不参与预解析  

    fn(); // -> undefined();  // Uncaught TypeError: fn is not a function
    var fn = function () {
     console.log('ok');
    }

    fn(); -> 'ok'
    function fn() {
     console.log('ok');
    }
    fn(); -> 'ok'
  建议:声明变量的时候尽可能使用var fn = ...的方式。

 

  3 自执行函数:定义和执行一块儿完成 

  (function (num) {
    console.log(num);
  })(100);

 

  自执行函数定义的那个function在全局做用域下不进行预解析,当代码执行到这个位置的时候,定义和执行一块儿完成了。

 

    补充:其余定义自执行函数的方式

 

      ~ function (num) {}(100) 
      + function (num) {}(100) 
      - function (num) {}(100) 
      ! function (num) {}(100)

 

  4 return下的代码依然会进行预解析

    function fn() {        
     console.log(num); // -> undefined
     return function () {    

     };        
     var num = 100;     
    }         
    fn();

  函数体中return下面的代码,虽然再也不执行了,可是须要进行预解析,return中的代码,都是咱们的返回值,因此不进行预解析。

 

  5 名字已经声明过了,不须要从新的声明,可是须要从新的赋值

    var fn = 13;          
    function fn() {         
     console.log('ok');        
    }             
    fn(); // Uncaught TypeError: fn is not a function    


 

  eg: 经典题目 

    fn(); // -> 2            
    function fn() {console.log(1);}       
    fn(); // -> 2            
    var fn = 10; // -> fn = 10        
    fn(); // -> 10() Uncaught TypeError: fn is not a function       
    function fn() {console.log(2);}       
    fn();
欢迎留言,欢迎踩踩踩!
相关文章
相关标签/搜索