zoukankan      html  css  js  c++  java
  • 解读JavaScript中的Hoisting机制(js变量声明提升机制)

    hoisting机制:javascript的变量声明具有hoisting机制,JavaScript引擎在执行的时候,会把所有变量的声明都提升到当前作用域的最前面。

    知识点一:javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构

    知识点二:变量声明宣称一个名字的存在,变量定义则为这个名字分配存储空间,而变量初始化则是为名字分配的存储空间赋初值

    知识点三:javascript中一个名字(name)以四种方式进入作用域(scope),其优先级顺序如下:
                   1、语言内置:所有的作用域中都有 this 和 arguments 关键字
                   2、形式参数:函数的参数在函数作用域中都是有效的
                   3、函数声明:形如function foo() {}
                   4、变量声明:形如var bar;

    知识点四:1、函数参数是原始类型的值(数值、字符串、布尔值),传递方式是传值传递,即在函数体内修改参数值,不会影响到函数外部。

                      2、函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递,传入的是原始值的地址,因此在函数内部修改参数,将会影响到原始值。
                      注意,如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值!
     
    一、hoisting机制含义解释简单实例
      1、我是变量声明,我会被提升在作用域顶端!
        eg: var a;
      2、我是变量定义,我的声明部分会被提升,赋值部分不会被提升!  
        eg: var a = 'hello';
      3、我是函数定义,或者叫我函数表达式。其实我就是变量定义,只不过恰好被赋值的类型是函数,所以也只提升变量名不提升函数值
        eg: var a = function (){
            console.log('hello');
          };
      4、我是函数声明,所以我全部被提升了,包括函数名和函数体。另外,我的优先级比变量声明要高,名字和我相同的变量声明会被忽略!
        eg: function a (){
            console.log('hello');
          };
     
    二、变量声明提升实例加解说(帮助更好理解)
      1、eg: var v = "hello";
         (function(){
            console.log(v);
            var v = "world";
         })();
       答案:undefined
       解析:(1)、function作用域里的变量v遮盖了上层作用域变量v。代码做少些变动:
           var v = "hello";
            if(true){
                              console.log(v);
                              var v = "world";
                         } 
          输出结果为”hello”,说明javascript是没有块级作用域的。函数是JavaScript中唯一拥有自身作用域的结构。(证明知识点一)
          (2)、在function作用域内,变量v的声明被提升了。所以最初的代码相当于:
            var v = "hello";
                               (function(){
                                     var v; //declaration hoisting
                                     console.log(v);
                                     v = "world";
                               })(); 
      2、eg:(function(){
                var a = "1";
                          var f = function(){};
                          var b = "2";
                          var c = "3";
                    })();
     
      解析:(1)、变量a,f,b,c的声明会被提升到函数作用域的最前面,类似如下:

                            (function(){
                                      var a,f,b,c;
                                       a = "1";
                                       f = function(){};
                                       b = "2";
                                       c = "3";
                           })();

            (2)、请注意函数表达式并没有被提升,这也是函数表达式与函数声明的区别。进一步看二者的区别:

               (function(){

               //var f1,function f2(){}; //hoisting,被隐式提升的声明

                                          f1(); //ReferenceError: f1 is not defined

                                         f2();

                                         var f1 = function(){};

                                         function f2(){console.log('111')}

                               })();  

         上面代码中函数声明f2被提升,所以在前面调用f2是没问题的。虽然变量f1也被提升,但f1提升后的值为undefined,其真正的初始值是在执行到函数表达式处被赋予的。所以只有声明                  是被提升的。

    二、变量声明提升优先级实例加解说(帮助更好理解)(证明知识点三)

      1、eg:(function(){

            var foo;

            console.log(typeof foo); //function

            function foo(){}

            foo = "foo";

            console.log(typeof foo); //string

        })();  

        解析:(1)、一个变量的名字与函数的名字相同,那么函数的名字会覆盖变量的名字,无论其在代码中的顺序如何。但名字的初始化却是按其在代码中书写的顺序进行的,不受以上                           优 先级的影响。如果形式参数中有多个同名变量,那么最后一个同名参数会覆盖其他同名参数,即使最后一个同名参数并没有定义。知识点三名字解析优先级存在例外,比如                              可以覆盖语言内置的名字arguments。

      2、命名函数表达式:可以像函数声明一样为函数表达式指定一个名字,但这并不会使函数表达式成为函数声明。命名函数表达式的名字不会进入名字空间,也不会被提升。

          eg:(function(){

            f();//TypeError: f is not a function

            foo();//ReferenceError: foo is not defined

            var f = function foo(){console.log(typeof foo);};

            f();//function

            foo();//ReferenceError: foo is not defined

           })(); 

        解析:命名函数表达式的名字只在该函数的作用域内部有效。

      3、eg:var myval = "my global var";

         (function() {

          console.log(myval); // "my global var"

         })(); 

        解析:将上面的例子稍作修改:

            var myval = "my global var";

            (function() {

               console.log(myval); //"undefined"

               var myval = "my local var";

            })();

            执行结果是输出了一个 undefined,出现这个结果的原因就是变量的声明被提升了,以上代码等同如下:

            var myval = "my global var";

            (function() {

              var myval;

               console.log(myval); //"undefined"

               myval = "my local var";

            })();

            被提升的仅仅是变量的声明部分,并没有立即初始化,所以会输出 undefined。

      4、例子3的这种提升机制,不仅仅表现于在普通的变量,同时也表现在函数上。eg:

        (function() {

           fun(); // Uncaught TypeError: undefined is not a function

           var fun = function() {

           console.log("Hello!");

           }

        })();

        解析:上面的例子等价于:

           (function() {

              var fun;

              fun(); // Uncaught TypeError: undefined is not a function

              fun = function() {

                 console.log("Hello!");

               }

           })();

          因为函数的声明同样被提升而没有立即初始化,所以会出错。

          当然,这种定义函数的方式称之为“函数表达式”,会有提升机制,如果是如下的这种“函数声明”方式,则完全没有提升机制方面的问题:

          (function() {

             fun();

             function fun() {

             console.log("Hello!"); //"Hello!"

             }

          })();

          这也是函数声明与函数表达式的主要区别。

    三、函数传参变量提升问题(证明知识点四)

      1、eg:var foo=1;

        (function (foo) {

           console.log(foo);//1

           foo=3;

           var foo=2;

           console.log(foo);//2

        })(foo);//1

        console.log(foo);

        解析:函数参数是原始类型的值(数值、字符串、布尔值),传递方式是传值传递,即在函数体内修改参数值,不会影响到函数外部。  

      2、eg:var foo={n:1};

         (function (foo) {

            console.log(foo.n);//1

            foo.n=3;

            var foo={n:2};

            console.log(foo.n);//2

         })(foo);

        console.log(foo.n);//3

        解析:函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递,传入的是原始值的地址,因此在函数内部修改参数,将会影响到原始值。

       3、eg:var foo={n:1};

         (function (foo) {

            console.log(foo.n);//1

            foo = {n:3};

            var foo={n:2};

            console.log(foo.n);//2

         })(foo);

        console.log(foo.n);//1

        解析:如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值!

        4、eg:var foo = {n:1};

           (function(foo){ //形参foo同实参foo一样指向同一片内存空间,这个空间里的n的值为1
              var foo; //优先级低于形参,无效。
                              console.log(foo);
                   console.log(foo.n); //输出1
            foo.n = 3; //形参与实参foo指向的内存空间里的n的值被改为3
            foo = {n:2}; //形参foo指向了新的内存空间,里面n的值为2.
            console.log(foo.n); //输出新的内存空间的n的值
          })(foo);
          console.log(foo.n); //实参foo的指向还是原来的内存空间,里面的n的值为3.

          答案:{n:1} ,1,2,3

  • 相关阅读:
    AngularJS Insert Update Delete Using PHP MySQL
    Simple task manager application using AngularJS PHP MySQL
    AngularJS MySQL and Bootstrap Shopping List Tutorial
    Starting out with Node.js and AngularJS
    AngularJS CRUD Example with PHP, MySQL and Material Design
    How to install KVM on Fedora 22
    Fake_AP模式下的Easy-Creds浅析
    河南公务员写古文辞职信
    AI
    政协委员:最大愿望是让小学生步行上学
  • 原文地址:https://www.cnblogs.com/lqqchangeload/p/10601985.html
Copyright © 2011-2022 走看看