glownight

返回

一、核心规则:this 指向谁?#

this 的指向取决于调用方式,而非定义位置:

┌─────────────────┬─────────────────────────────┐
│     调用方式     │           this 指向          │
├─────────────────┼─────────────────────────────┤
│ 普通函数调用      │ window(严格模式 undefined) │
│ 对象方法调用      │ 调用该方法的对象              │
│ new 构造函数     │ 新创建的实例对象              │
│ call/apply/bind  │ 指定的第一个参数               │
│ 箭头函数         │ 外层作用域的 this(定义时确定)│
│ DOM 事件回调     │ 触发事件的 DOM 元素           │
└─────────────────┴─────────────────────────────┘
plaintext

二、七种场景详解#

1. 全局上下文#

console.log(this);  // window(浏览器)或 global(Node.js)
javascript

2. 普通函数调用(默认绑定)#

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

foo();  // window(非严格模式)
javascript

严格模式下

'use strict';
function foo() {
  console.log(this);  // undefined
}
foo();
javascript

3. 对象方法调用(隐式绑定)#

const obj = {
  name: 'Alice',
  sayHi() {
    console.log(this.name);  // this → obj
  }
};

obj.sayHi();  // "Alice"
javascript

陷阱:赋值后调用会丢失绑定

const fn = obj.sayHi;
fn();  // undefined(this 指向 window,因为 fn() 是独立调用)
javascript

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

function Person(name) {
  this.name = name;  // this → 新创建的实例
}

const p = new Person('Bob');
console.log(p.name);  // "Bob"
javascript

new 做了什么

  1. 创建一个空对象
  2. this 指向这个空对象
  3. 执行构造函数代码
  4. 返回这个对象

5. 显式绑定:call / apply / bind#

function greet() {
  console.log(`Hello, ${this.name}`);
}

const user = { name: 'Charlie' };

greet.call(user);   // "Hello, Charlie" —— 立即调用,参数逐个传
greet.apply(user);  // "Hello, Charlie" —— 立即调用,参数数组传

const boundGreet = greet.bind(user);  // 返回新函数,永久绑定 this
boundGreet();  // "Hello, Charlie"
javascript

三者区别

方法调用时机传参方式
call立即执行fn.call(thisArg, arg1, arg2)
apply立即执行fn.apply(thisArg, [arg1, arg2])
bind返回新函数,稍后执行fn.bind(thisArg, arg1, arg2)

6. 箭头函数(词法绑定)#

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

const obj = {
  name: 'David',
  regularFn: function() {
    console.log(this.name);  // "David" —— obj 调用
    
    setTimeout(function() {
      console.log(this.name);  // undefined —— 独立调用,this → window
    }, 100);
    
    setTimeout(() => {
      console.log(this.name);  // "David" —— 继承外层 regularFn 的 this
    }, 100);
  }
};

obj.regularFn();
javascript

经典面试题

const obj = {
  name: 'Eve',
  sayHi: () => {
    console.log(this.name);  // undefined!
  }
};

obj.sayHi();  
// 箭头函数在全局作用域定义,this 指向 window
// obj.sayHi() 的调用方式不影响箭头函数的 this
javascript

正确写法

const obj = {
  name: 'Eve',
  sayHi() {  // 简写方法(非箭头函数)
    console.log(this.name);  // "Eve" ✅
  }
};
javascript

7. DOM 事件回调#

const button = document.getElementById('btn');

button.addEventListener('click', function() {
  console.log(this);  // button 元素
});

button.addEventListener('click', () => {
  console.log(this);  // window(继承外层作用域)
});
javascript

三、优先级规则#

当多种规则冲突时,优先级从高到低:

new 绑定 > 显式绑定(call/apply/bind)> 隐式绑定(对象方法)> 默认绑定
plaintext
function foo() {
  console.log(this.a);
}

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

obj1.foo();           // 1(隐式绑定)
obj2.foo.call(obj1);  // 1(显式绑定 > 隐式绑定)
new obj1.foo();       // undefined(new 绑定 > 隐式绑定)
javascript

四、常见陷阱与解决方案#

陷阱 1:回调函数丢失 this#

const obj = {
  name: 'Frank',
  greet() {
    console.log(`Hi, ${this.name}`);
  }
};

setTimeout(obj.greet, 100);  // "Hi, undefined" —— this 丢失
javascript

解决方案

// 方案 1:箭头函数包装
setTimeout(() => obj.greet(), 100);

// 方案 2:bind
setTimeout(obj.greet.bind(obj), 100);

// 方案 3:类字段 + 箭头函数(React 常用)
class MyClass {
  greet = () => {
    console.log(this.name);  // 永远指向实例
  }
}
javascript

陷阱 2:React 类组件中的 this#

class Button extends React.Component {
  handleClick() {
    console.log(this);  // undefined!(严格模式下)
  }
  
  render() {
    return <button onClick={this.handleClick}>Click</button>;
    // 传递的是函数引用,调用时 this 丢失
  }
}
jsx

解决方案

class Button extends React.Component {
  // 方案 1:构造函数中 bind
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  
  // 方案 2:类字段箭头函数(最简洁)
  handleClick = () => {
    console.log(this);  // 正确指向组件实例
  }
  
  render() {
    return <button onClick={this.handleClick}>Click</button>;
  }
}
jsx

五、快速判断口诀#

1. 箭头函数?→ 找外层作用域的 this
2. new 调用?→ 指向新实例
3. call/apply/bind?→ 指向指定对象
4. 对象.方法()?→ 指向该对象
5. 直接调用函数?→ window(严格模式 undefined)
plaintext

六、现代开发中的最佳实践#

场景推荐做法
React 类组件使用类字段箭头函数定义方法
需要保留 this 的回调箭头函数或 .bind(this)
不需要动态 this 的工具函数箭头函数(避免 this 干扰)
明确指定 this使用 call/apply/bind

理解 this 的关键在于:不要看函数定义在哪里,要看函数是怎么被调用的。箭头函数是唯一的例外——它的 this 在定义时就确定了。

this指向
作者 glownight
发布于 2026年4月27日