Javascript:call(),apply()和bind()

42次阅读

共计 3044 个字符,预计需要花费 8 分钟才能阅读完成。

What’ s“this”
在面向对象的 JS 中,我们了解到在 JS 中,一切都是对象。因为一切都是对象,我们开始明白我们可以为函数设置和访问其他属性。而 this 提供了一种更优雅的方式隐式“传递”一个对象的引用。关于 this,我们常见的误区:
认为 this 指向函数本身
function f1() {
console.log(this)
}
f1() // window
认为 this 指向函数作用域
关于这两个误区,其实在官方文档中有很明确的说明。
函数的调用方式决定了 this 的值。
那么 this 并不是指向函数本身,也不是指向函数的作用域,而是在运行时进行绑定的,并不是在编写时绑定,它的上下文也取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
它指向什么完全取决于函数在哪里被调用,也就是说,谁调用,谁负责
而 bind()、apply()、call()则是可以更改 this 指向的方法
(这部分并不是翻译的原文,因为原文关于 this 讲的不是很细致,而我觉得理解了 this,才能了解 bind()、apply()、call()有什么用处。所以这块是我根据《你不知道的 JavaScript 上卷》this 的部分,粗略的介绍了部分,有兴趣的话可以看看那本书,关于 this 讲的很细致。)
What’ s“this”
在面向对象的 JS 中,我们了解到在 JS 中,一切都是对象。因为一切都是对象,我们开始明白我们可以为函数设置和访问其他属性。而 this 提供了一种更优雅的方式隐式“传递”一个对象的引用。关于 this,我们常见的误区:
认为 this 指向函数本身
function f1() {
console.log(this)
}
f1() // window
认为 this 指向函数作用域
关于这两个误区,其实在官方文档中有很明确的说明。
函数的调用方式决定了 this 的值。
那么 this 并不是指向函数本身,也不是指向函数的作用域,而是在运行时进行绑定的,并不是在编写时绑定,它的上下文也取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
它指向什么完全取决于函数在哪里被调用,也就是说,谁调用,谁负责
而 bind()、apply()、call()则是可以更改 this 指向的方法
(这部分并不是翻译的原文,因为原文关于 this 讲的不是很细致,而我觉得理解了 this,才能了解 bind()、apply()、call()有什么用处。所以这块是我根据《你不知道的 JavaScript 上卷》this 的部分,粗略的介绍了部分,有兴趣的话可以看看那本书,关于 this 讲的很细致。)
call(), apply() and bind()
到目前为止,我们已将函数视为由名称(可选,也可以是匿名函数)组成的对象及其在调用时执行的代码。但这不是全部真相,实际上函数看起来更接近下面的图像:

现在,我将通过示例介绍每个函数中出现的这 3 种类似方法。
bind()
官方文档说:bind()方法创建一个新函数,当调用时,将其关键字设置为提供的值。
这非常强大。它让我们在调用函数时明确定义它的值。我们来看看 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!’
让我们分解吧。当我们使用 bind()方法时:

JS 引擎正在创建一个新的 pokemonName 实例,并将 pokemon 绑定为此变量。重要的是要理解它复制了 pokemonName 函数。
在创建了 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()方法调用具有给定此值的函数和单独提供的参数。
这意味着,我们可以调用任何函数,并在调用函数中明确指定 what_ this _should reference_。真的类似于 bind()方法!这绝对可以让我们免于编写 hacky 代码。
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
这些内置的方法存在于每个 JS 函数中都非常有用。即使你最终没有在日常编程中使用它们,你仍然会在阅读其他人的代码时经常遇到它们。

正文完
 0