基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

JavaScript中的this关键字

知识点图片

本文讲解 JavaScript 中 this 的指向规则。this 的值取决于函数的调用方式,而非定义位置。主要涵盖默认、隐式、显式、new绑定和箭头函数五种情况及其优先级。

我们来系统地梳理一下 JavaScript 中 this 的指向规则。这是 JavaScript 中一个非常重要但又容易混淆的概念。

核心原则:this 的值取决于函数被调用的方式(the call-site),而不是函数被定义的地方。

我们可以将 this 的指向规则归纳为以下五种情况,并按优先级从低到高排列。


1. 默认绑定 (Default Binding)

当一个函数作为普通函数独立调用,没有被任何对象拥有,也没有通过 call, apply, bind 指定时,就会使用默认绑定。

  • 非严格模式 (Non-Strict Mode)this 指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。
  • 严格模式 (Strict Mode)this 的值为 undefined

示例:

javascript
function sayHi() {
  console.log(this);
}

sayHi(); // 非严格模式下,输出 window 对象
         // 严格模式下 ('use strict'),输出 undefined

常见陷阱: 将对象的方法赋值给一个变量后独立调用。

javascript
const person = {
  name: 'Alice',
  sayName: function() {
    console.log(this.name);
  }
};

const greet = person.sayName;
greet(); // 非严格模式下,this 指向 window,输出 undefined (因为 window.name 通常为空)
         // 严格模式下,this 是 undefined,会报错:Cannot read property 'name' of undefined

在这个例子中,greet() 调用时,它是一个独立的函数调用,丢失了与 person 对象的关联,因此应用了默认绑定规则。


2. 隐式绑定 (Implicit Binding)

当函数作为对象的一个方法被调用时,this 指向这个对象。简单来说,就是看调用函数时,点 (.) 前面是谁,this 就是谁。

示例:

javascript
const person = {
  name: 'Bob',
  sayName: function() {
    console.log(this.name); // this 指向 person 对象
  }
};

person.sayName(); // 输出 "Bob"

链式调用中的隐式绑定:

javascript
const obj = {
  a: 1,
  b: {
    a: 2,
    fn: function() {
      console.log(this.a);
    }
  }
};

obj.b.fn(); // 输出 2

obj.b.fn() 调用中,fn 是被 obj.b 调用的,所以 this 指向 obj.b


3. 显式绑定 (Explicit Binding)

通过 call(), apply(), bind() 方法,我们可以强制指定函数内部 this 的指向。

  • function.call(thisArg, arg1, arg2, ...)

    • 立即调用函数。
    • 第一个参数 thisArg 就是 this 的指向。
    • 后面的参数逐个传递给函数。
  • function.apply(thisArg, [argsArray])

    • 立即调用函数。
    • 第一个参数 thisArgthis 的指向。
    • 第二个参数是一个数组,数组中的元素会作为参数传递给函数。
  • function.bind(thisArg, arg1, ...)

    • 不会立即调用函数,而是返回一个新函数
    • 这个新函数的 this 被永久地绑定到了 thisArg
    • bind 也可以预先设置部分参数。

示例:

javascript
function greet(greeting, punctuation) {
  console.log(greeting + ', ' + this.name + punctuation);
}

const person1 = { name: 'Charlie' };
const person2 = { name: 'David' };

// 使用 call
greet.call(person1, 'Hello', '!'); // 输出 "Hello, Charlie!"

// 使用 apply
greet.apply(person2, ['Hi', '.']); // 输出 "Hi, David."

// 使用 bind
const greetCharlie = greet.bind(person1, 'Good morning');
greetCharlie('?'); // 输出 "Good morning, Charlie?"

4. new 绑定 (new Binding)

当一个函数使用 new 关键字来调用时,这个函数被称为构造函数。new 绑定会改变 this 的指向。

使用 new 调用函数时,会自动执行以下步骤:

  1. 创建一个全新的空对象。
  2. 这个新对象的 [[Prototype]](即 __proto__)被链接到构造函数的 prototype 对象。
  3. 这个新对象被绑定为函数调用的 this
  4. 如果函数没有显式返回一个对象,那么 new 表达式会自动返回这个新创建的对象(this)。

示例:

javascript
function Person(name) {
  // this 被 new 绑定到了一个新创建的空对象上
  this.name = name;
  this.age = 30;
  // 隐式返回 this
}

const eve = new Person('Eve');
console.log(eve.name); // 输出 "Eve"
console.log(eve.age);  // 输出 30

在这种情况下,this 指向由 new 创建的实例对象 eve


5. 箭头函数 (Arrow Functions)

箭头函数是 ES6 中引入的一个重要特性,它彻底改变了 this 的规则。

  • 箭头函数没有自己的 this
  • 它会捕获其定义时所在上下文(词法作用域)的 this 值作为自己的 this
  • 一旦绑定,this 的值就不会再改变。call, apply, bind 对箭头函数无效。

这使得箭头函数在处理回调函数时非常有用。

示例对比:

javascript
// 传统函数的问题
const student = {
  name: 'Frank',
  courses: ['Math', 'Science'],
  printCourses: function() {
    setTimeout(function() {
      // 这里的 this 指向 window (默认绑定),因为 setTimeout 的回调是独立调用的
      console.log(this.name + ' is taking ' + this.courses); // 导致错误
    }, 1000);
  }
};
// student.printCourses(); // 非严格模式下会输出 " is taking undefined"

// 使用箭头函数解决
const student_arrow = {
  name: 'Frank',
  courses: ['Math', 'Science'],
  printCourses: function() {
    // 箭头函数定义时,外层作用域是 printCourses 函数
    // printCourses 的 this 指向 student_arrow (隐式绑定)
    // 所以箭头函数捕获的 this 就是 student_arrow
    setTimeout(() => {
      console.log(this.name + ' is taking ' + this.courses);
    }, 1000);
  }
};
student_arrow.printCourses(); // 1秒后输出 "Frank is taking Math,Science"

优先级总结

当多种规则同时适用时,this 指向的优先级如下:

new 绑定 > 显式绑定 (bind) > 隐式绑定 > 默认绑定

箭头函数的 this 规则是独立的,它不遵循上述优先级链,因为它根本没有自己的this。如果一个 this 已经被箭头函数捕获,那么任何方式都无法再改变它。

总结表格

调用方式 this 指向 示例
普通函数调用 全局对象 (window) 或 undefined (严格模式) myFunction()
对象方法调用 调用该方法的对象 (点前面的对象) obj.myMethod()
call/apply/bind 显式指定的第一个参数 myFunc.call(someObj)
new 构造函数 新创建的实例对象 new MyConstructor()
箭头函数 定义时所在词法作用域的 this const fn = () => { console.log(this) }

记住这个优先级和这五种场景,你就能准确判断绝大多数情况下 this 的指向了。

00:00
00:00