关于JS变量提高的一些坑

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

 

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

变量声明、命名、提高函数


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

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

 

变量提高spa

 

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

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

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

 接着看另一个例子:blog

 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():ip

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(){}

咱们须要重点注意的是,只有函数声明形式才能被提高。
变量赋值并无被提高,只是声明被提高了。可是,函数的声明有点不同,函数体也会一同被提高io

 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有不少不足之处,多谅解。

相关文章
相关标签/搜索