下面的两种写法,结果有何不同?
虽然obj.foo
和foo
指向同一个函数,但是执行结果可能不同。例如
产生这种差异的原因,在于函数内部使用了this
关键字。根据书面解释这种情况:this
指的是函数运行时的环境。也就是对于obj.foo()
来说,foo
运行在obj
环境;foo
运行在全局环境
,所以两者运行结果不一样。
思考
- 函数的运行环境到底怎么决定?
- var foo = obj.foo 为什么赋值后foo就成了全局执行环境?
内存的数据结构
var obj = { foo : 5 };
上面的代码将一个对象赋值给变量obj
。JavaScript会先内存里面生成一个对象{foo:5}
,然后再把内存地址赋值给变量obj
。也就是obj
是一个地址。
对象的的每一个属性,实际是以字典结构的形式保存的,类似下图,
对象的每个属性都包含描述属性。其中value
属性保存对应的值。
思考
如果属性的值是函数或者其他引用类型。描述属性中的value
应该是什么,例如
var obj = { foo : function(){} }
这时,JavaScript引擎会把函数单独保存在内存中,然后把地址赋值给value
属性
由于函数是一个单独的值,所以可以在不同的环境(上下文)中执行。
环境变量
JavaScript允许在函数体内部引用当前环境的其他变量,如
var f = function () {
console.log(x);
};
上面代码中,函数体里面使用了变量x
。该变量由运行环境提供。
现在问题就来了,由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this
就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。
var f = function () {
console.log(this.x);
}
上面代码中,函数体里面的this.x
就是指当前运行环境的x
。
var f = function () {
console.log(this.x);
}
var x = 1;
var obj = {
f: f,
x: 2,
};
// 单独执行
f() // 1
// obj 环境执行
obj.f() // 2
上面代码中,函数f
在全局环境执行,this.x
指向全局环境的x
。
在obj
环境执行,this.x
指向obj.x
。
回到本文开头提出的问题,obj.foo()
是通过obj
找到foo
,所以就是在obj
环境执行。一旦var foo = obj.foo
,变量foo
就直接指向函数本身,所以foo()
就变成在全局环境执行。