令人迷惑的JS作用域

关于JS作用域,必须知道的几条内容

  • JavaScript的变量作用域是基于其独有的作用域链的。
  • 在函数体内部,同名的局部变量比全局变量的优先级高。
  • JavaScript只有函数作用域,没有块级作用域。
  • 函数中声明的变量在整个函数内部都有定义。
  • 未使用var定义的变量都是全局变量。
  • 全局变量都是window对象的属性。
  • JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。

关于JS作用域链的深入理解,请见这里


例子:第一个系列

1.

function bar(){
    return foo;
}
console.log(typeof bar());

result : error(foo is not defined)

这个结果显而易见,无需解释


2.

function bar(){
    return foo;
    var foo = 1;
}
console.log(typeof bar());

result : undefined

这个结果就有点让人困惑了。按照普通思路,在return之前并没有定义foo变量,按道理说应该产生error(foo is not defined)的结果。或者退一步讲,也应该产生number这样的结果。可是为什么会产生undefined这种匪夷所思的结果呢。原因如下:在JavaScript函数中声明的变量在整个函数中都有定义!

上述例子其实可以等效为:

function bar(){
    var foo;
    return foo;
    foo = 1;
}
console.log(typeof bar());

这样的话就容易理解了,return的时候,在bar()函数内部整体,已经有关于foo的定义了。只是还没有进行初始化而已。


3.

function bar(){
    return foo;
    foo = 1;
}
console.log(typeof bar());

result : error(foo is not defined)
而反过来:

function bar(){
    foo = 1;
    return foo;
}
console.log(typeof bar());

result : number

在这里foo = 1作为表达式而非定义式。所以,一、没有进入预编译过程,二、按照执行顺序,这段代码根本没有执行,自然就找不到foo变量了。


4.

function bar(){
    return foo;
    function foo(){};
}
console.log(typeof bar());

result : function
再看这个:

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

result : undefined

在JS中, 是有预编译的过程的(预编译过程、执行过程), JS在执行每一段JS代码之前, 都会首先处理var关键字和function定义式(函数定义式和函数表达式)。
在调用函数执行之前, 会首先创建一个活动对象, 然后搜寻这个函数中的局部变量定义,和函数定义, 将变量名和函数名都做为这个活动对象的同名属性, 对于局部变量定义,变量的值会在真正执行的时候才计算, 此时只是简单的赋为undefined。

例子:第二个系列

1.

var foo = 1;
function bar(){foo = 10;}
bar();
console.log(foo);

result : 10


2.

var foo = 1;
function bar(){foo = 10;return;}
bar();
console.log(foo);

result : 10


3.

var foo = 1;
function bar(){
    foo = 10;
    return;
    var foo = 5
}
bar();
console.log(foo);

result : 1


4.

var foo = 1;
function bar(){
    foo = 10;
    return; 
    function foo(){}
}
bar();
console.log(foo);

result : 1

命名函数的赋值表达式

var foo = function bar(){
    bar(); // 正常运行
}
bar(); // 出现错误:ReferenceError

一个有趣的题目

var name = "The Window";   
var object = {   
  name : "My Object",   
  getNameFunc : function(){   
    return function(){   
      return this.name;   
     };   
  }   
};   
alert(object.getNameFunc()());  // result : The Window

后面再加个括号是匿名函数的自调用,直接执行了object.getNameFunc()返回的那个函数。而这个函数在window域下。

var name = "The Window";   
var object = {   
  name : "My Object",   
  getNameFunc : function(){   
      var that = this;
    return function(){   
      return that.name;   
     };   
  }   
};   
alert(object.getNameFunc()());  // result : My Object

第一种情况this的值:在执行object.getNameFunc()时候是object这个对象。在执行object.getNameFunc()()的时候已经变成了window。
第二种情况:在执行object.getNameFunc()时候是that = this = object这个对象。在执行object.getNameFunc()()的时候this = window, that = object。