JS中的this原理(一) 认识this

50 阅读2分钟

以一段代码为例

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.foofoo()就变成在全局环境执行?所以我们需要先了解JS的存储结构。

通过JS存储结构得知this的必要性

存储结构有两种区分:

  1. 将对象赋值给变量
  2. 将函数赋值给变量

第一种:将对象赋值给变量

var obj = { foo:  5 };

JavaScript 引擎会先在内存里面,生成一个对象{ foo: 5 },然后把这个对象的内存地址赋值给变量obj。 后面如果要读取obj.foo,引擎先从obj拿到内存地址,然后再从该地址读出原始的对象,返回它的foo属性。

原始的对象以字典结构保存,每一个属性名都对应一个属性描述对象。比如,foo属性的值保存在属性描述对象的value属性里面。

this指向1.png

第二种:将函数赋值给变量

var obj = { foo: function () {} };

这时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。

this指向2.png

由于函数是一个单独的值,所以它可以在不同的环境(上下文)执行。所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。this就出现了!它的设计目的就是在函数体内部,指代函数当前的运行环境。

综上所述,在开头那个例子中,obj.foo()是通过obj找到foo,所以就是在obj环境执行。一旦var foo = obj.foo,变量foo就直接指向函数本身,所以foo()就变成在全局环境执行。

this的绑定

this 是 JS 中的动态作用域机制, 具体来说有四种, 优先级有低到高分别如下:

  1. 默认的 this 绑定, 就是说 在一个函数中使用了this, 但是没有为this绑定对象. 这种情况下, 非严格默认, this就是全局变量 Node 环境中的 global, 浏览器环境中的 window.(严格模式下,默认的this是undefined.)
  2. 隐式绑定: 使用 obj.foo() 这样的语法来调用函数的时候, 函数 foo 中的this绑定到 obj 对象.
  3. 显示绑定: foo.call(obj, ...), foo.apply(obj,[...]), foo.bind(obj,...)
  4. 构造绑定: new foo() , 这种情况, 无论 foo 是否做了绑定, 都要创建一个新的对象, 然后 foo 中的 this 引用这个对象.

可以认为:this 总是指向调用它所在的函数的那个对象。