JavaScript为什么要有this,this到底指的是谁?

109 阅读2分钟

前言

相信被代码中的this搞得晕头转向的时候,我们一定会好奇为什么要有this,这个this就非用不可吗?this到底传递的是哪一个对象引用? 如果你有上面的疑惑就把文章看完吧,相信你会找到答案!


为什么要有this

下面这段代码是我们都很熟悉的使用构造函数创建对象实例,但如果不用this该怎么写呢?

function Cloth(color,size){
    this.color=color;
    this.size=size;
}
const obj1=new Cloth("red","M");
console.log(obj1);//Cloth { color: 'red', size: 'M' }

最容易想到是

function Cloth2(color,size){
    const instance = {};
    instance.color = color;
    instance.size = size;
    return instance;
}
const obj2=new Cloth2("red","M");
console.log(obj2);//{ color: 'red', size: 'M' }

注意这样上面返回的是一个新对象,而不是Cloth类的实例

为什么会这样?

JavaScript 的类构造函数有一个特殊行为:

  • 如果构造函数返回一个对象,这个对象会替代默认创建的实例
  • 如果返回非对象值(或没有 return),则使用默认创建的实例

所以this为什么存在呢?

  1. this提供了一种更优雅的方式来隐式的传递一个对象引用,可以让代码更简洁
  2. this 是构造函数模式的核心机制,用于为新创建的对象实例绑定属性。

...

所以this不仅有用而且有些地方还没它不行。既然它这么重要,那我们继续学习它的使用规则。

this可以用在哪里

概况的说只有函数作用域和全局作用域中。

全局作用域

在全局作用域中,this 的指向:

  • 非严格模式this 指向全局对象(浏览器中为 window,Node.js 中为 global)。
  • 严格模式this 为 undefined
console.log(this); // 浏览器中输出: Window 对象

函数作用域

除了我们上面提到的构造函数还可以在对象方法等其它函数中。

const person = {
  name: 'Alice',
  greet() {
    console.log(`Hello, ${this.name}!`); // this → person 对象
  }
};
person.greet(); // 输出: "Hello, Alice!"

注意:箭头函数没有自己的 this,它会继承外层作用域的 this

const obj = {
  value: 42,
  getValue: () => {
    console.log(this.value); // this → 外层作用域的 this(可能是 window)
  }
};
obj.getValue(); // 输出: undefined(严格模式可能报错)

this的绑定规则

终于到我们的重头戏了,这是解决this到底指的是谁的关键。

1. 默认绑定

当函数独立调用时,this默认绑定到全局对象

function showThis() {
  console.log(this);
}

showThis(); 
// 非严格模式:Window 对象(浏览器)或 global 对象(Node.js)
// 严格模式:undefined

2. 隐式绑定

当函数作为对象的方法调用时,this 绑定到该对象。

const person = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, ${this.name}`);
  }
};

person.greet(); // "Hello, Alice"(this → person 对象)

注意:方法赋值给变量后会丢失 this 绑定:

const greetFunc = person.greet;
greetFunc(); // "Hello, undefined"(默认绑定)

3. 隐式丢失

当一个函数被多层对象调用时,函数的this指向指向最近的那层对象

4. 显示绑定

使用 call()apply()或 bind() 可以显式指定 this 的值。

function introduce(lang) {
  console.log(`I speak ${lang}. My name is ${this.name}`);
}

const person = { name: 'Bob' };

// call 和 apply 立即调用
introduce.call(person, 'English'); // "I speak English. My name is Bob"
introduce.apply(person, ['Spanish']); // 参数以数组传递

// bind 返回绑定后的函数
const boundFunc = introduce.bind(person, 'French');
boundFunc(); // "I speak French. My name is Bob"

5.new绑定

使用 new 调用构造函数时,this 绑定到新创建的对象。

绑定规则的优先级

当多个规则同时适用时,优先级从高到低为:

  1. new 绑定new Foo()
  2. 显式绑定call/apply/bind
  3. 隐式绑定obj.method()
  4. 默认绑定(独立函数调用)
function foo() {
  console.log(this.a);
}

const obj1 = { a: 1, foo: foo };
const obj2 = { a: 2 };

obj1.foo.call(obj2); // 2(显式绑定 > 隐式绑定)
new obj1.foo(); // undefined(new 绑定 > 隐式绑定)