JS中this关键字详解(面试必背+错题复盘+bind/call/apply用法)

本文整合JS中this关键字的核心知识点、记忆口诀、易错点复盘,以及bind/call/apply三大方法的用法,结合经典面试题和个人错题,适合整理成博客存档,助力面试备考,彻底吃透this指向问题。

一、this核心记忆口诀(背会直接破题)

核心总纲:this不看书写时,只看运行时;谁调用,指向谁(箭头函数除外)

  • 普通函数:谁调用,this是谁
  • 直接调用:无调用者,this→window(浏览器)/undefined(严格模式/Node)
  • new调用:this→新创建的实例对象
  • call/apply:临时手动指定this,仅当前调用生效
  • bind:永久手动绑定this,生成新函数,不可修改
  • 箭头函数:无自身this,继承外层第一个普通函数的this;无外层普通函数则指向window
  • 补充坑点:对象{}不产生作用域,不绑定this

优先级口诀(从高到低):new绑定 \> bind硬绑定 \> 对象方法隐式绑定 \> 普通调用默认绑定 \> 箭头函数继承绑定

二、bind、call、apply 用法详解(重点区分)

三者核心作用:手动改变函数的this指向,区别在于调用方式、传参形式和是否永久绑定。

1. call 方法

  • 语法:函数名\.call\(指定的this, 参数1, 参数2, \.\.\.\)
  • 核心特点:立即执行函数,临时绑定this(仅当前调用生效),参数以列表形式传递
  • 示例:
function foo(a, b) {
  console.log(this.name, a + b);
}
const obj = { name: "张三" };
foo.call(obj, 1, 2); // 输出:张三 3(this临时指向obj,立即执行)
foo(); // 输出:undefined/global (this恢复默认,绑定失效)

2. apply 方法

  • 语法:函数名\.apply\(指定的this, \[参数1, 参数2, \.\.\.\]\)
  • 核心特点:立即执行函数,临时绑定this(仅当前调用生效),参数以数组形式传递
  • 示例:
function foo(a, b) {
  console.log(this.name, a + b);
}
const obj = { name: "李四" };
foo.apply(obj, [1, 2]); // 输出:李四 3(参数是数组,其余和call一致)
foo(); // 绑定失效,this恢复默认

3. bind 方法

  • 语法:const 新函数 = 原函数\.bind\(指定的this, 参数1, 参数2, \.\.\.\)
  • 核心特点:不立即执行函数,返回一个新函数;新函数的this被永久绑定,不可修改(硬绑定)
  • 关键注意:即使再用call/apply/bind修改新函数的this,也无效;可重复调用新函数,this始终不变
  • 示例:
function foo(a, b) {
  console.log(this.name, a + b);
}
const obj = { name: "王五" };
const newFoo = foo.bind(obj, 1, 2); // 生成新函数,this永久绑定obj
newFoo(); // 输出:王五 3
newFoo.call({ name: "赵六" }); // 依然输出:王五 3(绑定不可改)

4. 三者对比表

方法是否立即执行this绑定特性参数传递方式核心特点
call临时绑定,仅当前生效列表形式(逗号分隔)临时借用this,单次使用
apply临时绑定,仅当前生效数组形式参数较多时更便捷
bind永久绑定,生成新函数列表形式(可预传参)可重复使用,绑定不可改

三、个人错题复盘(箭头函数+bind易错重点)

以下是之前做题时出错的题目,重点分析错误原因,对应this核心规则,彻底避免再错。

错题1:对象中的箭头函数(第7题)

var a = 100;
var obj = {
  a: 200,
  foo: () => {
    console.log(this.a);
  }
};
obj.foo(); // 我的答案:200 → 正确答案:100
错误原因

误以为“obj调用foo,this就指向obj”,忽略了箭头函数无自身this,且对象{}不产生作用域

正确解析
  • foo是箭头函数,没有自己的this,需继承外层作用域的this;
  • 箭头函数写在obj里,但obj是对象,不是普通函数,不产生作用域;
  • 外层无其他普通函数,this指向全局window,this.a = 100。

错题2:bind永久绑定(第9题)

var a = 10;
function foo() {
  console.log(this.a);
}
var obj = { a: 20 };
var fn = foo.bind(obj);
fn(); // 我的答案:window → 正确答案:20
错误原因

忘记bind的“永久绑定”特性,误以为fn()是普通调用,this会变回window。

正确解析
  • bind会生成一个新函数(fn),并将新函数的this永久绑定到obj;
  • 无论新函数怎么调用(即使再用call),this都不会改变;
  • fn()调用时,this=obj,this.a=20。

错题3:对象中箭头函数的this(第10题fn2)

var name = 'global';
var obj = {
  name: 'obj',
  fn1: function() { console.log(this.name) },
  fn2: () => { console.log(this.name) },
};
obj.fn2(); // 我的答案:obj → 正确答案:global
错误原因

和第7题错误一致,混淆了“对象调用”和“箭头函数的this继承规则”,忽略对象不产生作用域。

正确解析
  • fn2是箭头函数,无自身this,继承外层作用域的this;
  • 外层无普通函数,this指向window,window.name = \&\#39;global\&\#39;;
  • obj.fn2()只是调用箭头函数,不会改变箭头函数的this指向(箭头函数不看调用者)。

错题4:setTimeout中箭头函数与普通函数(第5、6题对比)

// 第5题(普通函数)
var obj = {
  a: 10,
  foo: function() {
    setTimeout(function() {
      console.log(this.a); // 我的答案:window(正确),但理解不透彻
    }, 100);
  }
};
obj.foo();

// 第6题(箭头函数)
var obj = {
  a: 10,
  foo: function() {
    setTimeout(() => {
      console.log(this.a); // 我的答案:obj(正确),但分不清和第5题的区别
    }, 100);
  }
};
obj.foo();
错误原因

能选对答案,但未彻底掌握“普通函数vs箭头函数”在异步回调中的this区别。

正确解析
  • 第5题:setTimeout的回调是普通函数,有自身this;普通函数由window调用,this=window,this.a=全局a(无则undefined);
  • 第6题:setTimeout的回调是箭头函数,无自身this;外层是foo(普通函数),foo的this=obj(被obj调用),所以箭头函数继承this=obj,this.a=10。

四、this经典易错面试题(10道,含解析)

以下题目覆盖所有核心场景,结合前面的知识点,可自行先做题,再看解析巩固。

第1题:普通函数直接调用

var a = 10;
function foo() {
  console.log(this.a);
}
foo(); // 答案:10(浏览器)/ undefined(严格模式)

解析:普通函数直接调用,无明确调用者,this指向全局window(浏览器),window.a=10。

第2题:对象方法调用

var obj = {
  a: 20,
  foo: function() {
    console.log(this.a);
  }
};
obj.foo(); // 答案:20

解析:obj调用foo,谁调用this指向谁,this=obj,obj.a=20。

第3题:对象方法单独调用

var obj = {
  a: 20,
  foo: function() {
    console.log(this.a);
  }
};
var fn = obj.foo;
fn(); // 答案:10(浏览器)/ undefined(严格模式)

解析:fn接收的是foo函数本身,并非obj调用;fn()是普通直接调用,this指向全局。

第4题:new构造函数调用

function Foo() {
  this.a = 30;
  console.log(this);
}
var f = new Foo(); // 答案:Foo { a: 30 }

解析:new调用构造函数,this指向新创建的实例对象(f),函数内部this.a=30,打印实例。

第5题:setTimeout普通函数回调

var obj = {
  a: 10,
  foo: function() {
    setTimeout(function() {
      console.log(this.a);
    }, 100);
  }
};
obj.foo(); // 答案:10(浏览器)/ undefined(严格模式)

解析:setTimeout回调是普通函数,由window调用,this=window,window.a=10。

第6题:setTimeout箭头函数回调

var obj = {
  a: 10,
  foo: function() {
    setTimeout(() => {
      console.log(this.a);
    }, 100);
  }
};
obj.foo(); // 答案:10

解析:箭头函数无自身this,继承外层foo的this(foo被obj调用,this=obj),obj.a=10。

第7题:对象中的箭头函数

var a = 100;
var obj = {
  a: 200,
  foo: () => {
    console.log(this.a);
  }
};
obj.foo(); // 答案:100

解析:箭头函数无自身this,外层无普通函数,this=window,window.a=100(对象不产生作用域)。

第8题:call临时绑定

var obj1 = {
  a: 1,
  foo: function() {
    console.log(this.a);
  }
};
var obj2 = {
  a: 2,
};
obj1.foo.call(obj2); // 答案:2

解析:call临时将foo的this绑定到obj2,立即执行,this=obj2,obj2.a=2。

第9题:bind永久绑定

var a = 10;
function foo() {
  console.log(this.a);
}
var obj = { a: 20 };
var fn = foo.bind(obj);
fn(); // 答案:20

解析:bind生成新函数fn,this永久绑定obj,fn()调用时,this=obj,obj.a=20。

第10题:综合题(箭头+普通函数+对象调用)

var name = 'global';
var obj = {
  name: 'obj',
  fn1: function() {
    console.log(this.name);
  },
  fn2: () => {
    console.log(this.name);
  },
};

obj.fn1(); // 答案:obj
var fn = obj.fn1;
fn(); // 答案:global
obj.fn2(); // 答案:global

解析:

  • obj.fn1():obj调用普通函数,this=obj,输出obj;
  • fn():fn是普通函数直接调用,this=window,输出global;
  • obj.fn2():fn2是箭头函数,外层无普通函数,this=window,输出global。

五、总结

this的核心难点在于“运行时绑定”和“箭头函数的继承规则”,记住口诀+分清场景,就能避开90%的坑:

  1. 普通函数看调用者,箭头函数看书写位置(外层普通函数);
  2. call/apply临时绑,bind永久绑;
  3. 对象不产生作用域,箭头函数写在对象里,this不指向对象;
  4. 优先级顺序记牢,new和bind优先级最高。

收藏本文,面试前快速过一遍口诀和错题,就能轻松应对this相关面试题~

(注:文档部分内容可能由 AI 生成)

标签: none

添加新评论