javascript - شرح - ملفات جافا سكريبت
ما الفرق بين الاتصال والتقديم؟ (14)
ملخص:
كل من 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
ما هو الفرق بين استخدام call
والتقدم apply
لاستدعاء وظيفة؟
var func = function() {
alert('hello!');
};
func.apply();
vs func.call();
هل توجد فروق في الأداء بين الطريقتين السابقتين؟ متى يكون استخدام call
ممكنًا بشكل أفضل والعكس صحيح؟
أرغب في عرض مثال ، حيث يتم استخدام الوسيطة '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 *
الاختلاف الأساسي هو أن call()
يقبل قائمة وسيطة ، بينما apply()
على مصفوفة واحدة من الوسيطات .
الاختلاف الرئيسي هو ، باستخدام المكالمة ، يمكننا تغيير النطاق وتمرير الوسيطات كالمعتاد ، لكن تطبيق يتيح لك الاتصال به باستخدام الوسيطات كمصطلح (مرر كمصفوفة). ولكن من حيث ما يجب عليهم فعله في شفرتك ، فهم مشابهون جدًا.
بينما يكون بناء جملة هذه الدالة متطابق تقريبًا مع صيغة تطبيق () ، فإن الاختلاف الأساسي هو أن call () يقبل قائمة وسيطة ، بينما يطبق تطبيق () على مصفوفة واحدة من الوسيطات.
لذا كما ترى ، لا يوجد فرق كبير ، ولكن لا تزال هناك حالات نفضل استخدام call () أو تطبيق (). على سبيل المثال ، انظر إلى الشفرة أدناه ، والتي تجد أصغر وأكبر رقم في صفيف من 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]);
الفرق هو أن apply
يتيح لك استدعاء الدالة مع arguments
كمصفوفة. تتطلب call
سرد المعلمات بشكل صريح. ومن المفيد التذكر هو " A لصلاة وجيم ل omma."
انظر وثائق MDN عند apply call .
بناء الجملة الزائفة:
theFunction.apply(valueForThis, arrayOfArgs)
theFunction.call(valueForThis, arg1, arg2, ...)
هناك أيضًا ، اعتبارًا من ES6 ، إمكانية spread
الصفيف للاستخدام مع وظيفة call
، يمكنك رؤية التوافق here .
عينة من الرموز:
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
الفرق هو أن call()
يأخذ وسيطات الدالة بشكل منفصل ، apply()
يأخذ وسيطات الدالة في صفيف.
تأخذ Call () الحجج المفصولة بفواصل ، مثل:
.call(scope, arg1, arg2, arg3)
وتطبيق () يأخذ مجموعة من الحجج ، على سبيل المثال:
.apply(scope, [arg1, arg2, arg3])
في ما يلي بعض الأمثلة على الاستخدام: http://blog.i-evaluation.com/2012/08/15/javascript-call-and-apply/
على الرغم من أن call
والتطبيق يحققان نفس الشيء ، أعتقد أن هناك مكانًا واحدًا على الأقل لا يمكنك استخدام 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
للإجابة على الجزء المتعلق بوقت استخدام كل وظيفة ، استخدم 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
أسرع إذا قمت بذلك.
مثال آخر على المكالمات والتطبيق والربط. الفرق بين Call و Apply واضح ، لكن Bind يعمل على هذا النحو :
- تربط Bind نسخة من دالة يمكن تنفيذها
- المعلمة الأولى هي " هذا "
- المعلمة الثانية هي قائمة من الوسيطات مفصولة بفواصل (مثل 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
*/
من المفيد في بعض الأحيان أن يستعير كائن ما وظيفة كائن آخر ، بمعنى أن الكائن المقترض ينفذ الوظيفة المقربة ببساطة كما لو كانت خاصة به.
مثال صغير للرمز:
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
هذه الأساليب مفيدة للغاية لإعطاء الكائنات وظيفة مؤقتة.
هنا هو حسن ذاكري. يستخدم pply A rrays و يأخذ lways وسيط واحد أو اثنين. عند استخدام C كل ما عليك أن C ount عدد من الحجج.
يتم استخدام المكالمة وتطبيقهما لإجبار this
القيمة عند تنفيذ دالة ما. الاختلاف الوحيد هو أن call
يأخذ وسيطات n+1
حيث 1 هي 'n' arguments
و 'n' arguments
. apply
يأخذ حجتين فقط ، واحد هو this
الآخر هو مجموعة الحجة.
الميزة التي أراها في apply
على call
هي أنه يمكننا بسهولة تفويض استدعاء وظيفة لوظيفة أخرى دون بذل الكثير من الجهد ؛
function sayHello() {
console.log(this, arguments);
}
function hello() {
sayHello.apply(this, arguments);
}
var obj = {name: 'my name'}
hello.call(obj, 'some', 'arguments');
لاحظ مدى سهولة sayHello
apply
، لكن من الصعب جدًا تحقيق ذلك مع call
.
يمكننا تمييز الاتصال وتطبيق الأساليب على النحو التالي
CALL: توفر دالة مع وسيطة بشكل فردي. إذا كنت تعرف الوسائط التي يجب تمريرها أو لا توجد وسيطة لتمريرها يمكنك استخدام المكالمة.
تطبيق: استدعاء دالة مع وسيطة المقدمة كصفيف. يمكنك استخدام التطبيق إذا كنت لا تعرف عدد الوسيطات التي سيتم تمريرها إلى الوظيفة.
هناك ميزة استخدام تطبيق عبر مكالمة ، لا نحتاج إلى تغيير عدد الوسيطة إلا أنه يمكننا تغيير صفيف تم تمريره.
لا يوجد فرق كبير في الأداء. ولكن يمكننا أن نقول أن الاتصال أسرع قليلاً كمقارنة للتطبيق لأن هناك حاجة إلى تقييم الصفيف في طريقة التطبيق.