• 关于JS变量提升的一些坑


    1 function log(str) {
    2    // 本篇文章所有的打印都将调用此方法
    3    console.log(str);
    4 }

    函数声明和变量声明总是会被解释器悄悄地被“提升”到方法体的最顶部

    变量声明、命名、提升


    在JS中, 变量有4种基本方式进入作用域:

    • 语言内置: 所有的作用域里都有this和arguments;(需要注意的是arguments在全局作用域是不可见的)
    • 形式参数: 函数的形式参数会作为函数体作用域的一部分;
    • 函数声明: 像这种形式: function foo() {};
    • 变量声明: 像这样: var foo;

    变量提升

    function test1() {
          a = 5;
          log(a); 
          log(window.a); 
          var a = 10;
          log(a); 
        }
        test1();

    依次会输出 5 、undefined 、10   因为在解析时候是等价于

    1 var a;
    2 a=5;
    3 log(a);
    4 log(window.a);
    5 a=10;
    6 log(a);

     接着看另外一个例子:

     1 function test2() {
     2    var a = 1;
     3    var b = 2;
     4    var c = 3;
     5 }
     6 /*test2中的语句,是这样被执行的  这个时候就把变量提升了
     7     
    8 function test2(){
    9 var a,b,c; 10 var a = 1; 11 var b = 2; 12 var c = 3; 13 } 14 */

    只有函数级作用域,if语句不会有:test3():

    function test3(){
    var a = 1;
    log(a); // 1 
    if (true) {
    var a = 2;
    log(a); //2 
    }
    log(a); // 2 
    }

     

    函数的提升


    我们写JS的时候,通常会有两种写法:

    • 函数表达式 var fn=function fn(){}
    • 函数声明方式 function fn(){}

    我们需要重点注意的是,只有函数声明形式才能被提升。
    变量赋值并没有被提升,只是声明被提升了。但是,函数的声明有点不一样,函数体也会一同被提升

     1 function test3() {
     2    fn();
     3    function fn() {
     4      log("我来自fn");
     5    }
     6  }
     7  test3();
     8  function test4() {
     9    fn(); // fn is not a function
    10    var fn = function fn() {
    11      alert("我来自 fn  test4");
    12    }
    13  }
    14  test4();

    函数表达式需要注意的

    • 在function内部,fn完全等于fn1
    • 在function外面,fn1则是 not defined
     1 function test5() {
     2       var fn = function fn1() {
     3         log(fn === fn1); // true
     4         log(fn == fn1); // true
     5       }
     6       fn();
     7       log(fn === fn1); // fn1 is not defined
     8       log(fn == fn1);  // fn1 is not defined
     9     }
    10     test5();

    !兼容
    // b();
    // var a = function b() {alert('this is b')};
    // 则ie下是可以执行b的. 说明不同浏览器在处理函数表达式细节上是有差别的.

    补充一点函数表达式

    定义里面的指定的函数名是不是被提升的

     1 function text7() {
     2   a(); // TypeError "a is not a function" 
     3   b();
     4   c(); // TypeError "c is not a function" 
     5   d(); // ReferenceError "d is not defined"
     6 
     7   var a = function() {};    // a指向匿名函数 
     8   function b() {};          // 函数声明 
     9   var c = function d() {};  // 命名函数,只有c被提升,d不会被提升。
    10 
    11   a();
    13   b();
    14   c();
    15   d(); // ReferenceError "d is not defined"
    16 }
    17 text7();

    大家先看下面一段代码test6,思考一下会打印什么?

     1 function text6() {
     2    var a = 1;
     3    function b() {
     4      a = 10;
     5       return;
     6       function a() {}
     7     }
     8     b();
     9     log(a);         //
    10 }
    11 text6();

    ||

    ||

    ||

    || 输出在下面

    ||

    ||

    ||

    ||

    ||

    ||

    what? 什么鬼?为什么是1?
    这里需要注意的是,在function b()中,
    var = a // function 类型的
    a=10; // 重新把10复制给a,  此时的a是function b()中的内部变量
    return;
    function a() {} // 不会被执行

    所以,外面输出的a 依旧是最开始定义的全局变量

    函数的声明比变量的声明的优先级要高

     1 function text6() {
     2   function a() {}
     3   var a;
     4   log(a);                //打印出a的函数体
     5 
     6   var b;
     7   function b() {}
     8   log(b);                 //打印出b的函数体 
     9 
    10   // !注意看,一旦变量被赋值后,将会输出变量
    11   var c = 12
    12   function c() {}
    13   log(c);                 //12
    14   
    15   function d() {}
    16   var d = 12
    17   log(d);                //12
    18 }
    19 text6();

    变量解析的顺序


    一般情况下,会按照最开始说的四种方式依次解析

    • 语言内置:
    • 形式参数: 
    • 函数声明: 
    • 变量声明: 

     也有例外:

    • 内置的名称arguments表现得很奇怪,看起来应该是声明在形参之后,但是却在声明之前。这是说,如果形参里面有arguments,它会比内置的那个优先级高。所以尽可能不要在形参里面使用arguments;
    • 在任何地方定义this变量都会出语法错误
    • 如果多个形式参数拥有相同的名称,最后的那个优先级高,即便是实际运行的时候它的值是undefined;

    CAO!这么多坑,以后肿么写代码?


    用var定义变量。对于一个名称,在一个作用域里面永远只有一次var声明。这样就不会遇到作用域和变量提升问题。

    ECMAScript参考文档关于作用域和变量提升的部分:
        如果变量在函数体类声明,则它是函数作用域。否则,它是全局作用域(作为global的属性)。变量将会在执行进入作用域的时候被创建。块(比如if(){})不会定义新的作用域,只有函数声明和全局性质的代码(单个JS文件)才会创造新的作用域。变量在创建的时候会被初始化为undefined。如果变量声明语句里面带有赋值操作,则赋值操作只有被执行到的时候才会发生,而不是创建的时候。

    最后,

    由于时间仓促,demo有很多不足之处,多谅解。

  • 相关阅读:
    PAT1137
    Ubuntu小工具
    C文件的格式化工具(astyle)
    linux批量替换文本字符串
    scp & cat远程文件操作
    上传附件中英文混合的文件名上传
    membership DB生成 & dll 强命名 & 证书生成
    机器Coding For WinForm
    机器Coding For WPF
    C# cmd bcp 导出数据
  • 原文地址:https://www.cnblogs.com/luqin/p/5164132.html
Copyright © 2020-2023  润新知