【Javascript】如何使用 this 关键字,call()、bind()、apply() 方法

学习 call(), apply(), 和 bind() 方法是很重要的,因为它们让你能够控制 JavaScript 中 this 的上下文。在某些情况下,默认的 this 行为可能不符合预期,比如当你从一个对象借用方法到另一个对象,或者是在回调函数中保持正确的上下文时,这些方法提供了灵活性和控制力。通过掌握它们,你可以编写出更加高效、可复用并且上下文感知的函数,这在复杂的应用中尤其有用。

在我们深入了解 call(), apply(), 和 bind() 方法之前,先来了解一下 'this' 关键字及其工作机制。

'this' 关键字

让我们通过下面的要点来理解什么时候以及 this 关键字指的是什么:

  • 在一个对象的方法中, this 指向该对象。在对象内部定义的方法中,this 将指向拥有该方法的对象。

  • 在一个普通函数中, this 指向全局对象。在非严格模式下,如果函数是在全局上下文中调用(而不是作为对象的方法),this 指向的是全局对象(在浏览器中是 window)。

  • 在严格模式下的函数中, thisundefined。如果函数不是某个对象的方法,并且没有绑定到特定上下文(通过 call, apply, 或 bind),那么在严格模式下 this 将是 undefined

  • 在事件处理程序中, this 指向接收事件的元素。当触发事件时,this 指向的是触发事件的 HTML 元素。

    <button onclick="this.style.display='none'">
      点击移除我!
    </button>
    

    在这里,this 指向的是接收到 onclick 事件的按钮元素本身。

在箭头函数中, this 的行为有所不同。箭头函数没有自己的 this 上下文。相反,this 以词法形式继承自创建箭头函数时周围的上下文。这意味着箭头函数内部的 this 指向的是其外部函数或上下文的 this 值。

const person = {
  name: "Alice",
  greet: function() {
    setTimeout(() => {
      console.log(`嗨,我是 ${this.name}`);
    }, 1000);
  }
};

person.greet(); // 输出: 嗨,我是 Alice

在这个例子中,setTimeout 内部的箭头函数继承了 greet 方法的 this,后者指向 person 对象。

call() 方法

call() 方法允许你“借用”一个对象上的函数或方法,并用另一个对象来使用它,只需把另一个对象作为第一个参数传递即可。第一个参数成为了函数内部的 this 值,而其他参数紧随其后。

call() 方法并不会创建新的函数;它只是用提供的上下文和参数运行现有的函数。

const person = {
  fullName: function(city, country) {
    console.log(this.firstName + " " + this.lastName + " 正准备去 " + city + ", " + country + ".");
  }
}

const person1 = {
  firstName: "John",
  lastName: "Doe"
}

person.fullName.call(person1, "Oslo", "Norway");
// 输出: John Doe 正准备去 Oslo, Norway.

在这个例子中,call() 被用来执行 person 对象中的 fullName 方法,但是使用了 person1 的数据(firstNamelastName),附加的参数则是 “Oslo” 和 “Norway”。

apply() 方法

apply() 方法和 call() 方法非常相似。主要区别在于参数是如何传递给函数的。使用 apply() 时,你可以将参数作为一个数组(或类数组对象)传递,而不是单独传递。

call() 一样,apply() 方法也不会创建新的函数。它立即执行函数,使用提供的上下文 (this 值) 和参数。

const person = {
  fullName: function(city, country) {
    console.log(this.firstName + " " + this.lastName + " 正准备去 " + city + ", " + country + ".");
  }
}

const person1 = {
  firstName: "John",
  lastName: "Doe"
}

person.fullName.apply(person1, ["Oslo", "Norway"]);
// 输出: John Doe 正准备去 Oslo, Norway.

在这个例子中,apply() 被用来调用 person 对象中的 fullName 方法,但是上下文 (this) 设置为了 person1。参数 “Oslo” 和 “Norway” 作为一个数组传递。

bind() 方法

JavaScript 中的 bind() 方法让你能够设置一个函数或方法的上下文(this 值),就像 call()apply() 一样。然而,不同于 call()apply()bind() 方法不会立即调用函数。相反,它返回一个新的函数,其中 this 值被设置为你指定的对象。

const person = {
  fullName: function(city, country) {
    console.log(this.firstName + " " + this.lastName + " 正准备去 " + city + ", " + country + ".");
  }
}

const person1 = {
  firstName: "John",
  lastName: "Doe"
}

const func = person.fullName.bind(person1);
func("Oslo", "Norway");
// 输出: John Doe 正准备去 Oslo, Norway.

在这个例子中,bind() 创建了一个新的函数 func,其中 this 值被设置为 person1。这个函数不会立即被调用,但你可以稍后传入参数 “Oslo” 和 “Norway” 来调用它。

示例:带有多上下文的集中式记录器

这里有一个小型但复杂的应用示例,在这个例子中使用 call(), apply(), 或 bind() 可以提高效率——尤其是在处理用于记录目的的函数的部分应用时:

假设你有一个集中式的记录函数,用于记录不同用户执行的动作。使用 bind() 可以有效地将 this 上下文设置为不同的用户,避免了重复代码。

const logger = {
  logAction: function(action) {
    console.log(`${this.name} (ID: ${this.id}) 执行了: ${action}`);
  }
};

const user1 = { name: "Alice", id: 101 };
const user2 = { name: "Bob", id: 202 };

// 为不同的用户创建新的记录函数
const logForUser1 = logger.logAction.bind(user1);
const logForUser2 = logger.logAction.bind(user2);

// 执行动作时无需手动传递用户上下文
logForUser1("login");
// 输出: Alice (ID: 101) 执行了: login

logForUser2("purchase");
// 输出: Bob (ID: 202) 执行了: purchase

为什么这样更高效?

上下文重用: 每次记录动作时,你都不需要手动传递用户上下文。上下文 (this) 只绑定一次,使记录变得可重用且干净。

模块化: 如果你需要添加更多用户或动作,你可以快速地将它们绑定到 logger 上,而不必改变函数本身,使你的代码保持 DRY(不要做重复的工作)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值