一、this绑定规则
this 的本质是函数执行时的动态上下文,它不是定义时决定的(箭头函数除外),而是由调用方式决定。
1. 默认绑定(全局/严格模式)
直接调用函数,非严格模式下this指向全局对象(浏览器window),严格模式下为undefined。
function fn() { console.log(this) } fn(); // window / undefined (strict)2. 隐式绑定(对象方法调用)
函数作为对象的属性调用,this指向该对象。
const obj = { name: 'obj', fn() { console.log(this.name) } }; obj.fn(); // 'obj'隐式丢失:如果函数被单独取出再调用,就退化为默认绑定。
const f = obj.fn; f(); // undefined (默认绑定)3. 显式绑定(call/apply/bind)
使用call、apply、bind强制指定 this。
call(context, ...args)立即调用apply(context, [args])立即调用bind(context)返回一个新函数,永久绑定 this
function greet() { console.log(this.name); } const person = { name: '李四' }; greet.call(person); // 输出:李四 greet.apply(person); // 输出:李四call和apply都会立即执行函数,同时把this绑定在第一个参数上(context)。区别只是传参方式:
call(context, a, b),apply(context, [a, b])。
bind比较特殊:它不立即执行,而是返回一个新函数,这个新函数的this被永久锁定为指定对象。
const boundGreet = greet.bind(person); boundGreet(); // 输出:李四 boundGreet.call({ name: '王五' }); // 输出:李四 (已被 bind 锁死,无法再改变)4.new绑定(构造函数)
箭头函数没有自己的this,它体内的this直接去它被定义时所在的词法作用域里找。
const obj = { name: '对象', normalFn: function() { console.log(this.name); }, arrowFn: () => { console.log(this.name); } }; obj.normalFn(); // 输出:对象(隐式绑定) obj.arrowFn(); // 输出:undefined(这里的 this 是全局,因为定义 arrowFn 时外层作用域是全局)为什么arrowFn不是输出'对象'?
因为箭头函数是在定义时就决定了this,它的this就是定义它时外层代码的this,也就是全局或某个函数。在那个时刻,外层没有obj这个点,所以this指向全局。
最常用的场景:在回调中保留外部的this:
function Counter() { this.count = 0; setInterval(() => { this.count++; // 这里的 this 就是 Counter 实例,因为它定义在 Counter 构造函数内 }, 1000); }规则总结:箭头函数的this是词法作用域链的一部分,和调用方式无关。
5.优先级总结
如果多个规则同时出现,优先级从高到低:
箭头函数?→ 直接取定义时的外层 this,后面的规则都不适用。
用了
new?→ 指向新对象。用了
bind/call/apply?→ 显式绑定。通过对象点出来调用(
obj.fn())?→ 隐式绑定。啥都没有直接调用?→ 默认绑定(严格下是 undefined,非严格是全局对象)。
6.练习
①
var name = '全局'; function foo() { console.log(this.name); } const obj1 = { name: 'obj1', foo: foo }; const obj2 = { name: 'obj2' }; obj1.foo(); // ? foo.call(obj2); // ? const bar = obj1.foo; bar(); // ? const bound = foo.bind(obj1); bound(); // ? new foo(); // ?②
var name = 'window'; const obj = { name: 'object', inner: { name: 'inner', fn: function() { console.log(this.name); } } }; obj.inner.fn(); // ? const f = obj.inner.fn; f(); // ? f.call(obj); // ? const bound2 = f.bind(obj.inner); new bound2(); // ?7.答案
①
var name = '全局'; function foo() { console.log(this.name); } const obj1 = { name: 'obj1', foo: foo }; const obj2 = { name: 'obj2' }; obj1.foo(); // 'obj1' foo.call(obj2); // 'obj2' const bar = obj1.foo; bar(); // '全局' const bound = foo.bind(obj1); bound(); // 'obj1' new foo(); // undefined②
var name = 'window'; const obj = { name: 'object', inner: { name: 'inner', fn: function() { console.log(this.name); } } }; obj.inner.fn(); // 'inner' const f = obj.inner.fn; f(); // 'window' f.call(obj); // 'object' const bound2 = f.bind(obj.inner); new bound2(); // undefined