• 解读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

  • 相关阅读:
    c++ 面试注意的问题
    非root用户 gcc安装
    爱挑剔的acm程序员 acmer
    Add Digits
    Rectangle Area
    Contains Duplicate III 下标范围<=k 值范围<=t
    ruby第一次实践 ”hello world“
    判断一个 int 向量里是否有相同的数 并且距离在k之内 (2)
    判断一个 int 向量里是否有相同的数(1)
    字符串同构
  • 原文地址:https://www.cnblogs.com/lqqchangeload/p/10601985.html
Copyright © 2020-2023  润新知