ความแตกต่างระหว่างการโทรและการสมัครคืออะไร?


3103

ความแตกต่างระหว่างการใช้callและapplyการเรียกใช้ฟังก์ชันคืออะไร

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

func.apply(); VS func.call();

มีความแตกต่างด้านประสิทธิภาพระหว่างสองวิธีดังกล่าวข้างต้นหรือไม่? เมื่อเป็นที่ดีที่สุดที่จะใช้callมากกว่าapplyและในทางกลับกัน?


727
คิดว่าaใช้กับอาร์เรย์ของ args และใช้cเรียกคอลัมน์ args
Larry Battle

176
@ LarryBattle ฉันทำเกือบเหมือนกัน แต่ฉันคิดว่าในการใช้สำหรับอาร์เรย์และ c ในการเรียกเครื่องหมายจุลภาค (เช่นคั่นด้วยเครื่องหมายจุลภาคคั่น)
Samih

3
ฉันเห็นด้วยว่ามันโง่ สิ่งที่น่ารำคาญคือคำถามนี้ถูกถามระหว่างการสัมภาษณ์เพราะผู้มีอิทธิพลบางคนเพิ่มคำถามลงในรายการคำถาม js ที่สำคัญ
Ringo

6
คุณสมัครงานหนึ่งครั้ง (อาร์กิวเมนต์หนึ่งตัว) คุณ [โทรศัพท์] โทรหาผู้คนหลายครั้ง (หลายข้อโต้แย้ง) ทางเลือก: มี [เกินไป?] เกมCall of Duty หลายเกม
Gras Double

1
เมื่อความตั้งใจที่จะเรียกฟังก์ชั่น Variadic ที่มีรายการของค่าอาร์กิวเมนต์โดยไม่คำนึงถึงค่า "นี้" แล้วใช้ตัวดำเนินการแพร่กระจาย ES6 เช่นfn(...input)ที่อินพุตเป็นอาร์เรย์ developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Gajus

คำตอบ:


3648

ความแตกต่างคือapplyให้คุณเรียกใช้ฟังก์ชันด้วยargumentsเป็นอาร์เรย์ callต้องการพารามิเตอร์ที่ระบุไว้อย่างชัดเจน ความจำที่เป็นประโยชน์" สำหรับrray และCสำหรับ Omma."

ดูเอกสาร MDN ฯ เมื่อวันที่มีผลบังคับใช้และการโทร

ไวยากรณ์หลอก:

theFunction.apply(valueForThis, arrayOfArgs)

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

นอกจากนี้ยังเป็นของ ES6 ความเป็นไปได้ที่จะspreadอาร์เรย์สำหรับใช้กับcallฟังก์ชั่นที่คุณสามารถเห็นความสามารถที่นี่

รหัสตัวอย่าง:

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
สิ่งหนึ่งที่ต้องเพิ่มคือ args ต้องเป็นอาร์เรย์ตัวเลข ([]) อาร์เรย์ที่เชื่อมโยง ({}) จะไม่ทำงาน
Kevin Schroeder

326
@KevinSchroeder: ในการพูดจา javascript, []เรียกว่าอาร์เรย์ , {}เรียกว่าวัตถุ
Martijn

89
ฉันมักจะลืมที่ใช้อาร์เรย์และที่คาดว่าคุณจะแสดงรายการข้อโต้แย้ง เทคนิคที่ผมใช้ในการจำได้ว่ามันคือถ้าตัวอักษรตัวแรกของวิธีการที่เริ่มต้นด้วยแล้วมันจะใช้เวลาอาร์เรย์เช่น อาร์เรย์ pply
aziz punjani

16
@SAM การใช้การโทรแทนการเรียกฟังก์ชั่นปกติจะใช้งานได้ก็ต่อเมื่อคุณต้องการเปลี่ยนค่านี้สำหรับการเรียกใช้ฟังก์ชัน ตัวอย่าง (ที่แปลงฟังก์ชั่นการขัดแย้งวัตถุไปยังอาร์เรย์): หรือArray.prototype.slice.call(arguments) ใช้เหมาะสมถ้าคุณมีข้อโต้แย้งในอาร์เรย์ตัวอย่างเช่นในฟังก์ชั่นที่เรียกฟังก์ชั่นอื่นที่มี (เกือบ) พารามิเตอร์เดียวกัน คำแนะนำใช้การเรียกใช้ฟังก์ชั่นปกติหากทำในสิ่งที่คุณต้องการและบันทึกการโทรและใช้สำหรับโอกาสพิเศษเหล่านั้นเมื่อคุณต้องการจริงๆ [].slice.call(arguments)funcname(arg1)
บาง

4
@KunalSingh ทั้งสองcallและapplyรับพารามิเตอร์สองตัว อาร์กิวเมนต์ตัวแรกของapply' and ฟังก์ชัน call` ต้องเป็นวัตถุเจ้าของและพารามิเตอร์ตัวที่สองคืออาร์เรย์หรือพารามิเตอร์ที่คั่นด้วยเครื่องหมายจุลภาคตามลำดับ หากคุณผ่านnullหรือundefinedเป็นอาร์กิวเมนต์แรกจากนั้นในโหมดที่ไม่เข้มงวดพวกเขาจะถูกแทนที่ด้วยวัตถุทั่วโลกเช่นwindow
AJ Qarshi

229

เค. สก็อตต์อัลเลนเขียนบทความได้ดีมาก

โดยทั่วไปแล้วพวกเขาต่างกันในวิธีจัดการกับอาร์กิวเมนต์ของฟังก์ชัน

เมธอด Apply () เหมือนกับ call () ยกเว้น Apply () ต้องการอาเรย์เป็นพารามิเตอร์ตัวที่สอง อาร์เรย์แสดงอาร์กิวเมนต์สำหรับวิธีเป้าหมาย "

ดังนั้น:

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

42
พารามิเตอร์ที่สองของ Apply () และ call () เป็นทางเลือกไม่จำเป็น
กีวีโกรธ

34
พารามิเตอร์แรกไม่จำเป็นเช่นกัน
Ikrom

@Ikrom พารามิเตอร์แรกไม่จำเป็นสำหรับcallแต่ข้อกำหนดสำหรับ apply
iamcastelli

160

ในการตอบคำถามเกี่ยวกับการใช้งานแต่ละฟังก์ชั่นให้ใช้applyหากคุณไม่ทราบจำนวนข้อโต้แย้งที่คุณจะผ่านหรือถ้าพวกเขาอยู่ในอาร์เรย์หรือวัตถุคล้ายอาร์เรย์ (เช่นargumentsวัตถุที่จะส่งต่อข้อโต้แย้งของคุณเอง ใช้เป็นcallอย่างอื่นเนื่องจากไม่จำเป็นต้องตัดอาร์กิวเมนต์ในอาร์เรย์

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

เมื่อฉันไม่ผ่านข้อโต้แย้งใด ๆ (เช่นตัวอย่างของคุณ) ฉันชอบcallตั้งแต่ฉันเรียกใช้ฟังก์ชัน applyจะแปลว่าคุณกำลังใช้ฟังก์ชันกับอาร์กิวเมนต์ (ไม่มีอยู่)

ไม่ควรมีความแตกต่างด้านประสิทธิภาพใด ๆ ยกเว้นบางทีถ้าคุณใช้applyและใส่อาร์กิวเมนต์ในอาร์เรย์ (เช่นf.apply(thisObject, [a, b, c])แทนf.call(thisObject, a, b, c)) ฉันยังไม่ได้ทดสอบดังนั้นจึงอาจมีความแตกต่าง แต่จะเป็นเบราว์เซอร์ที่เฉพาะเจาะจงมาก เป็นไปได้ว่าcallจะเร็วกว่าหากคุณยังไม่มีข้อโต้แย้งในอาเรย์และapplyเร็วกว่าถ้าคุณทำ


111

นี่เป็นตัวช่วยจำที่ดี A pply ใช้A rrays และA lways รับหนึ่งหรือสองอาร์กิวเมนต์ เมื่อคุณใช้Cทั้งหมดคุณต้องC ount จำนวนอาร์กิวเมนต์


2
ช่วยในการจำที่เป็นประโยชน์ที่นั่น! ฉันจะเปลี่ยน 'อาร์กิวเมนต์หนึ่งหรือสองข้อ' เป็น 'อาร์กิวเมนต์สูงสุดสองข้อ' เนื่องจากไม่applyจำเป็นต้องใช้พารามิเตอร์แรกหรือพารามิเตอร์ที่สอง ฉันไม่แน่ใจว่าทำไมคนจะโทรapplyหรือcallไม่มีพารามิเตอร์ ดูเหมือนว่ามีคนพยายามหาสาเหตุที่นี่stackoverflow.com/questions/15903782/…
dantheta

92

ในขณะที่นี่เป็นหัวข้อเก่าฉันแค่ต้องการชี้ให้เห็นว่า. call เร็วกว่า. apply เล็กน้อย ฉันไม่สามารถบอกคุณได้ว่าทำไม

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


[ UPDATE!]

Douglas Crockford กล่าวถึงความแตกต่างสั้น ๆ ระหว่างสองสิ่งนี้ซึ่งอาจช่วยอธิบายความแตกต่างของประสิทธิภาพได้ ... http://youtu.be/ya4UHuXNygM?t=15m52s

การประยุกต์ใช้จะต้องมีอาร์กิวเมนต์มากมายในขณะที่การโทรใช้พารามิเตอร์แต่ละตัวเป็นศูนย์หรือมากกว่านั้น! อ่าาา!

.apply(this, [...])

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


ขึ้นอยู่กับฟังก์ชันที่ใช้กับพารามิเตอร์ / อาร์เรย์หากไม่ต้องการประมวลผลอาร์เรย์จะใช้เวลาน้อยลงหรือไม่
Eric Hodonsky

12
น่าสนใจแม้ไม่มีอาเรย์การโทรก็ยังเร็วกว่ามาก jsperf.com/applyvscallvsfn2
Josh Mc

@JoshMc นั่นจะเป็นเบราว์เซอร์ที่เฉพาะเจาะจงมาก ใน IE 11 ฉันจะสมัครได้สองครั้งเร็วกว่าการโทร
Vincent McNabb

1
1. การสร้างอาร์เรย์ใหม่หมายถึงตัวรวบรวมขยะจะต้องล้างข้อมูลในบางจุด 2. การเข้าถึงรายการในอาเรย์โดยใช้การยกเลิกการลงทะเบียนมีประสิทธิภาพน้อยกว่าการเข้าถึงตัวแปร (พารามิเตอร์) โดยตรง (ฉันเชื่อว่าเป็นสิ่งที่ kmatheny หมายถึงโดย "การแยกวิเคราะห์" ซึ่งเป็นสิ่งที่แตกต่างกันมากจริง ๆ ) แต่ไม่มีข้อโต้แย้งของฉันอธิบาย jsperf ต้องเกี่ยวข้องกับการใช้งานฟังก์ชั่นของทั้งสองฟังก์ชั่นเช่นบางทีพวกมันอาจสร้างอาเรย์ที่ว่างเปล่าได้ถ้าไม่มีใครผ่านไป
joeytwiddle

ขอบคุณสำหรับการแชร์การทดสอบและวิดีโอ
Gary

76

ดังต่อไปนี้สารสกัดจากปิด: คู่มือการแตกหักโดยไมเคิล Bolin มันอาจดูยาวไปหน่อย แต่ก็อิ่มตัวด้วยความเข้าใจอย่างถ่องแท้ จาก "ภาคผนวก B. แนวคิดเกี่ยวกับ JavaScript ที่เข้าใจผิดบ่อย":


thisหมายถึงอะไรเมื่อมีการเรียกใช้ฟังก์ชั่น

เมื่อเรียกใช้ฟังก์ชันของฟอร์มfoo.bar.baz()วัตถุfoo.barจะถูกเรียกว่าผู้รับ เมื่อเรียกใช้ฟังก์ชันจะเป็นผู้รับที่ใช้เป็นค่าสำหรับ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);

หากไม่มีตัวรับสัญญาณที่ชัดเจนเมื่อมีการเรียกใช้ฟังก์ชันวัตถุกลางจะกลายเป็นผู้รับ ตามที่อธิบายไว้ใน "goog.global" ในหน้า 47 หน้าต่างเป็นวัตถุร่วมเมื่อเรียกใช้ JavaScript ในเว็บเบราว์เซอร์ สิ่งนี้นำไปสู่พฤติกรรมที่น่าประหลาดใจ:

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

ถึงแม้ว่าobj.addValuesและfอ้างถึงฟังก์ชั่นเดียวกันพวกเขาจะทำงานแตกต่างกันเมื่อถูกเรียกเพราะค่าของผู้รับนั้นแตกต่างกันในการโทรแต่ละครั้ง ด้วยเหตุผลนี้เมื่อเรียกใช้ฟังก์ชันที่อ้างถึงthisเป็นสิ่งสำคัญเพื่อให้แน่ใจว่าthisจะมีค่าที่ถูกต้องเมื่อมีการเรียกใช้ จะมีความชัดเจนหากthisไม่ได้อ้างอิงในร่างกายของฟังก์ชั่นแล้วพฤติกรรมของf(20)และobj.addValues(20)จะเหมือนกัน

เนื่องจากฟังก์ชั่นเป็นวัตถุชั้นหนึ่งใน JavaScript พวกเขาสามารถมีวิธีการของตัวเอง ฟังก์ชั่นทั้งหมดมีวิธีการcall()และapply()ทำให้สามารถกำหนดตัวรับสัญญาณใหม่ (เช่นวัตถุที่thisอ้างถึง) เมื่อเรียกใช้ฟังก์ชัน ลายเซ็นวิธีการมีดังนี้:

/**
* @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;

โปรดทราบว่าความแตกต่างเพียงอย่างเดียวระหว่างcall()และapply()คือที่call()ได้รับพารามิเตอร์ฟังก์ชั่นเป็นข้อโต้แย้งของแต่ละบุคคลในขณะที่apply()ได้รับพวกเขาเป็นอาร์เรย์เดียว:

// 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]);

การเรียกต่อไปนี้เทียบเท่าfและและobj.addValuesอ้างอิงถึงฟังก์ชันเดียวกัน:

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

อย่างไรก็ตามเนื่องจากcall()มิได้apply()ใช้ค่าของผู้รับเองเพื่อทดแทนอาร์กิวเมนต์ผู้รับเมื่อไม่ระบุรายละเอียดต่อไปนี้จะไม่ทำงาน:

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

ค่าของthisไม่สามารถเป็นได้nullหรือundefinedเมื่อเรียกใช้ฟังก์ชัน เมื่อnullหรือundefinedจัดหาเป็นผู้รับถึงcall()หรือapply()วัตถุส่วนกลางจะใช้เป็นค่าสำหรับผู้รับแทน ดังนั้นรหัสก่อนหน้ามีผลข้างเคียงที่ไม่พึงประสงค์เหมือนกันของการเพิ่มคุณสมบัติที่มีชื่อvalueวัตถุทั่วโลก

การคิดฟังก์ชั่นอาจช่วยได้หากไม่มีความรู้เกี่ยวกับตัวแปรที่ได้รับมอบหมาย สิ่งนี้จะช่วยเสริมความคิดที่ว่าคุณค่าของสิ่งนี้จะถูกผูกไว้เมื่อเรียกใช้ฟังก์ชันแทนเมื่อถูกกำหนด


ในตอนท้ายของสารสกัด


เพื่อทราบความจริงที่additionalValuesไม่ได้อ้างอิงภายในobj.addValuesร่างกาย
Viktor Stolbin

ฉันรู้ว่าคุณกำลังตอบคำถาม แต่ต้องการเพิ่ม: คุณสามารถใช้การผูกเมื่อกำหนด f var f = obj.addValues;กลายเป็นvar f = obj.addValues.bind(obj) และตอนนี้ f (20) จะทำงานโดยไม่ต้องใช้สายหรือสมัครทุกครั้ง
jhliberty

ฉันรู้ว่าคุณไม่ได้เขียน แต่คุณเน้นข้อความและตัวอย่างจากหนังสือที่เกี่ยวข้องและฉันรู้สึกขอบคุณมาก พวกเขามีประโยชน์มาก
Fralcon

34

มันมีประโยชน์ในบางครั้งที่วัตถุหนึ่งยืมฟังก์ชันของวัตถุอื่นซึ่งหมายความว่าวัตถุที่ยืมมานั้นจะเรียกใช้ฟังก์ชันยืมเข้าราวกับว่ามันเป็นของมันเอง

ตัวอย่างรหัสขนาดเล็ก:

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

วิธีการเหล่านี้มีประโยชน์มากสำหรับการให้วัตถุใช้งานได้ชั่วคราว


1
สำหรับผู้ที่ต้องการทราบวิธีการconsole.logเช็คเอาต์: console.log คืออะไรและฉันจะใช้ได้อย่างไร
Michel Ayres

25

อีกตัวอย่างหนึ่งกับ Call, Apply และ Bind ความแตกต่างระหว่างการโทรและนำไปใช้นั้นชัดเจน แต่การผูกจะทำงานเช่นนี้:

  1. Bind ส่งคืนอินสแตนซ์ของฟังก์ชันที่สามารถดำเนินการได้
  2. พารามิเตอร์แรกคือ ' นี้ '
  3. พารามิเตอร์ที่สองคือรายการอาร์กิวเมนต์ที่คั่นด้วยเครื่องหมายจุลภาค (เช่นCall )

}

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

ฉันต้องการแสดงตัวอย่างโดยใช้อาร์กิวเมนต์ 'valueForThis' นี้:

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] 

รายละเอียด **: http://es5.github.io/#x15.4.4.7 *


20

การโทร () รับอาร์กิวเมนต์ที่คั่นด้วยเครื่องหมายจุลภาคเช่น:

.call(scope, arg1, arg2, arg3)

และใช้ () รับอาเรย์ของอาร์กิวเมนต์เช่น:

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

นี่คือตัวอย่างการใช้งานเพิ่มเติม: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/


`// call () === อาร์กิวเมนต์ที่คั่นด้วยเครื่องหมายจุลภาค (arguments-list). call (นี่, args1, args2, args3, ... ) // ใช้ () === อาร์เรย์ของอาร์กิวเมนต์ (รายการ array) ใช้ (สิ่งนี้, [arr0, arr1, arr2, ... ]) `
xgqfrms

19

จากMDN เอกสารบน Function.prototype.apply () :

ใช้ () วิธีการเรียกฟังก์ชั่นที่มีthisค่าที่กำหนดและข้อโต้แย้งที่มีให้เป็นอาร์เรย์ (หรือวัตถุเหมือนอาร์เรย์)

วากยสัมพันธ์

fun.apply(thisArg, [argsArray])

จากMDN เอกสารบน Function.prototype.call () :

เมธอด call () เรียกใช้ฟังก์ชันที่มีthisค่าและอาร์กิวเมนต์ที่ระบุเป็นรายบุคคล

วากยสัมพันธ์

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

จากFunction.apply และ Function.call ใน JavaScript :

เมธอด Apply () เหมือนกับ call () ยกเว้น Apply () ต้องการอาเรย์เป็นพารามิเตอร์ตัวที่สอง อาร์เรย์หมายถึงอาร์กิวเมนต์สำหรับวิธีการเป้าหมาย


ตัวอย่างรหัส:

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'
));

ดูซอนี้



10

นี่คือโพสต์เล็ก ๆ ฉันเขียนถึงสิ่งนี้:

http://sizeableidea.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"

นี่คืออีกอันหนึ่ง: blog.i-evaluation.com/2012/08/15/javascript-call-and-apply แต่โดยทั่วไปมันถูกต้อง:. call (ขอบเขต, arg1, arg2, arg3)
Mark Karwowski

7

ความแตกต่างคือการcall()ใช้ฟังก์ชั่นการขัดแย้งแยกจากกันและapply()ใช้ฟังก์ชั่นการขัดแย้งในอาร์เรย์


6

เราสามารถแยกความแตกต่างของการโทรและใช้วิธีการด้านล่าง

CALL: ฟังก์ชั่นที่มีข้อโต้แย้งเป็นรายบุคคล หากคุณรู้ว่าข้อโต้แย้งที่จะผ่านหรือไม่มีข้อโต้แย้งที่จะผ่านคุณสามารถใช้การโทร

ใช้: เรียกใช้ฟังก์ชันที่มีอาร์กิวเมนต์เป็นอาร์เรย์ คุณสามารถใช้นำไปใช้ถ้าคุณไม่ทราบว่ามีกี่อาร์กิวเมนต์ที่จะผ่านไปยังฟังก์ชั่น

มีข้อดีของการใช้นำไปใช้โทรเราไม่จำเป็นต้องเปลี่ยนจำนวนของการโต้แย้งเท่านั้นที่เราสามารถเปลี่ยนอาร์เรย์ที่ผ่าน

ประสิทธิภาพการทำงานไม่แตกต่างกันมาก แต่เราสามารถพูดได้ว่าการโทรนั้นเร็วกว่าเล็กน้อยเมื่อเปรียบเทียบกับการใช้เพราะอาร์เรย์จำเป็นต้องประเมินในวิธีการสมัคร


5

ความแตกต่างระหว่างวิธีการเหล่านี้กับวิธีการที่คุณต้องการผ่านพารามิเตอร์

“ A สำหรับอาร์เรย์และ C สำหรับจุลภาค” เป็นตัวช่วยในการจำ


11
คำตอบนี้ให้อะไรที่ไม่ได้มีมาอย่างดีในคำตอบอื่น ๆ ?
Kyll

5

การโทรและใช้ทั้งคู่จะใช้เพื่อบังคับthisค่าเมื่อมีการใช้งานฟังก์ชัน แต่ที่แตกต่างก็คือว่าcallจะใช้เวลาn+1การขัดแย้งที่ 1 และthis ใช้เวลาเพียงสองข้อโต้แย้งข้อหนึ่งคืออีกข้อคืออาเรย์อาร์กิวเมนต์'n' argumentsapplythis

ประโยชน์ที่ฉันเห็นในapplyกว่าcallคือการที่เราสามารถมอบหมายการเรียกฟังก์ชั่นฟังก์ชั่นอื่น ๆ ได้โดยไม่ต้องใช้ความพยายามมาก;

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

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

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

สังเกตว่าเรามอบหมายhelloให้sayHelloใช้งานได้applyง่ายเพียงใด แต่callสิ่งนี้ยากที่จะบรรลุ


4

แม้ว่าcallและapplyบรรลุในสิ่งเดียวกันผมคิดว่ามีอย่างน้อยหนึ่งสถานที่ที่คุณไม่สามารถใช้แต่สามารถใช้เพียงcall applyนั่นคือเมื่อคุณต้องการที่จะสนับสนุนการสืบทอดและต้องการเรียกตัวสร้าง

นี่คือฟังก์ชั่นที่ช่วยให้คุณสร้างคลาสที่รองรับการสร้างคลาสโดยขยายคลาสอื่น ๆ

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

ฉันเชื่อว่าการโทรจะทำงานที่นั่นกับตัวดำเนินการสเปรดดังที่อธิบายไว้ในคำตอบที่เลือก ถ้าฉันไม่มีอะไรหายไป
jhliberty

4

สรุป:

ทั้งสองcall()และวิธีการซึ่งตั้งอยู่บนapply() Function.prototypeดังนั้นพวกมันจึงมีอยู่ในทุกฟังก์ชั่นของวัตถุผ่านทางโซ่ต้นแบบ ทั้งสองcall()และสามารถดำเนินการฟังก์ชั่นที่มีค่าที่ระบุของapply()this

ความแตกต่างที่สำคัญระหว่างcall()และapply()เป็นวิธีที่คุณจะต้องทะเลาะกัน ทั้งในcall()และคุณผ่านเป็นอาร์กิวเมนต์แรกวัตถุที่คุณต้องการที่จะเป็นค่าเป็นapply() thisข้อโต้แย้งอื่น ๆ แตกต่างกันในวิธีต่อไปนี้:

  • ด้วยcall()คุณต้องใส่อาร์กิวเมนต์ตามปกติ (เริ่มจากอาร์กิวเมนต์ที่สอง)
  • ด้วยapply()คุณต้องผ่านในอาร์เรย์ของการขัดแย้ง

ตัวอย่าง:

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

ทำไมฉันต้องใช้ฟังก์ชั่นเหล่านี้

thisค่าสามารถเป็นบางครั้งหากินใน JavaScript ค่าที่thisกำหนดเมื่อฟังก์ชันถูกเรียกใช้งานไม่ใช่เมื่อมีการกำหนดฟังก์ชัน หากฟังก์ชั่นของเราขึ้นอยู่กับการthisผูกพันที่ถูกต้องเราสามารถใช้call()และapply()บังคับใช้พฤติกรรมนี้ ตัวอย่างเช่น:

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

ความแตกต่างที่สำคัญคือการใช้การเรียกเราสามารถเปลี่ยนขอบเขตและการส่งผ่านข้อโต้แย้งได้ตามปกติ แต่การใช้ช่วยให้คุณสามารถเรียกมันได้โดยใช้อาร์กิวเมนต์เป็นแบบ Array (ส่งผ่านเป็นอาร์เรย์) แต่ในแง่ของสิ่งที่พวกเขาทำในรหัสของคุณพวกเขาค่อนข้างคล้ายกัน

ในขณะที่ไวยากรณ์ของฟังก์ชั่นนี้เกือบจะเหมือนกันกับของ Apply () ความแตกต่างพื้นฐานคือการเรียก () ยอมรับรายการอาร์กิวเมนต์ในขณะที่ใช้ () ยอมรับอาร์เรย์ของอาร์กิวเมนต์เดียว

ดังที่คุณเห็นไม่มีความแตกต่างใหญ่ แต่ก็ยังมีหลายกรณีที่เราต้องการใช้ call () หรือ Apply () ตัวอย่างเช่นดูรหัสด้านล่างซึ่งค้นหาจำนวนที่น้อยที่สุดและมากที่สุดในอาร์เรย์จาก MDN โดยใช้วิธีการใช้งาน:

// 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)

ดังนั้นความแตกต่างที่สำคัญเป็นเพียงวิธีที่เราผ่านการขัดแย้ง:

โทร:

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

สมัคร:

function.apply(thisArg, [argsArray]);

2

ขอผมเพิ่มรายละเอียดลงไปหน่อย

การโทรสองสายนี้เกือบเทียบเท่า:

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

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

มีความแตกต่างเล็กน้อยเท่านั้น:

  • spreadผู้ประกอบการ ... ช่วยให้ผ่านiterable argsเป็นรายการที่จะโทร
  • applyยอมรับเฉพาะอาร์เรย์เหมือน args

ดังนั้นสายเหล่านี้เติมเต็มซึ่งกันและกัน ในกรณีที่เราคาดหวังiterable , callผลงานที่เราคาดหวังอาร์เรย์เหมือน , applyงาน

และสำหรับวัตถุที่มีทั้งiterableและarray เหมือนเช่น array จริงเราสามารถใช้วัตถุใด ๆ ได้ แต่การใช้อาจจะเร็วกว่าเพราะเอ็นจิ้น JavaScript ส่วนใหญ่ปรับให้เหมาะสมภายในดีกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.