返回

重新认识this:解锁JavaScript指针的秘密

前端

this的指向:函数调用位置与声明位置的博弈

在JavaScript中,this的指向取决于函数的调用位置和声明位置。函数的调用位置是指函数被调用的地方,而函数的声明位置是指函数被定义的地方。当函数在某个对象上调用时,this指向该对象;当函数在全局作用域中调用时,this指向window对象。

function Person(name) {
  this.name = name;

  this.greet = function() {
    console.log(`Hello, my name is ${this.name}!`);
  };
}

const person = new Person('John');
person.greet(); // Hello, my name is John!

在这个例子中,当Person函数在new的调用下被实例化时,this指向新创建的person对象。因此,当greet方法被调用时,this指向person对象,并且Hello, my name is John!被输出到控制台。

但是,如果我们直接调用Person函数,而不使用new关键字,this的指向就会发生变化。

Person('John'); // TypeError: Cannot set properties of undefined (setting 'name')

在这个例子中,Person函数在全局作用域中被调用,因此this指向window对象。但是,window对象没有name属性,因此当我们试图给window对象设置name属性时,就会抛出一个TypeError异常。

new关键字的绑定魔法

new关键字是JavaScript中用于创建对象的一个非常重要的运算符。当我们使用new关键字调用一个函数时,该函数会自动绑定this到新创建的对象上。这种绑定称为显式绑定,因为它是在函数调用时明确指定的。

function Person(name) {
  this.name = name;

  this.greet = function() {
    console.log(`Hello, my name is ${this.name}!`);
  };
}

const person = new Person('John');
person.greet(); // Hello, my name is John!

在这个例子中,new关键字显式地将this绑定到person对象上,因此当greet方法被调用时,this指向person对象,并且Hello, my name is John!被输出到控制台。

call/apply/bind:灵活操控this指向的工具

call、apply和bind都是JavaScript中用于绑定this的函数。它们允许我们显式地指定函数的this指向。

call和apply函数都接受两个参数:第一个参数是this指向的对象,第二个参数是一个参数数组。当函数被调用时,this指向第一个参数,并且函数的参数被传递给第二个参数。

function Person(name) {
  this.name = name;

  this.greet = function() {
    console.log(`Hello, my name is ${this.name}!`);
  };
}

const person = new Person('John');
Person.greet.call(person); // Hello, my name is John!

在这个例子中,greet方法被显式地绑定到person对象上,因此当greet方法被调用时,this指向person对象,并且Hello, my name is John!被输出到控制台。

bind函数与call和apply类似,但它返回一个新的函数,该函数的this指向被绑定到第一个参数。这允许我们稍后调用该函数,而无需显式地传递this指向。

function Person(name) {
  this.name = name;

  this.greet = function() {
    console.log(`Hello, my name is ${this.name}!`);
  };
}

const person = new Person('John');
const greet = person.greet.bind(person);
greet(); // Hello, my name is John!

在这个例子中,greet方法被显式地绑定到person对象上,因此当greet方法被调用时,this指向person对象,并且Hello, my name is John!被输出到控制台。

隐式绑定:在对象方法中的自动指向

当一个函数在对象的方法中被调用时,this自动指向该对象。这种绑定称为隐式绑定,因为它是在函数定义时自动发生的。

function Person(name) {
  this.name = name;

  this.greet = function() {
    console.log(`Hello, my name is ${this.name}!`);
  };
}

const person = new Person('John');
person.greet(); // Hello, my name is John!

在这个例子中,当greet方法在person对象上被调用时,this自动指向person对象,因此Hello, my name is John!被输出到控制台。

默认绑定:全局作用域中的最后一道防线

当一个函数在全局作用域中被调用时,this指向window对象。这种绑定称为默认绑定,因为它是在函数调用时如果没有其他绑定的情况下发生的。

function greet() {
  console.log(`Hello, my name is ${this.name}!`);
}

greet(); // Hello, my name is undefined!

在这个例子中,greet函数在全局作用域中被调用,因此this指向window对象。但是,window对象没有name属性,因此当我们试图访问window对象