javascript - شرح - ملفات جافا سكريبت




ما الفرق بين الاتصال والتقديم؟ (14)

ما هو الفرق بين استخدام call والتقدم apply لاستدعاء وظيفة؟

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

func.apply(); vs func.call();

هل توجد فروق في الأداء بين الطريقتين السابقتين؟ متى يكون استخدام call ممكنًا بشكل أفضل والعكس صحيح؟


ملخص:

كل من 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


أرغب في عرض مثال ، حيث يتم استخدام الوسيطة '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 يعمل على هذا النحو :

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

من المفيد في بعض الأحيان أن يستعير كائن ما وظيفة كائن آخر ، بمعنى أن الكائن المقترض ينفذ الوظيفة المقربة ببساطة كما لو كانت خاصة به.

مثال صغير للرمز:

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: توفر دالة مع وسيطة بشكل فردي. إذا كنت تعرف الوسائط التي يجب تمريرها أو لا توجد وسيطة لتمريرها يمكنك استخدام المكالمة.

تطبيق: استدعاء دالة مع وسيطة المقدمة كصفيف. يمكنك استخدام التطبيق إذا كنت لا تعرف عدد الوسيطات التي سيتم تمريرها إلى الوظيفة.

هناك ميزة استخدام تطبيق عبر مكالمة ، لا نحتاج إلى تغيير عدد الوسيطة إلا أنه يمكننا تغيير صفيف تم تمريره.

لا يوجد فرق كبير في الأداء. ولكن يمكننا أن نقول أن الاتصال أسرع قليلاً كمقارنة للتطبيق لأن هناك حاجة إلى تقييم الصفيف في طريقة التطبيق.





dynamic