Sự khác biệt giữa cuộc gọi và áp dụng là gì?


3105

Sự khác biệt giữa việc sử dụng callapplygọi một hàm là gì?

var func = function() {
  alert('hello!');
};

func.apply(); đấu với func.call();

Có sự khác biệt về hiệu suất giữa hai phương pháp đã nói ở trên không? Khi nào là tốt nhất để sử dụng callhơn applyvà ngược lại?


727
Hãy nghĩ đến aviệc áp dụng cho mảng args và ctrong cuộc gọi cho các cột của args.
Trận chiến

176
@LarryBattle Tôi cũng làm như vậy, nhưng tôi nghĩ rằng một ứng dụng cho mảng và c trong lệnh gọi dấu phẩy (tức là các đối số được phân tách bằng dấu phẩy).
Samih

3
Tôi đồng ý nó là ngu ngốc. Điều khó chịu là bằng cách nào đó câu hỏi này được hỏi trong các cuộc phỏng vấn bởi vì một số người có ảnh hưởng lớn đã thêm câu hỏi vào danh sách các câu hỏi js quan trọng của họ.
Ringo

6
Bạn nộp đơn xin việc một lần (một đối số), bạn [điện thoại] gọi cho mọi người nhiều lần (một vài đối số). Thay thế: có [quá?] Nhiều trò chơi Call of Duty.
Gras Double

1
Khi có ý định gọi một hàm matrixdic với một danh sách các giá trị đối số bất kể giá trị "này", sau đó sử dụng toán tử trải ES6, ví dụ như fn(...input)đầu vào là một mảng. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/iêu
Gajus

Câu trả lời:


3649

Sự khác biệt là applycho phép bạn gọi hàm với argumentsmột mảng; callyêu cầu các tham số được liệt kê rõ ràng. Một ghi nhớ hữu ích là " A for a rray và C for c omma."

Xem tài liệu của MDN về việc áp dụnggọi .

Cú pháp giả:

theFunction.apply(valueForThis, arrayOfArgs)

theFunction.call(valueForThis, arg1, arg2, ...)

Ngoài ra, kể từ ES6, khả năng spreadmảng được sử dụng với callhàm, bạn có thể thấy sự tương thích ở đây .

Mã mẫu:

function theFunction(name, profession) {
    console.log("My name is " + name + " and I am a " + profession +".");
}
theFunction("John", "fireman");
theFunction.apply(undefined, ["Susan", "school teacher"]);
theFunction.call(undefined, "Claude", "mathematician");
theFunction.call(undefined, ...["Matthew", "physicist"]); // used with the spread operator


25
Một điều cần thêm là các đối số phải là một mảng số ([]). Mảng kết hợp ({}) sẽ không hoạt động.
Kevin Schroeder

327
@KevinSchroeder: Trong cách nói javascript, []được gọi là một mảng , {}được gọi là một đối tượng .
Martijn

89
Tôi thường sử dụng để quên mất một mảng và mong bạn liệt kê các đối số. Một kỹ thuật tôi thường nhớ đó là nếu chữ cái đầu tiên của phương thức bắt đầu bằng một thì nó sẽ lấy một mảng tức là một mảng pply
aziz trừng phạt

16
@SAM Sử dụng cuộc gọi thay vì cuộc gọi chức năng thông thường chỉ có ý nghĩa nếu bạn cần thay đổi giá trị của cuộc gọi này cho cuộc gọi chức năng. Một ví dụ (chuyển đổi một đối số hàm-đối tượng thành một mảng): Array.prototype.slice.call(arguments)hoặc [].slice.call(arguments). áp dụng có ý nghĩa nếu bạn có các đối số trong một mảng, ví dụ trong một hàm gọi một hàm khác có (gần như) các tham số tương tự. Khuyến nghị Sử dụng một cuộc gọi chức năng bình thường funcname(arg1)nếu đó là những gì bạn cần, và lưu cuộc gọiáp dụng cho những dịp đặc biệt khi bạn thực sự cần chúng.
một số

4
@KunalSingh Cả hai callapplycó hai tham số. Đối số đầu tiên của apply' and hàm call` phải là đối tượng chủ sở hữu và tham số thứ hai sẽ là các tham số được phân tách bằng mảng hoặc dấu phẩy tương ứng. Nếu bạn vượt qua nullhoặc undefinedlà đối số đầu tiên thì trong chế độ không nghiêm ngặt, chúng sẽ được thay thế bằng đối tượng toàn cầu, tức làwindow
AJ Qarshi

229

K. Scott Allen có một bài viết hay về vấn đề này.

Về cơ bản, chúng khác nhau về cách chúng xử lý các đối số chức năng.

Phương thức áp dụng () giống hệt với lệnh gọi (), ngoại trừ áp dụng () yêu cầu một mảng làm tham số thứ hai. Mảng đại diện cho các đối số cho phương thức đích. "

Vì thế:

// assuming you have f
function f(message) { ... }
f.call(receiver, "test");
f.apply(receiver, ["test"]);

42
tham số thứ hai của áp dụng () và gọi () là tùy chọn, không bắt buộc.
kiwi tức giận

34
Tham số đầu tiên là không cần thiết quá.
Ikrom

@Ikrom, tham số đầu tiên không bắt buộc callnhưng yêu cầu đối với apply
iamcastelli

160

Để trả lời phần về thời điểm sử dụng từng hàm, hãy sử dụng applynếu bạn không biết số lượng đối số bạn sẽ truyền hoặc nếu chúng đã ở trong một đối tượng giống như mảng hoặc mảng (như argumentsđối tượng để chuyển tiếp các đối số của riêng bạn. Sử dụng callkhác, vì không cần phải bọc các đối số trong một mảng.

f.call(thisObject, a, b, c); // Fixed number of arguments

f.apply(thisObject, arguments); // Forward this function's arguments

var args = [];
while (...) {
    args.push(some_value());
}
f.apply(thisObject, args); // Unknown number of arguments

Khi tôi không chuyển bất kỳ đối số nào (như ví dụ của bạn), tôi thích callvì tôi đang gọi hàm. applysẽ ngụ ý rằng bạn đang áp dụng hàm cho các đối số (không tồn tại).

Không nên có bất kỳ sự khác biệt về hiệu năng, ngoại trừ có thể nếu bạn sử dụng applyvà bọc các đối số trong một mảng (ví dụ f.apply(thisObject, [a, b, c])thay vì f.call(thisObject, a, b, c)). Tôi chưa thử nó, vì vậy có thể có sự khác biệt, nhưng nó sẽ rất cụ thể cho trình duyệt. Nó có thể callnhanh hơn nếu bạn không có các đối số trong một mảng và applynhanh hơn nếu bạn làm như vậy.


111

Đây là một ghi nhớ tốt. Một pply sử dụng A rrays và A lways mất một hoặc hai đối số. Khi bạn sử dụng C tất cả các bạn phải C ount số các đối số.


2
Ghi nhớ hữu ích ngay đó!. Tôi sẽ thay đổi 'một hoặc hai đối số' để nói 'tối đa hai đối số' vì không applyyêu cầu tham số thứ nhất hoặc thứ hai . Tôi không chắc chắn tại sao người ta sẽ gọi applyhoặc callkhông có tham số. Có vẻ như ai đó đang cố gắng tìm hiểu lý do tại sao stackoverflow.com/questions/15903782/iêu
dantheta

92

Mặc dù đây là một chủ đề cũ, tôi chỉ muốn chỉ ra rằng .call nhanh hơn một chút so với .apply. Tôi không thể cho bạn biết chính xác tại sao.

Xem jsPerf, http://jsperf.com/test-call-vs-apply/3


[ UPDATE!]

Douglas Crockford đề cập ngắn gọn về sự khác biệt giữa hai loại, điều này có thể giúp giải thích sự khác biệt về hiệu suất ... http://youtu.be/ya4UHuXNygM?t=15m52s

Áp dụng mất một loạt các đối số, trong khi Cuộc gọi không có hoặc nhiều tham số riêng lẻ! À!

.apply(this, [...])

.call(this, param1, param2, param3, param4...)


Điều này phụ thuộc vào chức năng làm gì với các tham số / mảng, nếu nó không cần xử lý mảng, nó có mất ít thời gian hơn không?
Eric Hodonsky

12
Điều thú vị là ngay cả khi không có mảng, cuộc gọi vẫn nhanh hơn nhiều. jsperf.com/applyvscallvsfn2
Josh Mc

@JoshMc Điều đó sẽ rất cụ thể cho trình duyệt. Trong IE 11, tôi sẽ nhận được ứng dụng nhanh gấp đôi cuộc gọi.
Vincent McNabb

1
1. Tạo một mảng mới có nghĩa là trình thu gom rác sẽ cần phải dọn sạch nó tại một số điểm. 2. Truy cập các mục trong mảng bằng cách sử dụng tính hiệu quả sẽ kém hiệu quả hơn so với truy cập trực tiếp vào một biến (tham số). (Tôi tin rằng đó là ý nghĩa của kmatheny khi "phân tích cú pháp", đây thực sự là một điều gì đó khá khác biệt.) Nhưng cả hai lập luận của tôi đều không giải thích được jsperf. Điều đó phải liên quan đến việc triển khai hai chức năng của động cơ, ví dụ có lẽ chúng tạo ra một mảng trống, nếu không có gì được thông qua.
joeytwiddle

Cảm ơn bạn đã chia sẻ bài kiểm tra và video
Gary

76

Theo một trích đoạn từ Đóng cửa: Hướng dẫn dứt khoát của Michael Bolin . Nó có thể trông hơi dài, nhưng nó bão hòa với nhiều cái nhìn sâu sắc. Từ "Phụ lục B. Các khái niệm JavaScript thường bị hiểu sai":


Điều gì đề thiscập đến khi một chức năng được gọi

Khi gọi một hàm của biểu mẫu foo.bar.baz(), đối tượng foo.barđược gọi là người nhận. Khi hàm được gọi, nó là máy thu được sử dụng làm giá trị cho this:

var obj = {};
obj.value = 10;
/** @param {...number} additionalValues */
obj.addValues = function(additionalValues) {
  for (var i = 0; i < arguments.length; i++) {
    this.value += arguments[i];
  }
  return this.value;
};
// Evaluates to 30 because obj is used as the value for 'this' when
// obj.addValues() is called, so obj.value becomes 10 + 20.
obj.addValues(20);

Nếu không có máy thu rõ ràng khi một hàm được gọi, thì đối tượng toàn cục sẽ trở thành máy thu. Như đã giải thích trong "goog.global" trên trang 47, cửa sổ là đối tượng toàn cầu khi JavaScript được thực thi trong trình duyệt web. Điều này dẫn đến một số hành vi đáng ngạc nhiên:

var f = obj.addValues;
// Evaluates to NaN because window is used as the value for 'this' when
// f() is called. Because and window.value is undefined, adding a number to
// it results in NaN.
f(20);
// This also has the unintentional side effect of adding a value to window:
alert(window.value); // Alerts NaN

Mặc dù obj.addValuesfđề cập đến cùng một chức năng, chúng hoạt động khác nhau khi được gọi vì giá trị của người nhận khác nhau trong mỗi cuộc gọi. Vì lý do này, khi gọi một hàm đề cập đến this, điều quan trọng là phải đảm bảo rằng nó thissẽ có giá trị chính xác khi nó được gọi. Để rõ ràng, nếu thiskhông được tham chiếu trong thân hàm, thì hành vi của f(20)obj.addValues(20)sẽ giống nhau.

Vì các hàm là các đối tượng hạng nhất trong JavaScript, nên chúng có thể có các phương thức riêng. Tất cả các hàm đều có các phương thức call()apply()có thể xác định lại người nhận (nghĩa là đối tượng thistham chiếu) khi gọi hàm. Các chữ ký phương thức như sau:

/**
* @param {*=} receiver to substitute for 'this'
* @param {...} parameters to use as arguments to the function
*/
Function.prototype.call;
/**
* @param {*=} receiver to substitute for 'this'
* @param {Array} parameters to use as arguments to the function
*/
Function.prototype.apply;

Lưu ý rằng sự khác biệt duy nhất giữa call()apply()call()nhận các tham số hàm dưới dạng các đối số riêng lẻ, trong khi apply()nhận chúng dưới dạng một mảng duy nhất:

// When f is called with obj as its receiver, it behaves the same as calling
// obj.addValues(). Both of the following increase obj.value by 60:
f.call(obj, 10, 20, 30);
f.apply(obj, [10, 20, 30]);

Các cuộc gọi sau là tương đương, fobj.addValuestham chiếu đến cùng chức năng:

obj.addValues.call(obj, 10, 20, 30);
obj.addValues.apply(obj, [10, 20, 30]);

Tuy nhiên, vì call()cũng không apply()sử dụng giá trị của máy thu của chính nó để thay thế cho đối số người nhận khi nó không được chỉ định, nên những điều sau đây sẽ không hoạt động:

// Both statements evaluate to NaN
obj.addValues.call(undefined, 10, 20, 30);
obj.addValues.apply(undefined, [10, 20, 30]);

Giá trị của thiskhông bao giờ có thể nullhoặc undefinedkhi một hàm được gọi. Khi nullhoặc undefinedđược cung cấp làm người nhận đến call()hoặc apply(), đối tượng toàn cầu được sử dụng làm giá trị cho người nhận thay thế. Do đó, mã trước đó có cùng tác dụng phụ không mong muốn là thêm thuộc tính được đặt tên valuevào đối tượng toàn cục.

Có thể hữu ích khi nghĩ về một hàm là không có kiến ​​thức về biến được gán. Điều này giúp củng cố ý tưởng rằng giá trị của điều này sẽ bị ràng buộc khi hàm được gọi thay vì khi nó được xác định.


Kết thúc chiết xuất.


Chỉ cần lưu ý thực tế, điều đó additionalValueskhông được tham chiếu bên trong obj.addValuescơ thể
Viktor Stolbin

Tôi biết bạn đã trả lời câu hỏi nhưng muốn thêm: bạn có thể đã sử dụng liên kết khi xác định f. var f = obj.addValues;trở thành var f = obj.addValues.bind(obj) và bây giờ f (20) sẽ hoạt động mà không phải sử dụng cuộc gọi hoặc áp dụng mọi lúc.
jhliberty

Tôi biết bạn đã không viết nó, nhưng bạn đã làm nổi bật văn bản và ví dụ từ cuốn sách có liên quan, và tôi rất biết ơn. Họ đã rất hữu ích.
Fralcon

34

Đôi khi nó hữu ích cho một đối tượng mượn chức năng của một đối tượng khác, có nghĩa là đối tượng mượn chỉ đơn giản thực hiện chức năng cho vay như thể nó là của chính nó.

Một ví dụ mã nhỏ:

var friend = {
    car: false,
    lendCar: function ( canLend ){
      this.car = canLend;
 }

}; 

var me = {
    car: false,
    gotCar: function(){
      return this.car === true;
  }
};

console.log(me.gotCar()); // false

friend.lendCar.call(me, true); 

console.log(me.gotCar()); // true

friend.lendCar.apply(me, [false]);

console.log(me.gotCar()); // false

Các phương thức này rất hữu ích để cung cấp cho các đối tượng chức năng tạm thời.


1
Cho những người muốn biết cách xem, console.loghãy kiểm tra: console.log là gì và tôi sử dụng nó như thế nào?
Michel Ayres

25

Một ví dụ khác với Call, Apply và Bind. Sự khác biệt giữa Gọi và Áp dụng là rõ ràng, nhưng Bind hoạt động như thế này:

  1. Bind trả về một thể hiện của hàm có thể được thực thi
  2. Tham số đầu tiên là ' cái này '
  3. Tham số thứ hai là danh sách các đối số được phân tách bằng dấu phẩy (như Cuộc gọi )

}

function Person(name) {
    this.name = name; 
}
Person.prototype.getName = function(a,b) { 
     return this.name + " " + a + " " + b; 
}

var reader = new Person('John Smith');

reader.getName = function() {
   // Apply and Call executes the function and returns value

   // Also notice the different ways of extracting 'getName' prototype
   var baseName = Object.getPrototypeOf(this).getName.apply(this,["is a", "boy"]);
   console.log("Apply: " + baseName);

   var baseName = Object.getPrototypeOf(reader).getName.call(this, "is a", "boy"); 
   console.log("Call: " + baseName);

   // Bind returns function which can be invoked
   var baseName = Person.prototype.getName.bind(this, "is a", "boy"); 
   console.log("Bind: " + baseName());
}

reader.getName();
/* Output
Apply: John Smith is a boy
Call: John Smith is a boy
Bind: John Smith is a boy
*/

23

Tôi muốn đưa ra một ví dụ, trong đó đối số 'valueFor This' được sử dụng:

Array.prototype.push = function(element) {
   /*
   Native code*, that uses 'this'       
   this.put(element);
   */
}
var array = [];
array.push(1);
array.push.apply(array,[2,3]);
Array.prototype.push.apply(array,[4,5]);
array.push.call(array,6,7);
Array.prototype.push.call(array,8,9);
//[1, 2, 3, 4, 5, 6, 7, 8, 9] 

** chi tiết: http://es5.github.io/#x15.4.4.7 *


20

Call () lấy các đối số được phân tách bằng dấu phẩy, ví dụ:

.call(scope, arg1, arg2, arg3)

và áp dụng () lấy một mảng các đối số, ví dụ:

.apply(scope, [arg1, arg2, arg3])

Dưới đây là một vài ví dụ sử dụng khác: http : //blog.i-ev Assessment.com/2012/08/15/javascript-call-and-apply/


`// call () === đối số được phân tách bằng dấu phẩy (đối số-list) .call (this, args1, args2, args3, ...) // áp dụng () === mảng đối số (mảng-mục). áp dụng (cái này, [
Array0, Array1, Array2

19

Từ các tài liệu MDN trên Function.prototype.apply () :

Phương thức áp dụng () gọi một hàm với một thisgiá trị đã cho và các đối số được cung cấp dưới dạng một mảng (hoặc một đối tượng giống như mảng).

Cú pháp

fun.apply(thisArg, [argsArray])

Từ các tài liệu MDN trên Function.prototype.call () :

Phương thức call () gọi một hàm với một thisgiá trị đã cho và các đối số được cung cấp riêng lẻ.

Cú pháp

fun.call(thisArg[, arg1[, arg2[, ...]]])

Từ Function.apply và Function.call trong JavaScript :

Phương thức áp dụng () giống hệt với lệnh gọi (), ngoại trừ áp dụng () yêu cầu một mảng làm tham số thứ hai. Mảng đại diện cho các đối số cho phương thức đích.


Mã ví dụ:

var doSomething = function() {
    var arr = [];
    for(i in arguments) {
        if(typeof this[arguments[i]] !== 'undefined') {
            arr.push(this[arguments[i]]);
        }
    }
    return arr;
}

var output = function(position, obj) {
    document.body.innerHTML += '<h3>output ' + position + '</h3>' + JSON.stringify(obj) + '\n<br>\n<br><hr>';
}

output(1, doSomething(
    'one',
    'two',
    'two',
    'one'
));

output(2, doSomething.apply({one : 'Steven', two : 'Jane'}, [
    'one',
    'two',
    'two',
    'one'
]));

output(3, doSomething.call({one : 'Steven', two : 'Jane'},
    'one',
    'two',
    'two',
    'one'
));

Xem thêm Fiddle này .



10

Đây là một bài viết nhỏ, tôi đã viết về điều này:

http://sizizableidea.com/call-versus-apply-javascript/

var obj1 = { which : "obj1" },
obj2 = { which : "obj2" };

function execute(arg1, arg2){
    console.log(this.which, arg1, arg2);
}

//using call
execute.call(obj1, "dan", "stanhope");
//output: obj1 dan stanhope

//using apply
execute.apply(obj2, ["dan", "stanhope"]);
//output: obj2 dan stanhope

//using old school
execute("dan", "stanhope");
//output: undefined "dan" "stanhope"

đây là một cái nữa: blog.i-ev Assessment.com/2012/08/15/javascript-call-and-apply nhưng về cơ bản là đúng: .call (scope, arg1, arg2, arg3)
Mark Karwowski

7

Sự khác biệt là call()lấy các đối số hàm riêng biệt và apply()lấy các đối số hàm trong một mảng.


6

Chúng ta có thể phân biệt các cuộc gọi và áp dụng các phương thức như dưới đây

GỌI: Một hàm với đối số cung cấp riêng. Nếu bạn biết các đối số được thông qua hoặc không có đối số nào để vượt qua, bạn có thể sử dụng cuộc gọi.

ÁP DỤNG: Gọi một hàm với đối số được cung cấp dưới dạng một mảng. Bạn có thể sử dụng áp dụng nếu bạn không biết có bao nhiêu đối số sẽ chuyển đến hàm.

Có một lợi thế của việc sử dụng áp dụng qua cuộc gọi, chúng tôi không cần thay đổi số lượng đối số chỉ chúng tôi có thể thay đổi một mảng được thông qua.

Không có sự khác biệt lớn trong hiệu suất. Nhưng chúng ta có thể nói cuộc gọi nhanh hơn một chút so với áp dụng vì một mảng cần đánh giá trong phương thức áp dụng.


5

Sự khác biệt giữa các phương thức này là, cách bạn muốn truyền tham số.

Mốc A cho mảng và C cho dấu phẩy là một mnemonic tiện dụng.


11
Câu trả lời này cung cấp những gì chưa được cung cấp đầy đủ trong các câu trả lời khác?
Kyll

5

Gọi và áp dụng cả hai được sử dụng để buộc thisgiá trị khi một chức năng được thực thi. Sự khác biệt duy nhất là calllấy các n+1đối số trong đó 1 this'n' arguments. applychỉ mất hai đối số, một thislà đối số là mảng đối số.

Ưu điểm tôi thấy trong applyhơn calllà chúng ta có thể dễ dàng giao một lời gọi hàm đến chức năng khác mà không cần nhiều nỗ lực;

function sayHello() {
  console.log(this, arguments);
}

function hello() {
  sayHello.apply(this, arguments);
}

var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');

Quan sát cách dễ dàng, chúng tôi được giao hellođể sayHellosử dụng apply, nhưng với callđiều này rất khó khăn để đạt được.


4

Mặc dù callapplyđạt được điều tương tự, tôi nghĩ có ít nhất một nơi mà bạn không thể sử dụng callmà chỉ có thể sử dụng apply. Đó là khi bạn muốn hỗ trợ kế thừa và muốn gọi hàm tạo.

Đây là một chức năng cho phép bạn tạo các lớp cũng hỗ trợ tạo các lớp bằng cách mở rộng các lớp khác.

function makeClass( properties ) {
    var ctor = properties['constructor'] || function(){}
    var Super = properties['extends'];
    var Class = function () {
                 // Here 'call' cannot work, only 'apply' can!!!
                 if(Super)
                    Super.apply(this,arguments);  
                 ctor.apply(this,arguments);
                }
     if(Super){
        Class.prototype = Object.create( Super.prototype );
        Class.prototype.constructor = Class;
     }
     Object.keys(properties).forEach( function(prop) {
           if(prop!=='constructor' && prop!=='extends')
            Class.prototype[prop] = properties[prop];
     });
   return Class; 
}

//Usage
var Car = makeClass({
             constructor: function(name){
                         this.name=name;
                        },
             yourName: function() {
                     return this.name;
                   }
          });
//We have a Car class now
 var carInstance=new Car('Fiat');
carInstance.youName();// ReturnsFiat

var SuperCar = makeClass({
               constructor: function(ignore,power){
                     this.power=power;
                  },
               extends:Car,
               yourPower: function() {
                    return this.power;
                  }
              });
//We have a SuperCar class now, which is subclass of Car
var superCar=new SuperCar('BMW xy',2.6);
superCar.yourName();//Returns BMW xy
superCar.yourPower();// Returns 2.6

Tôi tin rằng cuộc gọi sẽ hoạt động ở đó với toán tử lây lan như được mô tả trong câu trả lời đã chọn. Trừ khi tôi thiếu một cái gì đó.
jhliberty

4

Tóm lược:

Cả hai call()apply()là phương pháp được đặt trên Function.prototype. Do đó, chúng có sẵn trên mọi đối tượng chức năng thông qua chuỗi nguyên mẫu. Cả hai call()apply()có thể thực thi một chức năng với một giá trị được chỉ định của this.

Sự khác biệt chính giữa call()apply()là cách bạn phải vượt qua trong các cuộc tranh luận về nó. Trong cả hai call()apply()bạn vượt qua như một đối số đầu tiên, đối tượng bạn muốn trở thành giá trị là this. Các đối số khác khác nhau theo cách sau:

  • Với call() bạn phải đưa vào các đối số bình thường (bắt đầu từ đối số thứ hai)
  • Với apply()bạn phải vượt qua trong các đối số.

Thí dụ:

let obj = {
  val1: 5,
  val2: 10
}

const summation = function (val3, val4) {
  return  this.val1 + this.val2 + val3 + val4;
}

console.log(summation.apply(obj, [2 ,3]));
// first we assign we value of this in the first arg
// with apply we have to pass in an array


console.log(summation.call(obj, 2, 3));
// with call we can pass in each arg individually

Tại sao tôi cần sử dụng các chức năng này?

Các thisgiá trị có thể đôi khi khó khăn trong javascript. Giá trị được thisxác định khi hàm được thực thi không phải khi hàm được xác định. Nếu chức năng của chúng tôi phụ thuộc vào một thisràng buộc đúng, chúng tôi có thể sử dụng call()apply()thực thi hành vi này. Ví dụ:

var name = 'unwantedGlobalName';

const obj =  {
  name: 'Willem',
  sayName () { console.log(this.name);}
}


let copiedMethod = obj.sayName;
// we store the function in the copiedmethod variable



copiedMethod();
// this is now window, unwantedGlobalName gets logged

copiedMethod.call(obj);
// we enforce this to be obj, Willem gets logged


4

Sự khác biệt chính là, bằng cách sử dụng lệnh gọi, chúng ta có thể thay đổi phạm vi và truyền đối số như bình thường, nhưng áp dụng cho phép bạn gọi nó bằng cách sử dụng đối số dưới dạng Mảng (truyền chúng dưới dạng mảng). Nhưng về những gì họ làm trong mã của bạn, chúng khá giống nhau.

Mặc dù cú pháp của hàm này gần giống với hàm của (), sự khác biệt cơ bản là lệnh gọi () chấp nhận một danh sách đối số, trong khi áp dụng () chấp nhận một mảng các đối số.

Vì vậy, như bạn thấy, không có sự khác biệt lớn, nhưng vẫn có những trường hợp chúng tôi thích sử dụng lệnh gọi () hoặc áp dụng (). Ví dụ: xem mã bên dưới, tìm mã nhỏ nhất và lớn nhất trong một mảng từ MDN, sử dụng phương thức áp dụng:

// min/max number in an array
var numbers = [5, 6, 2, 3, 7];

// using Math.min/Math.max apply
var max = Math.max.apply(null, numbers); 
// This about equal to Math.max(numbers[0], ...)
// or Math.max(5, 6, ...)

var min = Math.min.apply(null, numbers)

Vì vậy, sự khác biệt chính chỉ là cách chúng ta truyền các đối số:

Gọi:

function.call(thisArg, arg1, arg2, ...);

Ứng dụng:

function.apply(thisArg, [argsArray]);

2

Hãy để tôi thêm một chút chi tiết này.

hai cuộc gọi này gần như tương đương nhau:

func.call(context, ...args); // pass an array as list with spread operator

func.apply(context, args);   // is same as using apply

Chỉ có một sự khác biệt nhỏ:

  • Các spreadnhà điều hành ... cho phép đi qua iterable args như danh sách để gọi.
  • Các applychấp nhận chỉ các mảng giống như mảng .

Vì vậy, những cuộc gọi này bổ sung cho nhau. Nơi chúng ta mong đợi một công cụ lặp , callhoạt động, nơi chúng ta mong đợi một mảng giống như , applyhoạt động.

Và đối với các đối tượng vừa lặp lại vừa giống như mảng , giống như một mảng thực, về mặt kỹ thuật, chúng tôi có thể sử dụng bất kỳ đối tượng nào, nhưng áp dụng có thể sẽ nhanh hơn vì hầu hết các công cụ JavaScript bên trong tối ưu hóa nó tốt hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.