回顾一下 “this”

我们了解到, 在面向对象的 JS 中, 一切都是对象。 因为一切都是对象, 我们开始明白我们可以为函数设置并访问额外的属性。

通过原型给函数设置属性并且添加其他方法非常棒…**但是我们如何访问它们? ! ? ? ? ! **

当他说 “myself” 时, 他的确意味着 ‘this’

我们介绍过 this 关键字。 我们了解到每个函数都会自动获取此属性。 所以这时, 如果我们创建一个有关我们函数执行上下文的抽象模型(我不是唯一一个这么做的人! … 对吗? ! ? ! ), 它看起来就会像这样:

我们花了一些时间来熟悉 this 关键字, 但是一旦我们这样做了, 我们就开始意识到它是多么有用了。 this 在函数内部使用, 并且总是引用单个对象 — 这个对象会在使用 “this” 的地方调用函数

但是生活肯定都不是完美的。 有时候我们会失去 this 的引用。 当这种情况发生时, 我们最终使用了令人困惑的解决方法去保存我们对于 this 的引用。 让我们通过 localSorage 练习来看看这个方法吧:

第 31 行 :(

那为什么我需要保存 this 引用呢? 因为在 deleteBtn.addEventListener 中, this 指向了 deleteBtn 对象。 这并不太好。 有更好的解决方案吗?


call()、 apply() 和 bind() — 一个新的希望

到目前为止, 我们已将函数视为由名称(可选, 也可以是匿名函数)及其在调用时执行的代码所组成的对象。 但这并不是全部真相。 作为一个 热爱真理的人, 我必须让你知道一个函数实际上看起来更接近下面的图像:

这是什么? ? ? ? ? ? ? 别担心! 现在, 我将通过示例介绍每个函数中出现的这 3 种类似方法。 真是很让人兴奋呢!

bind()

官方文档说: ** bind() ** 方法创建一个新函数, 在调用时, 将其 this 关键字设置为所需的值。 (它实际上谈论了更多的东西, 但我们将把它留到下一次讲)

这非常强大。 它让我们在调用函数时明确定义 this 的值。 我们来看看 cooooode:

var pokemon = {
    firstname: 'Pika',
    lastname: 'Chu ',
    getPokeName: function() {
        var fullname = this.firstname + ' ' + this.lastname;
        return fullname;
    }
};

var pokemonName = function() {
    console.log(this.getPokeName() + 'I choose you!');
};

var logPokemon = pokemonName.bind(pokemon); // creates new object and binds pokemon. 'this' of pokemon === pokemon now

logPokemon(); // 'Pika Chu I choose you!'

在第 14 行使用了 bind()方法

**我们来逐个分析。 ** 当我们使用了 bind() 方法:

  1. JS 引擎创建了一个新的 pokemonName 的实例, 并将 pokemon 绑定到 this 变量。 重要的是要理解**它复制了 pokemonName 函数。 **
  2. 在创建了 pokemonName 函数的副本之后, 它可以调用 logPokemon() 方法, 尽管它最初不在 pokemon 对象上。 它现在将识别其属性(Pika 和 Chu)及其方法。

很酷的是, 在我们 bind() 一个值后, 我们可以像使用任何其他正常函数一样使用该函数。 我们甚至可以更新函数来接受参数, 并像这样传递它们:

var pokemon = {
    firstname: 'Pika',
    lastname: 'Chu ',
    getPokeName: function() {
        var fullname = this.firstname + ' ' + this.lastname;
        return fullname;
    }
};

var pokemonName = function(snack, hobby) {
    console.log(this.getPokeName() + 'I choose you!');
    console.log(this.getPokeName() + ' loves ' + snack + ' and ' + hobby);
};

var logPokemon = pokemonName.bind(pokemon); // creates new object and binds pokemon. 'this' of pokemon === pokemon now

logPokemon('sushi', 'algorithms'); // Pika Chu  loves sushi and algorithms

call(), apply()

call() 方法的官方文档说: ** call() ** 方法调用一个给定 this 值的函数, 并单独提供参数。

这意味着, 我们可以调用任何函数, 并明确指定 this 应该在调用函数中引用的内容。 真的类似于 bind() 方法! 这绝对可以让我们免于编写 hacky 代码(即使我们仍然是 hackerzzz)。

bind()call() 之间的主要区别在于 call() 方法:

  1. 支持接受其他参数
  2. 当它被调用的时候, 立即执行函数。
  3. call() 方法不会复制正在调用它的函数。

call()apply() 使用于**完全相同的目的。 ** 它们工作方式之间的唯一区别call() 期望所有参数都单独传递, 而 apply() 需要所有参数的数组。 例如:

var pokemon = {
    firstname: 'Pika',
    lastname: 'Chu ',
    getPokeName: function() {
        var fullname = this.firstname + ' ' + this.lastname;
        return fullname;
    }
};

var pokemonName = function(snack, hobby) {
    console.log(this.getPokeName() + ' loves ' + snack + ' and ' + hobby);
};

pokemonName.call(pokemon, 'sushi', 'algorithms'); // Pika Chu  loves sushi and algorithms
pokemonName.apply(pokemon, ['sushi', 'algorithms']); // Pika Chu  loves sushi and algorithms

注意, apply 接受数组, call 接受每个单独的参数。

这些存在于每一个 JS 函数的内置方法都非常有用。 即使你最终没有在日常编程中使用它们, 你仍然会在阅读其他人的代码时经常遇到它们。

如果您有任何疑问, 请一如既往地通过 Instagram 与我们联系。 ❤

如果发现译文存在错误或其他需要改进的地方, 欢迎到 掘金翻译计划 对译文进行修改并 PR, 也可获得相应奖励积分。 文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区, 文章来源为 掘金 上的英文分享文章。 内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域, 想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏