كيف يعمل array.prototype.map ()

جافا سكريبت هي لغة منتشرة في كل مكان الآن. بمجرد أن يقتصر على الاستخدام من جانب العميل ، يمكنك الآن العثور عليه على الخوادم بالعديد من النكهات. مع نمو JavaScript ، ازدادت ترسانتها من الوظائف التي يمكن للمستخدمين استخدامها. في معظم الأوقات تكون راضيًا عن استخدام هذه الأساليب ونادرًا ما ترغب في اتخاذ هذه الخطوة الإضافية لفهم ما يحدث بالفعل تحت الغطاء.

في تلك المذكرة، دعونا نلقي خطوة اضافية اليوم واستكشاف وظيفة شعبية جدا: Array.prototype.map().

إخلاء المسؤولية : لن أشرح كيفية الاستخدام map()- يوضح المثال أدناه ذلك ، أو يمكنك العثور على العديد من الأمثلة عند البحث في Google. بدلاً من ذلك ، دعنا نتعمق في كيفية تنفيذ الخريطة فعليًا خلف الكواليس.

map()ينشئ هذا الأسلوب مصفوفة جديدة بنتيجة استدعاء دالة متوفرة على كل عنصر في المصفوفة المستدعاة.

مثال:

var array1 = [1, 4, 9, 16]; // pass a function to map const map1 = array1.map(x => x * 2); console.log(map1); // expected output: Array [2, 8, 18, 32]

التنفيذ

دعنا نختار التطبيق مباشرة من فم الحصان ونحاول تشريحه. يوجد أدناه MDN polyfill. اقض بعض الوقت في فهم الكود وانسخه وقم بتشغيله على جهازك. إذا كنت مطور JavaScript مبتدئًا / متوسطًا ، فستواجه بالتأكيد سؤالين على الأقل.

/*Array.prototype.map implementation*/ Array.prototype.map = function (callback/*, thisArg*/) { var T, A, k; if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); var len = O.length >>> 0; if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { T = arguments[1]; } A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); A[k] = mappedValue; } k++; } return A; };

لقد أبرزت بعض الأسئلة الشائعة التي قد تطرأ في تعليقات التعليمات البرمجية أدناه.

/*Array.prototype.map implementation*/ Array.prototype.map = function (callback/*, thisArg*/) { var T, A, k; if (this == null) { throw new TypeError('this is null or not defined'); } var O = Object(this); var len = O.length >>> 0;// QUESTION 1 : What is the need for this line of code? if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { T = arguments[1]; } // QUESTION 2 :What is the need for the if condition and why are we assiging T=arguments[1]? A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); // QUESTION 3: why do we pass T,k and O when all you need is kvalue? A[k] = mappedValue; } k++; } return A; };

دعونا نتحدث عن كل واحد منهم بدءًا من الأسفل

السؤال 3: لماذا نجتاز T و k و O بينما كل ما تحتاجه هو kValue؟

mappedValue = callback.call(T, kValue, k, O);

هذا هو أبسط الأسئلة الثلاثة ، لذا اخترت هذا للبدء به. في معظم الحالات ، سيكون تمرير kValue إلى رد الاتصال كافيًا ولكن:

  • ماذا لو كانت لديك حالة استخدام حيث تحتاج إلى إجراء عملية فقط على كل عنصر آخر؟ حسنًا ، أنت بحاجة إلى فهرس (k) .
  • وبالمثل ، قد تكون هناك حالات استخدام أخرى حيث تحتاج إلى أن تكون المصفوفة (O) نفسها متاحة في رد الاتصال.
  • لماذا تي ؟ في الوقت الحالي ، اعلم فقط أن T يتم تمريرها للحفاظ على السياق. ستفهم هذا بشكل أفضل بمجرد الانتهاء من السؤال 2.

السؤال 2: ما هي الحاجة إلى شرط if ولماذا نخصص T = وسيطات [1]؟

if (arguments.length > 1) { T = arguments[1]; }

تحتوي وظيفة الخريطة في التطبيق أعلاه على وسيطين : رد الاتصال و thisArg الاختياري . رد الاتصال هو وسيطة إلزامية بينما thisArg اختيارية.

يمكن للمرء أن يمرر ما يجب أن يكون القيمة "this" داخل رد النداء من خلال توفير الوسيطة الاختيارية الثانية. هذا هو السبب في أن الكود يتحقق مما إذا كان هناك أكثر من وسيط واحد ويقوم بتعيين الوسيط الاختياري الثاني إلى متغير يمكن تمريره إلى رد النداء.

لتوضيح ذلك بشكل أفضل ، لنفترض أن لديك متطلبًا وهميًا حيث تحتاج إلى إرجاع الرقم / 2 إذا كان قابلاً للقسمة على 2 ، وإذا لم يكن قابلاً للقسمة على 2 ، فأنت بحاجة إلى إعادة اسم المستخدم للشخص المتصل. يوضح الكود أدناه كيف يمكنك تحقيق ذلك:

const myObj = { user: "John Smith" } var x = [10, 7]; let output = x.map(function (n) { if (n % 2 == 0) { return n / 2; } else { return this.user } }, myObj) // myObj is the second optional argument arguments[1] console.log(output); // [5,'John Smith'] //if you run the program without supplying myObj it would be //undefined as it cannot access myObj values console.log(output); // [ 5, undefined ]

السؤال الأول: ما هي الحاجة إلى هذا السطر من التعليمات البرمجية؟

var len = O.length >>> 0

استغرق هذا بعض الوقت بالنسبة لي لاكتشاف. هناك الكثير مما يحدث في هذا السطر من التعليمات البرمجية. في JavaScript ، لديك القدرة على إعادة تعريف "هذا" داخل دالة عن طريق استدعاء الطريقة باستخدام call . يمكنك القيام بذلك باستخدام الربط أو التقديم أيضًا ، ولكن في هذه المناقشة دعنا نبقى مع المكالمة.

const anotherObject={length:{}} const myObj = { user: "John Smith" } var x = [10, 7]; let output = x.map.call(anotherObject,function (n) { if (n % 2 == 0) {return n / 2;} else {return this.user} }, myObj)

عندما تستدعي باستخدام المكالمة ،سيكون المعامل الأول هو السياق الذي يتم فيه تنفيذ وظيفة الخريطة. بإرسال المعلمة ، فإنك تقوم بالكتابة فوق "هذا" داخل الخريطة بـ "this" الخاص بكائن آخر.

إذا لاحظت ، فإن خاصية length للكائن anotherObject هي كائن فارغ وليست عددًا صحيحًا. إذا كنت تستخدم فقط الطول O بدلاً من الطول O> >> 0 ، فسيؤدي ذلك إلى قيمة غير محددة. عن طريق التحول الصفري ، فأنت تقوم بالفعل بتحويل أي كسور وأعداد غير صحيحة إلى عدد صحيح. في هذه الحالة ، سيتم فرض النتيجة على 0.

لن تحتاج معظم حالات الاستخدام إلى هذا الفحص ، ولكن قد تكون هناك حالة طرفية حيث يجب معالجة هذا النوع من السيناريو. المبرمجون الجيدون الذين صمموا المواصفات فكروا بالفعل في الأمر! بالحديث عن المواصفات ، يمكنك بالفعل العثور على المواصفات الخاصة بكيفية تنفيذ كل وظيفة في Ecmascript هنا:

مواصفات لغة ECMAScript - ECMA-262 الإصدار 5.1

يمكن نسخ هذه الوثيقة والترجمات المحتملة لها وتقديمها للآخرين ، والأعمال المشتقة التي تعلق ...

www.ecma-international.org

المواصفات ( الخطوة 3 ) تقول بوضوح أن الطول يجب أن يكون عددًا صحيحًا بدون إشارة يبلغ 32 بت. هذا هو السبب في أننا نحول صفر تعبئة للتأكد من أن الطول عدد صحيح ، لأن الخريطة نفسها لا تتطلب أن تكون هذه القيمة كائن صفيف.

هذا هو!

أود أن أشكر اثنين من الأشخاص ، لأنني لم ألتق بهم أبدًا ، لكنهم كانوا لطفاء بما يكفي لأخذ الوقت (في منتديات الإنترنت) ومساعدتي في فهم بعض الفروق الدقيقة.

Salathiel Genese، Jordan Harband - شكرا لك!

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

شكرا لك على وقتك والترميز السعيد!