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


Sự khác biệt giữa việc sử dụng call và apply để gọi một hàm?

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

func.apply(); so 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 tốt nhất nên sử dụng call kết thúc apply và ngược lại?


2741
2017-12-31 19:56


gốc


Hãy nghĩ về a áp dụng cho mảng của args và c trong cuộc gọi cho các cột của args. - Larry Battle
@LarryBattle Hoàn toàn giúp tôi nhớ sự khác biệt cho chứng nhận 70-480 của tôi :) - Shouvik
@LarryBattle Tôi làm gần như giống nhau, nhưng tôi nghĩ rằng một trong áp dụng cho mảng và c trong cuộc gọi cho dấu phẩy (tức là các đối số được phân tách bằng dấu phẩy). - Samih
@LarryBattle dễ nhớ Larry, cuối cùng nhớ lại mối tương quan với tên - Tomo
Bạn ứng dụng cho một công việc một lần (một đối số), bạn [điện thoại] gọi điện mọi người nhiều lần (nhiều đối số). Thay thế: có [quá?] Nhiều Gọi điện của trò chơi Duty. - Gras Double


Các câu trả lời:


Sự khác biệt là apply cho phép bạn gọi hàm bằng arguments như một mảng; call yêu cầu các tham số được liệt kê một cách rõ ràng. Một nghi lễ hữu ích là "A cho mộtrray và C cho comma. "

Xem tài liệu của MDN về ứng dụng và gọi điện.

Cú pháp giả:

theFunction.apply(valueForThis, arrayOfArgs)

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

Cũng có ES6, khả năng spread mảng để sử dụng với call chức năng, bạn có thể thấy tính 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


3308
2017-12-31 20:00



Một điều cần thêm là args phải là một mảng số ([]). Các mảng kết hợp ({}) sẽ không hoạt động. - Kevin Schroeder
@KevinSchroeder: Trong ngôn từ javascript, [] được gọi là mảng, {} được gọi là vật. - Martijn
Tôi thường được sử dụng để quên mất một mảng, và đó hy vọng bạn liệt kê các đối số. Một kỹ thuật tôi từng nhớ là nếu chữ cái đầu tiên của phương thức bắt đầu bằng một sau đó nó có một mảng một mảng pply - aziz punjani
@SAM Using gọi điện thay vì gọi hàm bình thường chỉ có ý nghĩa nếu bạn cần thay đổi giá trị của điều này cho cuộc gọi hàm. Một ví dụ (chuyển đổi đối số-đối số của hàm thành một mảng): Array.prototype.slice.call(arguments) hoặc là [].slice.call(arguments). ứng dụng có ý nghĩa nếu bạn có các đối số trong một mảng, ví dụ như trong một hàm gọi một hàm khác với (gần như) các tham số giống nhau. sự giới thiệu Sử dụng cuộc gọi hàm bình thường funcname(arg1) nếu điều đó làm những gì bạn cần và tiết kiệm gọi điện và ứng dụng cho những dịp đặc biệt khi bạn thực sự cần chúng. - some
@KunalSingh Cả hai call và apply có hai tham số. Đối số đầu tiên của apply' and Hàm call` phải là đối tượng của chủ sở hữu và tham số thứ hai sẽ là các tham số được phân tách bằng dấu phẩy hoặc bằng dấu phẩy. Nếu bạn vượt qua null hoặc là undefined là đối số đầu tiên, sau đó ở chế độ không nghiêm ngặt, chúng được thay thế bằng đối tượng toàn cầu, tức là window - A J Qarshi


K. Scott Allen có viết đẹp 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ố hàm.

Phương thức apply () giống hệt với call (), ngoại trừ apply () 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"]);

209
2017-12-31 19:59



tham số thứ hai của hàm apply () và call () là tùy chọn, không bắt buộc. - angry kiwi
Tham số đầu tiên cũng không được yêu cầu. - Ikrom


Để trả lời phần về thời điểm sử dụng từng chức năng, hãy sử dụng apply nếu bạn không biết số lượng đối số bạn sẽ chuyển, hoặc nếu chúng đã ở trong một mảng hoặc đối tượng giống mảng (như arguments đối tượng để chuyển tiếp các đối số của riêng bạn. Sử dụng call nếu không, vì không cần 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 call kể từ khi tôi gọi điện thoại chức năng. apply sẽ ngụ ý bạn là áp dụng hàm cho đối số (không tồn tại).

Không nên có bất kỳ sự khác biệt về hiệu suất nào, ngoại trừ có thể nếu bạn sử dụng apply và 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 đã không thử nghiệm 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. Có thể là call nhanh hơn nếu bạn không có các đối số trong một mảng và apply là nhanh hơn nếu bạn làm.


150
2017-12-31 21:50





Đây là một nghi lễ tốt. Apply sử dụng Array và Always mất một hoặc hai đối số. Khi bạn sử dụng Ctất cả những gì bạn phải Ckể về số lượng đối số.


102
2017-09-04 13:36



Hữu ích ghi nhớ ngay ở đó !. Tôi sẽ thay đổi 'một hoặc hai đối số' để nói 'tối đa hai đối số' vì không phải là tham số đầu tiên hoặc thứ hai của apply bắt buộc. Tôi không chắc tại sao một người sẽ gọi apply hoặc là call khô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/… - dantheta
Vì vậy, khi sử dụng Call () và khi nào thì sử dụng Apply () .... - Krish


Trong khi đây là một chủ đề cũ, tôi chỉ muốn chỉ ra rằng .call là hơi nhanh hơn .apply. Tôi không thể cho bạn biết chính xác lý do tại sao.

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


[UPDATE!]

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

Áp dụng có một mảng các đối số, trong khi Cuộc gọi lấy 0 hoặc nhiều tham số riêng lẻ! Ah hah!

.apply(this, [...])

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


91
2017-11-07 17:36



Điều này phụ thuộc vào chức năng của 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? - Relic
Thật thú vị ngay cả khi không có mảng, cuộc gọi vẫn cò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 nhận được áp dụng đi nhanh gấp hai lần như cuộc gọi. - Vincent McNabb
1. Tạo một mảng mới có nghĩa là bộ thu gom rác sẽ cần phải làm 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 dereference kém hiệu quả hơn truy cập trực tiếp biến (tham số). (Tôi tin rằng đó là những gì kmatheny có nghĩa là "phân tích cú pháp", mà thực sự là một cái gì đó khá khác nhau.) Nhưng không phải của các đối số của tôi giải thích jsperf. Điều đó phải liên quan đến việc triển khai thực hiện hai chức năng của động cơ, ví dụ: có lẽ họ tạo ra một mảng trống anyway, nếu không ai được thông qua. - joeytwiddle
Cảm ơn bạn đã chia sẻ bài kiểm tra và video - Gary


Theo dõi một trích 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 rất nhiều cái nhìn sâu sắc. Từ "Phụ lục B. Các khái niệm JavaScript bị hiểu lầm thường xuyên":


this Đề cậ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à bộ 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ó người nhận rõ ràng khi một hàm được gọi, thì đối tượng chung sẽ trở thành người nhận. Như được giải thích trong "goog.global" trên trang 47, cửa sổ là đối tượng chung 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 du obj.addValues và f tham khảo cùng 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 this sẽ có giá trị chính xác khi được gọi. Để rõ ràng, nếu this không được tham chiếu trong thân hàm, sau đó là hành vi của f(20) và obj.addValues(20) sẽ giống nhau.

Bởi vì các hàm là các đối tượng hạng nhất trong JavaScript, chúng có thể có các phương thức riêng của chúng. Tất cả các hàm đều có các phương thức call() và apply() có thể xác định lại người nhận (tức là đối tượng this đề cập đến) khi gọi hàm. Chữ ký của 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() và apply() đó là call() nhận các tham số hàm như 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, như f và obj.addValues tham khảo cùng chức năng:

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

Tuy nhiên, vì cả hai call() cũng không apply() sử dụng giá trị của bộ thu riêng của nó để thay thế cho đối số người nhận khi nó không được chỉ định, những điều sau 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 this không bao giờ có thể null hoặc là undefined khi một hàm được gọi. Khi nào null hoặc là undefined được cung cấp như người nhận call() hoặc là apply(), đối tượng chung được sử dụng làm giá trị cho người nhận thay thế. Do đó, đoạn mã trước có cùng tác dụng phụ không mong muốn khi thêm thuộc tính có tên value cho đối tượng toàn cầu.

Có thể hữu ích khi nghĩ về một hàm không có kiến ​​thức về biến mà 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 hàm được xác định.


Kết thúc trích xuất.


71
2017-12-04 12:41



Chỉ cần lưu ý thực tế, rằng additionalValues không được tham chiếu bên trong obj.addValues thân hình - 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


Nó rất hữu ích vào những thời điểm cho một đối tượng mượn hàm của một đối tượng khác, có nghĩa là đối tượng vay chỉ đơn giản thực hiện chức năng cho vay như thể nó là của riêng 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

Những phương pháp này rất hữu ích cho việc đưa ra các đối tượng chức năng tạm thời.


33
2018-02-25 19:31



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


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à hiển nhiên, nhưng Trói buộc hoạt động như thế này:

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

}

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
2018-03-31 07:32





Tôi muốn hiển thị một ví dụ, trong đó đối số 'valueForThis' đượ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*


21
2017-07-05 10:56





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

.call(scope, arg1, arg2, arg3) 

và apply () nhận 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-evaluation.com/2012/08/15/javascript-call-and-apply/


20
2018-01-21 18:11



`// call () === các đối số được phân cách bằng dấu phẩy (danh sách đối số) .call (this, args1, args2, args3, ...) // áp dụng () === mảng đối số (mảng-các mục). áp dụng (điều này, [arr0, arr1, arr2, ...]) ` - xgqfrms


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

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

Cú pháp

fun.apply(thisArg, [argsArray])

Từ 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 this giá trị và đố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 apply () giống hệt với call (), ngoại trừ apply () 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 pháp đích.


Ví dụ về mã:

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.


18
2018-01-18 02:27