精华 【ES 系列】this 决策树
发布于 1 个月前 作者 m31271n 688 次浏览 最后一次编辑是 19 天前 来自 分享

总结了自己平时使用的 this 判断方法,希望对大家的学习和工作有所帮助。

决策树

通用决策树:

if (this 位于非箭头函数中) {
  if (函数使用 new 调用,即作为构造函数被调用) {
    [this 指向新创建的对象]
  } else if (函数使用 . 调用,即作为对象方法被调用) {
    [this 指向 . 之前的对象]
  } else {
    if (严格模式) {
      [this 为 undefined]
    } else {
      [this 为 null]
      [但,使用 undefined 或者 null 指定 this 时,this 指向全局对象]
      [this 指向全局对象]
    }
  }
} else if (this 位于箭头函数中) {
  [词法分析时,函数所处作用域的 this]
} else {
  [this 指向全局对象]
}

另外,
在严格模式下:
+ 如果手动指定了 this 且 this 是原始值,则 this 不再经历从原始值向原始值包装对象的转化
在非严格模式下:
+ 使用布尔值,字符串,数字等原始值指定 this 时,this 指向原始值所对应的包装对象
+ 使用 undefined 或者 null 指定 this 时,this 指向全局对象

严格模式下的决策树

严格模式会成为趋势,为了方便查看,也写一个严格模式下的 this 决策树:

if (this 位于非箭头函数中) {
  if (函数使用 new 调用,即作为构造函数被调用) {
    [this 指向新创建的对象]
  } else if (函数使用 . 调用,即作为对象方法被调用) {
    [this 指向 . 之前的对象]
  } else {
    [this 为 undefined]
    [如果手动指定了 this,那么 this 就是指定的值,不再对其进行对象包装]
  }
} else if (this 位于箭头函数中) {
  [词法分析时,函数所处作用域的 this]
} else {
  [this 指向全局对象]
}

示例

函数作为构造函数被调用

'use strict'

function C(num) {
  this.num = num
}

const c = new C(20)

console.log(c.num)  // 20
  • 函数使用 new 调用? 是

this 指向新创建的对象。

函数作为对象方法被调用

'use strict'

const obj = {
  num: 20,
  f: function () {
    return this
  }
}

console.log(obj.f() === obj)  // true
'use strict'

function f() {
  return this
}

const obj = {
  num: 20,
  f
}

console.log(obj.f() === obj)  // true
  • 函数使用 new 调用? 否
  • 函数使用 . 调用? 是

this 指向 . 前的对象。

函数被直接调用

function f() {
  return this
}

f()

console.log(f() === this)  // true
  • 函数使用 new 调用? 否
  • 函数使用 . 调用 ? 否
  • 严格模式?否

this 指向全局对象。

'use strict'

function f() {
  return this
}

f()

console.log(f() === undefined)  // true
  • 函数使用 new 调用? 否
  • 函数使用 . 调用? 否
  • 严格模式?是

thisundefined

还有种让人迷惑的直接调用:

'use strict'

const obj = {
  num: 20,
  f: function () {
    // 定义 g 之后就被调用了,这也是直接调用
    function g() {
      return this
    }

    return g()
  }
}

console.log(obj.f() === undefined)  // true
  • 函数使用 new 调用?否
  • 函数使用 . 调用?否
  • 严格模式?是

this 指向 undefined

函数 g 在匿名函数中声明并被调用了。分清楚 g 的调用和 f 的调用,就很容易明白了。

通过 call() / apply() / bind() 调用

'use strict'

const obj = {
  num: 20,
  f: function () {
    return this
  }
}

const objAnother = {}

console.log(obj.f.call(objAnother) === objAnother)  // true

obj.f.call(objAnother) 实际上等同于 objAnother.f()

  • 函数使用 new 调用?否
  • 函数使用 . 调用?是

this 指向 . 前的对象。

'use strict'

function f () {
  return this
}

console.log(f.call(1024))  // 1024
  • 函数使用 new 调用? 否
  • 函数使用 . 调用? 否
  • 严格模式?是。那么,不再对 this 进行对象包装

this1024

function f () {
  return this
}

console.log(f.call(1024))  // [Number: 1024]
  • 函数使用 new 调用? 否
  • 函数使用 . 调用? 否
  • 严格模式?否。
  • this 指定为原始值,进行对象包装

this 指向包装对象 [Number: 1024]

// 代码运行在 Node 模块模式中
function f () {
  return this
}

console.log(f.call(null) === global)  // true
console.log(f.call(undefined) === global)  // true
  • 函数使用 new 调用? 否
  • 函数使用 . 调用? 否
  • 严格模式?否。
  • this 指定为 undefinednull。this 指向全局对象。

this 指向指向全局对象。

后记

this 决策树在应对难以判断的 this 指向问题时,很有用。 如果大家有更好的方式,欢迎讨论。

2 回复

有意思,感谢分享!

回到顶部