JavaScript中的this关键字深度解析

43 阅读3分钟

JavaScript中的this关键字深度解析

为什么需要this

在JavaScript中,this提供了一种隐式传递对象引用的优雅方式。想象一下,如果没有this,我们编写代码时需要:

// 需要显式传递上下文对象
function identify(context) {
  return context.name.toUpperCase()
}

function speek(context) {
  const greeting = 'Hello, I am ' + identify(context)
  console.log(greeting)
}

const me = { name: 'Tom' }
speek(me) // "Hello, I am TOM"

使用this后,代码变得更加简洁优雅:

function identify() {
  return this.name.toUpperCase()
}

function speek() {
  const greeting = 'Hello, I am ' + identify.call(this)
  console.log(greeting)
}

const me = { name: 'Tom' }
speek.call(me) // "Hello, I am TOM"

this就像一个智能代词,它根据函数的调用方式动态确定指向的对象,让代码更简洁、更易于复用。

五大绑定规则解析

1. 默认绑定(独立函数调用)

当函数被独立调用时,this默认指向全局对象(浏览器中是window

var a = 1

function foo() {
  console.log(this.a) // 1 (指向window)
}

foo() // 独立函数调用

2. 隐式绑定(方法调用)

当函数作为对象方法被调用时,this指向调用它的对象

var obj = {
  a: 2,
  foo: function() {
    console.log(this.a) // 2
  }
}

obj.foo() // 通过obj调用

3. 隐式丢失

当函数被间接引用时,可能发生隐式丢失

var a = 1
var obj = {
  a: 2,
  foo: function() {
    console.log(this.a)
  }
}

var bar = obj.foo // 函数别名
bar() // 1 (独立调用,this指向window)

// 多层调用时,指向最近的对象
var obj2 = {
  a: 3,
  obj: obj
}
obj2.obj.foo() // 2 (指向obj而非obj2)

4. 显式绑定(call/apply/bind)

使用callapplybind可以强制指定this的指向

function foo(x, y) {
  console.log(this.a, x + y)
}

var obj = { a: 1 }

foo.call(obj, 1, 2)   // 1 3
foo.apply(obj, [1, 2]) // 1 3

const bar = foo.bind(obj, 2, 3)
bar(4) // 1 5 (4被忽略,因为参数已绑定)

5. new绑定(构造函数调用)

使用new调用函数时,this指向新创建的对象实例

function Person() {
  this.name = '超超'
  this.age = 18
  console.log(this) // Person {name: '超超', age: 18}
}

const p1 = new Person()

new操作符实际上做了四件事:

  1. 创建一个新对象
  2. 将新对象的原型指向构造函数的原型
  3. 将构造函数中的this绑定到这个新对象
  4. 如果构造函数没有返回其他对象,则返回这个新对象

特殊场景:箭头函数

箭头函数没有自己的this,它继承外层作用域this

var a = 1
var obj = {
  a: 2,
  bar: function() {
    const baz = () => {
      console.log(this.a) // 2 (继承bar的this)
    }
    baz()
  }
}
obj.bar()

// 多层箭头函数嵌套
function a() {
  console.log(this) // window
  let b = function() {
    let c = () => {
      let d = () => {
        console.log(this) // window (继承自b的this)
      }
      d()
    }
    c()
  }
  b()
}
a()

综合应用练习

var a = 3
function foo() {
  var a = 2
  function bar() {
    var a = 1
    console.log(this.a) // 3 (默认绑定,指向window)
  }
  bar()
}
foo()
var a = 1
function foo() {
  console.log(this.a)
}
var obj = {
  a: 2,
  foo: foo
}
var obj2 = {
  a: 3,
  obj: obj
}
obj2.obj.foo() // 2 (隐式绑定,指向obj)

总结

  1. 优先使用显式绑定:当需要明确this指向时,使用call/apply/bind
  2. 方法调用注意上下文:确保对象方法被正确调用,避免隐式丢失
  3. 箭头函数谨慎使用:在需要动态this的场景避免箭头函数
  4. 构造函数使用new:确保构造函数正确实例化对象

理解this是掌握JavaScript核心的关键,它让我们的代码更加灵活高效。通过熟练掌握这四大规则,你就能在复杂场景中游刃有余地使用this了!