理解 this

135 阅读2分钟

对 this 的误解

认为 this 指向的是函数自身

在 JavaScript 中,所有函数都是对象。既然是对象,那么就可以有属性来存储其值。所以就会存在认为 this 指向函数自身,比如下面的例子:

function foo(value) {
  console.log(`Foo: ${value}`)
  
  this.count++
}

foo.count = 0

foo(1)

应该会有很多人认为此时 count 值为 1;其实不是,它的值为 NaN。为什么呢?因为此时 this 指向的是全局对象,在全局作用域创建变量 count,默认值为 undefined,执行操作后其值为 NaN。因此,this 并不是指向函数自身的。

认为 this 指向函数的作用域

注意:this 在任何情况下都不指向函数的词法作用域。

从 ECMAScript 角度理解 this(Reference)

this 是在函数运行时进行绑定的,而不是在编写时绑定;this 的绑定与函数声明位置没有关系,而与函数调用方式有关系。

ECMAScript 类型

ecmascript_type.png

如何确定 this

this.png

备注:对于确实是否是 Reference 类型,需要查看 ECMAScript 规范确认。

this 绑定规则

1、默认绑定

函数调用时不带任何修饰的引用,非严格模式下为全局对象(window),严格模式下为 undefined。

function foo() {
  console.log(this.a)
}

var a = 2

2、隐式绑定

通过引用(对象)调用函数,this 绑定到该对象,不过需要注意隐式丢失 this(原始函数赋值给一个变量)。

// 隐式绑定
function foo() {
  console.log(this.a)
}

const obj = {
  a: 2,
  foo: foo
}

obj.foo()    // 2

// 隐式丢失
function foo() {
  console.log(this.a)
}

const obj = {
  a: 2,
  foo: foo
}

var a = 4
const bar = obj.foo
bar()    // 4    此时 bar 是 foo 函数的引用,调用时不带任何修饰,应用默认规则

// 还有一种是回调函数传参

3、显式绑定

通过 Function.prototype.bindapplycall 方法显式指定 this(硬绑定:bind 中绑定 this 对象)。

// 显式绑定
function foo() {
  console.log(this.a)
}

const obj = {
  a: 8
}

foo.call(obj)

// 硬绑定
function foo(something) {
  return this.a + something
}

var obj = {
  a: 5
}

var bar = funtion() {
  return foo.apply(obj, arguments)
}

var b = bar(3)
console.log(b)

4、new 绑定

new 对象过程:

  • 创建新对象 obj;
  • 给新对象 obj 的内部属性赋值,比如 [[Prototype]],构造原型链;(如果构造函数的原型是对象类型,则指向构造函数的原型;否则指向 Object 的原型。obj.proto = F.prototype)
  • 执行构造函数中的代码,新对象会绑定到函数调用的 this;
  • 如果构造函数内部返回对象类型数据,则返回该对象类型;否则返回新对象 obj。

优先级

new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

参考链接