您好,登錄后才能下訂單哦!
小編給大家分享一下Javascript如何使用.call()和.apply()編寫更好的代碼,希望大家閱讀完這篇文章之后都有所收獲,下面讓我們一起去探討吧!
Javascript函數(shù)對(duì)象的原型公開了兩個(gè)有價(jià)值的方法,分別是call()和apply()。
首先,讓我們了解每種方法的作用。
call()函數(shù)用于通過為其提供的this
的上下文來調(diào)用函數(shù)。它允許我們通過在特定函數(shù)內(nèi)顯式提供用于this
事件的對(duì)象來調(diào)用函數(shù)。
為了更好地了解為什么存在這種方法,請(qǐng)考慮以下示例:
function sayHello() {
console.log(`Hello, ${this.name}`);
}
sayHello(); // Hello, undefined
如你所見,this
函數(shù)內(nèi)部指的是全局作用域。在上面的代碼中,sayHello
函數(shù)試圖在全局范圍內(nèi)查找名為name
變量。由于不存在這樣的變量,它打印出undefined
. 如果我們定義了一個(gè)在全局范圍內(nèi)調(diào)用的name
變量,該函數(shù)將按預(yù)期工作,如下所示:
const name = 'archeun';
function sayHello() {
console.log(`Hello, ${this.name}`);
}
sayHello(); // Hello, archeun
如果我們嚴(yán)格在上面的代碼中使用了模式,它實(shí)際上會(huì)拋出一個(gè)運(yùn)行時(shí)錯(cuò)誤,因?yàn)?code>this將是未定義的。
這里的缺點(diǎn)是sayHello
函數(shù)假定this
變量的范圍,我們無法控制它。根據(jù)我們執(zhí)行它的詞法范圍,該函數(shù)的行為會(huì)有所不同。這時(shí)候call()
方法派上用場(chǎng)了。如你所知,它允許我們顯式注入我們需要用于this
函數(shù)內(nèi)部變量的對(duì)象:
考慮下面的例子:
const name = 'archeun';
function sayHello() {
console.log(`Hello, ${this.name}`);
}
sayHello(); // Hello, archeun
const visitor = {
name: 'My lord!'
}
/**
* The first parameter of the call method is,
* the object to be used for the `this` context inside the function.
* So when the `sayHello` function encounters `this.name`, it now knows
* to refer to the `name` key of the `visitor` object we passed
* to the `call` method.
*/
sayHello.call(visitor); // Hello, My lord!
/**
* Here we do not provide the `this` context.
* This is identical to calling sayHello().
* The function will assume the global scope for `this`.
*/
sayHello.call(); // Hello, archeun
除了this
作為方法的第一個(gè)參數(shù)傳遞的上下文之外,call()
還接受被調(diào)用函數(shù)的參數(shù)。在第一個(gè)參數(shù)之后,我們傳遞給call()
方法的所有其他參數(shù)都將作為參數(shù)傳遞給被調(diào)用函數(shù)。
function sayHello(greetingPrefix) {
console.log(`${greetingPrefix}, ${this.name}`);
}
const visitor = {
name: 'My lord!'
}
/**
* Here `Hello` will be passed as the argument to the
* `greetingPrefix` parameter of the `sayHello` function
*/
sayHello.call(visitor, 'Hello'); // Hello, My lord!
/**
* Here `Howdy` will be passed as the argument to the
* `greetingPrefix` parameter of the `sayHello` function
*/
sayHello.call(visitor, 'Howdy'); // Howdy, My lord!
我們可以編寫一個(gè)函數(shù)并在不同的this
上下文中調(diào)用它:
function sayHello(greetingPrefix) {
console.log(`${greetingPrefix}, ${this.name}`);
}
const member = {
name: 'Well-known member'
}
const guest = {
name: 'Random guest'
}
/**
* `sayHello` function will refer to the `member` object
* whenever it encouneters `this`
*/
sayHello.call(member, 'Hello'); // Hello, Well-known member
/**
* `sayHello` function will refer to the `guest` object
* whenever it encouneters `this`
*/
sayHello.call(guest, 'Howdy'); // Howdy, Random guest
如你所見,如果使用得當(dāng),這會(huì)提高代碼的可重用性和可維護(hù)性。
我們可以使用call()
方法來鏈接通過函數(shù)創(chuàng)建的對(duì)象的構(gòu)造函數(shù)。使用該函數(shù)創(chuàng)建對(duì)象時(shí),函數(shù)可以采用另一個(gè)函數(shù)作為其構(gòu)造函數(shù)。如下例所示,Dog
和Fish
都調(diào)用Animal
函數(shù)來初始化它們的公共屬性,即name
和noOfLegs
:
function Animal(name, noOfLegs) {
this.name = name;
this.noOfLegs = noOfLegs;
}
function Dog(name, noOfLegs) {
// Reuse Animal function as the Dog constructor
Animal.call(this, name, noOfLegs);
this.category = 'mammals';
}
function Fish(name, noOfLegs) {
// Reuse Animal function as the Fish constructor
Animal.call(this, name, noOfLegs);
this.category = 'fish';
}
const tiny = new Dog('Tiny', 4);
const marcus = new Fish('Marcus', 0);
console.log(tiny); // {name: "Tiny", noOfLegs: 4, category: "mammals"}
console.log(marcus); // {name: "Marcus", noOfLegs: 0, category: "fish"}
這也是代碼重用的一種變體。這種模式還使我們能夠用其他語言編寫接近 OOP 原則的代碼。
匿名函數(shù)繼承調(diào)用它們的詞法作用域。我們可以使用call()
方法將this
作用域顯式注入匿名函數(shù)。考慮下面的例子:
const animals = [
{ type: 'Dog', name: 'Tiny', sound: 'Bow wow' },
{ type: 'Duck', name: 'Marcus', sound: 'Quack' }
];
for (let i = 0; i < animals.length; i++) {
/**
* This anonymous function now has access to each animal object
* through `this`.
*/
(function (i) {
this.makeSound = function () {
console.log(`${this.name} says ${this.sound}!`);
}
this.makeSound();
}).call(animals[i], i);
}
// Tiny says Bow wow!
// Marcus says Quack!
在這里,我們不必實(shí)現(xiàn)一個(gè)專門的函數(shù)來將makeSound
方法附加到每個(gè)動(dòng)物對(duì)象上。這使我們無法編寫和命名一次性使用的實(shí)用程序函數(shù)。
這些是我們可以有效地使用call()
方法使我們的代碼干凈、可重用和可維護(hù)的幾種方法。
apply()
在功能方面幾乎與call()
方法相同。唯一的區(qū)別是它接受一個(gè)類似數(shù)組的對(duì)象作為它的第二個(gè)參數(shù)。
/**
* After `this` context argument
* `call` accepts a list of individual arguments.
* Therefore, if `args` is an array, we can use the
* `ES6` spread operator to pass individual elements
* as the list of arguments
*/
func.call(context, ...args);
/**
* After `this` context argument
* `apply` accepts a single array-like object
* as its second argument.
*/
func.apply(context, args);
除了apply()
如何處理被調(diào)用方參數(shù)外,該功能與call()
方法相同。但是,由于這種差異,我們可以將其用于不同于call()
的用例。
Array.prototype.push
函數(shù)可用于將元素推送到數(shù)組的末尾。例如:
const numbers = [1, 2, 3, 4];
numbers.push('a', 'b', 'c'); // push elements on by one
console.log(numbers); // [1, 2, 3, 4, "a", "b", "c"]
如果你想將一個(gè)數(shù)組的所有元素推送到另一個(gè)數(shù)組,該怎么辦?像下面這樣:
const numbers = [1, 2, 3, 4];
const letters = ['a', 'b', 'c'];
numbers.push(letters);
console.log(numbers); // [1, 2, 3, 4, ["a", "b", "c"]]
這并不是我們想要的。它將整個(gè)字母數(shù)組作為單個(gè)元素附加到數(shù)字?jǐn)?shù)組。我們本可以使用concat()
方法,但它將創(chuàng)建數(shù)組的副本并返回它。我們也不需要。我們還可以在字母數(shù)組上循環(huán)并單獨(dú)推送每個(gè)元素。但還有一種更優(yōu)雅的方式:
const numbers = [1, 2, 3, 4];
const letters = ['a', 'b', 'c'];
numbers.push.apply(numbers, letters);
console.log(numbers); // [1, 2, 3, 4, "a", "b", "c"]
如果我們有特權(quán)使用ES6擴(kuò)展運(yùn)算符,我們可以通過這樣做來實(shí)現(xiàn)這一點(diǎn),
const numbers = [1, 2, 3, 4];
const letters = ['a', 'b', 'c'];
numbers.push(...letters);
console.log(numbers); // [1, 2, 3, 4, "a", "b", "c"]
對(duì)于任何接受參數(shù)列表的函數(shù),例如Math.max
我們可以有效地使用 apply
。考慮以下。
如果你想找出一組數(shù)字的最小值和最大值,下面是老派的做法:
let min = +Infinity;
let max = -Infinity;
const numbers = [4, 5, 1, 2, 8, 3, 4, 6, 3];
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] > max) {
max = numbers[i];
}
if (numbers[i] < min) {
min = numbers[i];
}
}
console.log(`Min: ${min}, Max: ${max}`); // Min: 1, Max: 8
我們可以apply()
以更優(yōu)雅的方式實(shí)現(xiàn)相同的效果,如下所示:
const numbers = [4, 5, 1, 2, 8, 3, 4, 6, 3];
min = Math.min.apply(null, numbers);
max = Math.max.apply(null, numbers);
console.log(`Min: ${min}, Max: ${max}`); // Min: 1, Max: 8
與前一種情況相同,如果我們可以使用ES6擴(kuò)展運(yùn)算符,我們可以通過執(zhí)行以下操作來實(shí)現(xiàn)相同的效果:
const numbers = [4, 5, 1, 2, 8, 3, 4, 6, 3];
min = Math.min(...numbers);
max = Math.max(...numbers);
console.log(`Min: ${min}, Max: ${max}`); // Min: 1, Max: 8
看完了這篇文章,相信你對(duì)“Javascript如何使用.call()和.apply()編寫更好的代碼”有了一定的了解,如果想了解更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。