以一段代码为例
var obj = {
foo: function () { console.log(this.bar) },
bar: 1
};
var foo = obj.foo;
var bar = 2;
obj.foo() // 1
foo() // 2
这种差异的原因,就在于函数体内部使用了this关键字。this指的是函数运行时所在的环境。对于obj.foo()来说,foo运行在obj环境,所以this指向obj;对于foo()来说,foo运行在全局环境,所以this指向全局环境。所以,两者的运行结果不一样。
那为什么呢?
为什么obj.foo()就是在obj环境执行,而一旦var foo = obj.foo,foo()就变成在全局环境执行?所以我们需要先了解JS的存储结构。
通过JS存储结构得知this的必要性
存储结构有两种区分:
- 将对象赋值给变量
- 将函数赋值给变量
第一种:将对象赋值给变量
var obj = { foo: 5 };
JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。
后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。
原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。比如,foo属性的值保存在属性描述对象的value属性里面。
第二种:将函数赋值给变量
var obj = { foo: function () {} };
这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。
由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。this就出现了!它的设计目的就是在函数体内部,指代函数当前的运行环境。
综上所述,在开头那个例子中,obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。
this的绑定
this 是 JS 中的动态作用域机制, 具体来说有四种, 优先级有低到高分别如下:
- 默认的 this 绑定, 就是说 在一个函数中使用了
this, 但是没有为this绑定对象. 这种情况下, 非严格默认,this就是全局变量 Node 环境中的 global, 浏览器环境中的 window.(严格模式下,默认的this是undefined.) - 隐式绑定: 使用 obj.foo() 这样的语法来调用函数的时候, 函数 foo 中的
this绑定到 obj 对象. - 显示绑定: foo.call(obj, ...), foo.apply(obj,[...]), foo.bind(obj,...)
- 构造绑定: new foo() , 这种情况, 无论 foo 是否做了绑定, 都要创建一个新的对象, 然后 foo 中的
this引用这个对象.
可以认为:this 总是指向调用它所在的函数的那个对象。