this 绑定机制
this是 JavaScript 中最容易出错的概念。它不是在定义时确定,而是在调用时根据调用方式动态绑定。
四种绑定规则
1. 默认绑定
js
function greet() {
console.log(this)
}
greet() // 非严格模式:window / global;严格模式:undefined
// 严格模式
'use strict'
function greetStrict() {
console.log(this) // undefined
}2. 隐式绑定
js
const user = {
name: 'Alice',
greet() {
console.log(this.name) // this → user
}
}
user.greet() // 'Alice'
// ⚠️ 隐式丢失:赋值给变量后丢失绑定
const fn = user.greet
fn() // undefined(this → window/undefined)
// ⚠️ 回调中丢失绑定
setTimeout(user.greet, 0) // undefined3. 显式绑定
js
function greet(greeting) {
return `${greeting}, ${this.name}`
}
const alice = { name: 'Alice' }
const bob = { name: 'Bob' }
// call:立即调用,参数逐个传入
greet.call(alice, 'Hello') // 'Hello, Alice'
// apply:立即调用,参数以数组传入
greet.apply(bob, ['Hi']) // 'Hi, Bob'
// bind:返回新函数,永久绑定 this
const greetAlice = greet.bind(alice)
greetAlice('Hey') // 'Hey, Alice'
greetAlice.call(bob, 'Hey') // 'Hey, Alice'(bind 后无法再改变 this)4. new 绑定
js
function Person(name) {
// new 调用时,this 指向新创建的对象
this.name = name
this.greet = function() {
console.log(this.name)
}
}
const alice = new Person('Alice')
alice.greet() // 'Alice'优先级
new 绑定 > 显式绑定(call/apply/bind)> 隐式绑定 > 默认绑定js
function foo() { console.log(this.x) }
const obj1 = { x: 1, foo }
const obj2 = { x: 2, foo }
obj1.foo() // 1(隐式)
obj1.foo.call(obj2) // 2(显式 > 隐式)
const BoundFoo = foo.bind(obj1)
BoundFoo.call(obj2) // 1(bind 后显式绑定无效)
const instance = new BoundFoo() // new 绑定 > bind
// this 指向新对象,x 为 undefined箭头函数:词法 this
箭头函数没有自己的 this,它捕获定义时所在作用域的 this,且无法被改变。
js
const timer = {
seconds: 0,
// ❌ 普通函数:this 丢失
startWrong() {
setInterval(function() {
this.seconds++ // this → window,不是 timer
}, 1000)
},
// ✅ 箭头函数:捕获外层 this
start() {
setInterval(() => {
this.seconds++ // this → timer ✓
}, 1000)
}
}
// 箭头函数的 this 无法被 call/apply/bind 改变
const arrow = () => console.log(this)
arrow.call({ x: 1 }) // 仍然是外层的 this,不是 { x: 1 }类方法中的箭头函数
js
class Button {
constructor(label) {
this.label = label
}
// ❌ 普通方法:作为回调时 this 丢失
handleClickWrong() {
console.log(this.label)
}
// ✅ 类字段箭头函数:this 永远指向实例
handleClick = () => {
console.log(this.label) // 始终正确
}
}
const btn = new Button('Submit')
document.addEventListener('click', btn.handleClick) // ✓
document.addEventListener('click', btn.handleClickWrong) // ✗ this 丢失手写 call / apply / bind
js
// 手写 call
Function.prototype.myCall = function(context, ...args) {
context = context ?? globalThis
const key = Symbol() // 避免属性名冲突
context[key] = this // this 是被调用的函数
const result = context[key](...args)
delete context[key]
return result
}
// 手写 apply
Function.prototype.myApply = function(context, args = []) {
context = context ?? globalThis
const key = Symbol()
context[key] = this
const result = context[key](...args)
delete context[key]
return result
}
// 手写 bind
Function.prototype.myBind = function(context, ...preArgs) {
const fn = this
return function(...args) {
// 如果通过 new 调用,this 指向新对象,忽略绑定的 context
if (new.target) {
return new fn(...preArgs, ...args)
}
return fn.apply(context, [...preArgs, ...args])
}
}React 中的 this 问题
jsx
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = { count: 0 }
// 方式一:构造函数中 bind
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ count: this.state.count + 1 })
}
// 方式二:类字段箭头函数(推荐)
handleClickArrow = () => {
this.setState({ count: this.state.count + 1 })
}
render() {
return (
<div>
{/* 方式三:内联箭头函数(每次渲染创建新函数,性能差)*/}
<button onClick={() => this.handleClick()}>Click</button>
{/* 推荐:使用类字段箭头函数 */}
<button onClick={this.handleClickArrow}>Click</button>
</div>
)
}
}总结
this在调用时确定,不是定义时- 四种规则优先级:
new> 显式 > 隐式 > 默认 - 箭头函数没有自己的
this,捕获外层词法this - 类方法作为回调时会丢失
this,用箭头函数类字段解决 bind返回的函数,this无法再被改变(除了new)