zoukankan      html  css  js  c++  java
  • javascript 预解析

      预解析:

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

    1.声明和定义

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

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

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

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

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

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

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

        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。

        由于函数的声明和定义是同时进行的,所以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. 先给形参num1、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;      

      num2 = 12; 相当于给window增加了一个num2的属性名,属性值是12;
      var num1 = 12; 相当于给全局作用域增加了一个全局变量num1,但是不仅如此,它也相当于给window增加了一个属性名num,属性值是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也就没有被赋值,
    最后console.log(num)输出为undefined。

      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();
    欢迎留言,欢迎踩踩踩!
    
    
  • 相关阅读:
    并发编程 19—— 显式的Conditon 对象
    JVM实用参数——新生代垃圾回收
    设计模式 8 —— 适配器和外观模式
    并发编程 18—— 使用内置条件队列实现简单的有界缓存
    并发编程 17—— Lock
    Spring 事务管理 01 ——
    并发编程 16—— 线程池 之 原理二
    并发编程 15—— 线程池 之 原理一
    并发编程 14—— 线程池 之 整体架构
    java.util.logging.Logger 使用详解
  • 原文地址:https://www.cnblogs.com/zccblog/p/7574883.html
Copyright © 2011-2022 走看看