讲解

this 是函数调用时由「调用方式」决定的上下文对象,规则可以归纳为四条,按优先级从低到高:一、默认绑定——普通函数独立调用 fn(),非严格模式 this 是全局对象,严格模式和模块里是 undefined;二、隐式绑定——作为对象方法调用 obj.fn(),this 是点号前面的 obj;三、显式绑定——fn.call(obj)、fn.apply(obj)、fn.bind(obj) 强制指定 this;四、new 绑定——new Fn() 时 this 是新建的对象。判断任何 this 指向,先问「它是怎么被调用的」。

箭头函数是特例:它没有自己的 this,永远捕获定义处外层的 this,且无法被 call/bind 改变。这让它成为回调场景的救星——传统回调(定时器、事件、数组方法)里普通函数的 this 会变成默认绑定而「丢失」,箭头函数则稳定指向外层。

this 丢失是最常见的坑:const f = obj.method; f() 单独调用时 this 不再是 obj;把对象方法作为回调传出去(onClick(user.save))同样丢失。解决方案有三个:调用处保持 obj.method() 形式、用 bind 提前绑定、或把方法改写成箭头函数属性(捕获定义处 this)。事件处理里 addEventListener 会把 this 设为当前元素,这是宿主环境给的隐式规则。

示例

const user = {
  name: '小明',
  greet() {
    return '我是 ' + this.name;
  },
};

// 隐式绑定:点号前是谁,this 就是谁
console.log(user.greet());

// 方法被单独取出:this 丢失
const detached = user.greet;
try {
  detached();
} catch (e) {
  console.log('单独调用丢失 this:', e.constructor.name);
}

// bind 显式绑定
const bound = user.greet.bind({ name: '小红' });
console.log('bind 之后:', bound());

// call/apply 立即调用并指定 this
function introduce(city) {
  return this.name + ' 来自 ' + city;
}
console.log(introduce.call({ name: '小刚' }, '上海'));
console.log(introduce.apply({ name: '小丽' }, ['杭州']));

// new 绑定
function Person(name) {
  this.name = name;
}
const p = new Person('新建的人');
console.log('new 绑定:', p.name);

// 箭头函数捕获外层 this
const team = {
  name: '前端组',
  members: ['甲', '乙'],
  list() {
    return this.members.map((m) => this.name + '的' + m);
  },
};
console.log(team.list());

(注:示例在模块/严格模式下运行,独立调用的 detached() 因 this 为 undefined 而抛 TypeError;在浏览器非严格模式的脚本里它会静默绑定到 window。)

常见坑

  • 把对象方法当回调传:button.onClick = user.save 后 this 丢失。传 () => user.save() 或 user.save.bind(user)。
  • 回调里的普通函数:arr.forEach(function() { this.xxx }) 里 this 默认绑定,通常不是你想要的。换箭头函数。
  • 嵌套函数里的 this:方法内部又定义普通函数 helper(),helper 里的 this 与外层方法的 this 无关(默认绑定)。用箭头函数或外层先 const self = this。
  • 以为 this 指向函数自身或定义处:普通函数的 this 只看调用点,与在哪定义无关——这正是它和箭头函数的本质区别。

小结

this 由调用方式决定:独立调用、方法调用、call/apply/bind、new 四种绑定规则;箭头函数词法捕获外层 this 且不可改。下一节看对象之间的继承机制:原型链。