JavaScript 的 "this":从新手到轻松掌握

74 阅读3分钟

JavaScript 的 "this":从新手到轻松掌握

最近在学习 JavaScript 中的 this 关键字,发现它真的是让人又爱又恨的存在。今天我想和大家分享我对 this 的理解,希望能帮助到同样在学习的同学。

一、为什么要用 this

我们先看一个例子:

// 没有 this 的写法
function identify(context) {
  return context.name.toUpperCase()
}

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

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

每次调用都要显式传递 context 对象,代码显得冗长。而使用 this

// 使用 this 的写法
function identify() {
  return this.name.toUpperCase()
}

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

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

this 提供了更优雅的方式来隐式传递对象引用,让代码更简洁、易于复用。

二、this 到底是什么?

简单来说,this 是一个代词,它的值取决于函数被调用的方式。就像中文里的"我",在不同人的嘴里指向不同的人。

// 在不同环境中,this 指向不同
console.log(this) // 全局环境下指向 window

const obj = {
  name: 'Tom',
  sayHi() {
    console.log(this.name) // 这里的 this 指向 obj
  }
}

三、this 的四大绑定规则

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

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

var a = 1
function foo() {
  console.log(this.a) // 1
}
foo() // 相当于 window.foo()

注意:严格模式下,this 会是 undefined

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

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

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

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

obj.foo() // 2,this 指向 obj

但是注意隐式丢失的问题:

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

const bar = obj.foo // 只是赋值函数,没有调用!
bar() // undefined,此时 this 指向 window
3. 显式绑定(call/apply/bind)

我们可以强制指定 this 的指向:

function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation)
}

const person = { name: 'Tom' }

// call - 立即执行,参数逐个传递
greet.call(person, 'Hello', '!') // Hello, Tom!

// apply - 立即执行,参数以数组传递
greet.apply(person, ['Hi', '!!']) // Hi, Tom!!

// bind - 返回新函数,稍后执行
const greetTom = greet.bind(person, 'Hey')
greetTom('...') // Hey, Tom...
4. new 绑定(构造函数调用)

使用 new 调用函数时,this 会绑定到新创建的对象:

function Person(name) {
  this.name = name
  this.sayHi = function() {
    console.log('Hi, I am ' + this.name)
  }
}

const tom = new Person('Tom')
tom.sayHi() // Hi, I am Tom

注意:如果构造函数有 return 语句:

  • 返回基本类型,会被忽略
  • 返回对象,则会替换掉 new 创建的对象
function Person() {
  this.name = 'Tom'
  return { name: 'Jerry' }
}

const p = new Person()
console.log(p.name) // Jerry,不是 Tom!

四、箭头函数的 this

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

const obj = {
  name: 'Tom',
  sayHi: function() {
    // 普通函数,this 指向 obj
    setTimeout(function() {
      console.log('Hi, ' + this.name) // undefined,this 指向 window
    }, 100)
  }
}

const obj2 = {
  name: 'Jerry',
  sayHi: function() {
    // 箭头函数,继承外层的 this
    setTimeout(() => {
      console.log('Hi, ' + this.name) // Hi, Jerry
    }, 100)
  }
}

五、优先级和特殊情况

四种绑定规则的优先级: new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定

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

const obj1 = { a: 1, foo }
const obj2 = { a: 2, foo }

obj1.foo() // 1,隐式绑定
obj1.foo.call(obj2) // 2,显式绑定优先

总结

理解 this 的关键是记住:this 的指向在函数被调用时确定,而不是定义时。它就像个"变色龙",会根据不同的调用环境改变指向。

对于初学者,我的建议是:

  1. 先掌握四大绑定规则
  2. 多写代码实践,理解各种情况
  3. 善用箭头函数解决大部分问题

希望这篇文章对你有帮助!如果有任何疑问或指正,欢迎在评论区讨论~