Skip to content

JS中This指向问腿

在 JavaScript 中,this 是一个特殊关键字,其指向并非在函数定义时确定,而是在函数调用时动态绑定,取决于调用方式。理解 this 的指向是 JS 面试的高频考点,以下从核心场景、特殊情况和面试题三个维度详细讲解。

一、this 指向的核心场景

1. 全局作用域中的 this

  • 非严格模式:全局作用域中 this 指向全局对象(浏览器中是 window,Node.js 中是 global)。
  • 严格模式:全局作用域中 this 仍指向全局对象(严格模式仅影响函数内的 this)。
javascript
console.log(this === window); // 浏览器中:true(非严格模式)

'use strict';
console.log(this === window); // 仍为 true(严格模式不改变全局 this)

2. 普通函数调用(最容易混淆)

函数独立调用时,this 指向由是否为严格模式决定:

  • 非严格模式this 指向全局对象(window/global)。
  • 严格模式thisundefined
javascript
// 非严格模式
function foo() {
  console.log(this === window); // true
}
foo(); // 独立调用

// 严格模式
'use strict';
function bar() {
  console.log(this); // undefined
}
bar(); // 独立调用

3. 对象方法调用

函数作为对象的方法被调用时,this 指向调用该方法的对象(即方法所属的对象)。

javascript
const obj = {
  name: '张三',
  sayHi() {
    console.log(this.name); // 指向 obj
  }
};
obj.sayHi(); // 输出:张三

// 注意:方法赋值后独立调用,this 会改变
const func = obj.sayHi;
func(); // 非严格模式下:this 指向 window(输出:undefined)

4. 构造函数调用(new 关键字)

new 调用构造函数时,this 指向新创建的实例对象

javascript
function Person(name) {
  this.name = name; // this 指向新实例
}
const p = new Person('李四');
console.log(p.name); // 输出:李四

特殊情况:若构造函数返回一个对象类型(非原始值),this 会指向返回的对象(而非实例):

javascript
function Person(name) {
  this.name = name;
  return { age: 18 }; // 返回对象
}
const p = new Person('李四');
console.log(p.name); // undefined(this 指向返回的 {age:18})

5. 箭头函数中的 this

箭头函数没有自己的 this,其 this 继承自外层作用域的 this(定义时确定,而非调用时)。

javascript
const obj = {
  name: '王五',
  sayHi: () => {
    console.log(this.name); // this 继承自外层(全局),指向 window
  },
  delaySay() {
    setTimeout(() => {
      console.log(this.name); // 继承自 delaySay 的 this(即 obj)
    }, 100);
  }
};
obj.sayHi(); // 输出:undefined(window 无 name)
obj.delaySay(); // 输出:王五(正确)

6. call/apply/bind 改变 this

这三个方法可强制绑定函数的 this 指向:

  • func.call(thisArg, arg1, arg2, ...):立即调用函数,参数逐个传入。
  • func.apply(thisArg, [arg1, arg2, ...]):立即调用函数,参数以数组传入。
  • func.bind(thisArg, arg1, arg2, ...):返回新函数(不立即调用),this 永久绑定。
javascript
function sayName() {
  console.log(this.name);
}
const obj1 = { name: '赵六' };
const obj2 = { name: '钱七' };

sayName.call(obj1); // 输出:赵六(this 指向 obj1)
sayName.apply(obj2); // 输出:钱七(this 指向 obj2)

const boundFunc = sayName.bind(obj1);
boundFunc(); // 输出:赵六(绑定后无法修改)
boundFunc.call(obj2); // 仍输出:赵六(bind 优先级最高)

7. DOM 事件处理中的 this

DOM 事件回调函数中,this 指向触发事件的 DOM 元素(与 event.target 类似,但有区别)。

html
<button id="btn">点击</button>
<script>
  const btn = document.getElementById('btn');
  btn.onclick = function() {
    console.log(this === btn); // true(this 指向触发事件的按钮)
  };
</script>

二、常见误区总结

  1. 箭头函数不能作为构造函数:箭头函数没有 this,用 new 调用会报错。
  2. 对象方法中的嵌套函数:嵌套函数独立调用时,this 指向全局(非严格模式),而非外层对象:
    javascript
    const obj = {
      name: '孙八',
      outer() {
        function inner() {
          console.log(this.name); // 非严格模式:window.name(可能为 undefined)
        }
        inner(); // 独立调用,this 不指向 obj
      }
    };
    obj.outer(); // 输出:undefined
  3. bind 绑定后无法被覆盖call/apply 无法修改 bind 已绑定的 this

三、面试题及解析

题目 1:基础判断

javascript
var name = '全局';
const obj = {
  name: '对象',
  say() {
    console.log(this.name);
  }
};
const func = obj.say;

obj.say(); // ?
func(); // ?

答案
obj.say() 输出 对象(方法调用,this 指向 obj);
func() 输出 全局(独立调用,this 指向 window,var 声明的 name 挂载到 window)。

题目 2:箭头函数与普通函数对比

javascript
var age = 10;
const person = {
  age: 20,
  getAge1: function() {
    return this.age;
  },
  getAge2: () => {
    return this.age;
  }
};
console.log(person.getAge1()); // ?
console.log(person.getAge2()); // ?

答案
person.getAge1() 输出 20(普通函数,this 指向 person);
person.getAge2() 输出 10(箭头函数,this 继承自全局,指向 window)。

题目 3:构造函数与返回值

javascript
function Student(name) {
  this.name = name;
  return name; // 返回原始值
}
const s1 = new Student('小明');
console.log(s1.name); // ?

function Teacher(name) {
  this.name = name;
  return { name: '老师' }; // 返回对象
}
const t1 = new Teacher('小红');
console.log(t1.name); // ?

答案
s1.name 输出 小明(构造函数返回原始值,this 仍指向实例);
t1.name 输出 老师(构造函数返回对象,this 指向返回的对象)。

题目 4:call 与嵌套函数

javascript
const obj = {
  a: 1,
  foo() {
    const bar = function() {
      console.log(this.a);
    };
    bar.call(this); // 强制绑定 this 为 obj
  }
};
obj.foo(); // ?

答案:输出 1bar.call(this)this 是 foo 的 this(即 obj),故 bar 的 this 指向 obj)。

题目 5:综合场景

javascript
var x = 10;
const obj = {
  x: 20,
  fn() {
    console.log(this.x);
    return function() {
      console.log(this.x);
    };
  }
};
obj.fn()(); // 输出什么?

答案:先输出 20obj.fn() 中 this 指向 obj),再输出 10(返回的函数独立调用,this 指向 window)。

总结

判断 this 指向的优先级:

  1. new 调用 > bind 绑定 > call/apply > 对象方法调用;
  2. 普通函数独立调用:非严格模式指向全局,严格模式为 undefined
  3. 箭头函数:继承外层作用域的 this,与调用方式无关。

掌握这些规则,就能应对绝大多数 this 指向的面试题。