zoukankan      html  css  js  c++  java
  • 关于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有很多不足之处,多谅解。

  • 相关阅读:
    WHERE col1=val1 AND col2=val2;index exists on col1 and col2, the appropriate rows can be fetched directly
    MySQL 交集 实现方法
    MBProgressHUD的使用
    Xcode4 使用 Organizer 分析 Crash logs(转)
    SimpleXML 使用详细例子
    PHP的XML Parser(转)
    iPhone,iPhone4,iPad程序启动画面的总结 (转)
    Pop3得到的Email 信件格式介绍
    yii总结
    隐藏Tabbar的一些方法
  • 原文地址:https://www.cnblogs.com/luqin/p/5164132.html
Copyright © 2011-2022 走看看