مبادئ البرمجة الوظيفية في جافا سكريبت

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

الصورة

هنا نتحدث عن البرمجة الوظيفية وبعض مبادئها المهمة. سيتم توضيح كل ذلك من خلال العديد من أمثلة كود JavaScript.

ما هي البرمجة الوظيفية؟


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

الآن ، على سبيل المثال ، سنقوم بتحليل بعض أفكار البرمجة الوظيفية.

وظائف نقية


الدالات البحتة هي المفهوم الأساسي الأول الذي يجب دراسته من أجل فهم جوهر البرمجة الوظيفية.

ما هي "وظيفة نقية"؟ ما الذي يجعل وظيفة "نظيفة"؟ يجب أن تفي الوظيفة الخالصة بالمتطلبات التالية:

  • إنها ترجع دائمًا ، عند تمرير نفس الحجج إليها ، نفس النتيجة (وتسمى هذه الوظائف أيضًا الحتمية).
  • هذه الوظيفة ليس لها آثار جانبية.

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

▍ وسيطات الدالة وقيم الإرجاع


تخيل أننا بحاجة إلى إنشاء دالة تحسب مساحة الدائرة. سوف تأخذ الدالة غير النقية ، كمعلمة ، نصف قطر الدائرة ( radius ) ، وبعد ذلك تقوم بإرجاع قيمة حساب التعبير radius * radius * PI :

 const PI = 3.14; function calculateArea(radius) { return radius * radius * PI; } calculateArea(10); //  314 

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

الآن تخيل أن بعض علماء الرياضيات توصلوا إلى استنتاج مفاده أن قيمة ثابت PI يجب أن تكون 42 ، ولهذا السبب تم تغيير قيمة هذا الثابت.

الآن دالة ليست نقية ، عندما يتم تمرير نفس قيمة الإدخال ، الرقم 10 ، ستُرجع القيمة 10 * 10 * 42 = 4200 . اتضح أنه باستخدام نفس الشيء كما في المثال السابق ، قيمة المعلمة radius ، ترجع الدالة نتيجة مختلفة. دعونا إصلاح هذا:

 const PI = 3.14; function calculateArea(radius, pi) { return radius * radius * pi; } calculateArea(10, PI); //  314 

الآن ، عند استدعاء هذه الوظيفة ، سنقوم دائمًا بتمرير الوسيطة pi . نتيجة لذلك ، ستعمل الوظيفة فقط مع ما يتم تمريره إليها عند الاتصال ، دون اللجوء إلى كيانات عالمية. إذا قمنا بتحليل سلوك هذه الوظيفة ، فيمكننا التوصل إلى الاستنتاجات التالية:

  • إذا نجحت الدالات في وسيطة radius تساوي 10 وكانت الوسيطة pi تساوي 3.14 ، 3.14 دائمًا النتيجة نفسها - 314 .
  • عند الاتصال باستخدام وسيطة radius 10 و pi 42 ، ستعود دائمًا إلى 4200 .

قراءة الملفات


إذا كانت وظيفتنا تقرأ الملفات ، فلن تكون نظيفة. الحقيقة هي أن محتويات الملفات قد تتغير.

 function charactersCounter(text) { return `Character count: ${text.length}`; } function analyzeFile(filename) { let fileContent = open(filename); return charactersCounter(fileContent); } 

توليد عدد عشوائي


لا يمكن أن تكون أي وظيفة تعتمد على مولد الأرقام العشوائية نقية.

 function yearEndEvaluation() { if (Math.random() > 0.5) {   return "You get a raise!"; } else {   return "Better luck next year!"; } } 

الآن دعونا نتحدث عن الآثار الجانبية.

▍ الآثار الجانبية


مثال على أحد الآثار الجانبية التي يمكن أن تحدث عند استدعاء دالة هو تعديل المتغيرات العامة أو الوسائط التي يتم تمريرها إلى الوظائف حسب المرجع.

لنفترض أننا بحاجة إلى إنشاء دالة تأخذ عددًا صحيحًا وتزيد هذا الرقم بمقدار 1. إليك ما قد يبدو عليه تنفيذ فكرة مماثلة:

 let counter = 1; function increaseCounter(value) { counter = value + 1; } increaseCounter(counter); console.log(counter); // 2 

هناك counter عالمي متغير. وظيفتنا ، التي ليست نقية ، تستقبل هذه القيمة كوسيطة وتستبدلها ، وتضيف واحدة إلى قيمتها السابقة.

المتغير العالمي يتغير ، مماثل في البرمجة الوظيفية غير مرحب به.

في حالتنا ، يتم تعديل قيمة المتغير الشامل. كيف تجعل وظيفة increaseCounter() النظيفة increaseCounter() نظيفة في ظل هذه الظروف؟ في الواقع ، الأمر بسيط للغاية:

 let counter = 1; function increaseCounter(value) { return value + 1; } increaseCounter(counter); // 2 console.log(counter); // 1 

كما ترى ، ترجع الدالة 2 ، لكن قيمة counter المتغير الشامل لا تتغير. هنا يمكننا أن نستنتج أن الدالة تقوم بإرجاع القيمة التي تم تمريرها إليها ، وزيادة بنسبة 1 ، مع عدم تغيير أي شيء.

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

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

▍ قوة وظائف نقية


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

  • إذا تم تمرير المعلمة A إلى الدالة ، فمن المتوقع أن تكون قيمة الإرجاع B.
  • إذا تم تمرير المعلمة C إلى الدالة ، فمن المتوقع أن تكون قيمة الإرجاع D.

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

 let list = [1, 2, 3, 4, 5]; function incrementNumbers(list) { return list.map(number => number + 1); } 

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

 incrementNumbers(list); //  [2, 3, 4, 5, 6] 

من المتوقع من هذه الوظيفة ، بعد قبول صفيف من النموذج [1, 2, 3, 4, 5] ، فإنه سيعود صفيف جديد [2, 3, 4, 5, 6] . هذه هي الطريقة التي يعمل بها.

الحصانة


يمكن وصف حصانة كيان معين بأنه لا يتغير مع مرور الوقت ، أو باعتباره استحالة تغيير هذا الكيان.

إذا حاولوا تغيير كائن ثابت ، فلن ينجح هذا. بدلاً من ذلك ، ستحتاج إلى إنشاء كائن جديد يحتوي على القيم الجديدة.

على سبيل المثال ، غالبًا ما تستخدم JavaScript حلقة for . في سياق عمله ، كما هو موضح أدناه ، يتم استخدام المتغيرات القابلة للتغيير:

 var values = [1, 2, 3, 4, 5]; var sumOfValues = 0; for (var i = 0; i < values.length; i++) { sumOfValues += values[i]; } sumOfValues // 15 

في كل تكرار للحلقة ، تتغير قيمة المتغير i وقيمة المتغير الشامل (يمكن اعتباره حالة البرنامج) sumOfValues . كيف في مثل هذه الحالة للحفاظ على ثبات الكيانات؟ الجواب يكمن في استخدام العودية.

 let list = [1, 2, 3, 4, 5]; let accumulator = 0; function sum(list, accumulator) { if (list.length == 0) {   return accumulator; } return sum(list.slice(1), accumulator + list[0]); } sum(list, accumulator); // 15 list; // [1, 2, 3, 4, 5] accumulator; // 0 

هناك دالة sum() ، والتي تأخذ مجموعة من الأرقام. هذه الوظيفة تستدعي نفسها حتى الصفيف فارغ (هذه هي الحالة الأساسية للخوارزمية العودية ). في كل "تكرار" ، نضيف قيمة أحد عناصر المصفوفة إلى معلمة وظيفة accumulator ، دون التأثير على accumulator المتغير العام. في هذه الحالة ، تظل list المتغيرات العامة accumulator دون تغيير ؛ يتم تخزين نفس القيم فيها قبل استدعاء الوظيفة وبعده.

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

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

إذا نجحنا في حل هذه المشكلة باستخدام Ruby وباستخدام مبادئ OOP ، فسنقوم أولاً بإنشاء فصل ، UrlSlugify عليه اسم UrlSlugify ، ثم UrlSlugify بإنشاء طريقة لهذه الفئة slugify! والذي يستخدم لتحويل السلسلة.

 class UrlSlugify attr_reader :text def initialize(text)   @text = text end def slugify!   text.downcase!   text.strip!   text.gsub!(' ', '-') end end UrlSlugify.new(' I will be a url slug   ').slugify! # "i-will-be-a-url-slug" 

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

ومع ذلك ، أثناء هذا التحول ، يحدث طفرة في حالة البرنامج.

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

 let string = " I will be a url slug   "; function slugify(string) { return string.toLowerCase()   .trim()   .split(" ")   .join("-"); } slugify(string); // i-will-be-a-url-slug 

نحن هنا نستخدم الوظائف التالية ، الممثلة في جافا سكريبت بواسطة طرق الصفيف والمصفوفة القياسية:

  • toLowerCase : يحول أحرف السلسلة إلى toLowerCase .
  • trim : يزيل مسافة بيضاء من بداية ونهاية الخط.
  • split : تقسيم سلسلة إلى أجزاء ، ووضع الكلمات مفصولة بمسافات في صفيف.
  • join : يشكل سلسلة مع كلمات مفصولة بشرطة مستندة إلى صفيف مع الكلمات.

تسمح لك هذه الوظائف الأربع بإنشاء وظيفة لتحويل سلسلة لا تغير هذه السلسلة نفسها.

ربط الشفافية


قم بإنشاء square() دالة square() يُرجع نتيجة ضرب رقم بنفس الرقم:

 function square(n) { return n * n; } 

هذه وظيفة خالصة ستعيد دائمًا نفس قيمة المخرجات لنفس قيمة الإدخال.

 square(2); // 4 square(2); // 4 square(2); // 4 // ... 

على سبيل المثال ، بغض النظر عن عدد الأرقام 2 يتم تمريرها إليها ، ستقوم هذه الوظيفة دائمًا بإرجاع الرقم 4 . نتيجة لذلك ، اتضح أنه يمكن استبدال مكالمة square(2) الشكل square(2) بالرقم 4 . هذا يعني أن وظيفتنا لها خاصية الشفافية المرجعية.

بشكل عام ، يمكننا أن نقول أنه إذا كانت الدالة تُرجع دائمًا النتيجة نفسها لنفس قيم الإدخال التي تم تمريرها إليها ، فإن لها شفافية مرجعية.

ure وظائف نقية + بيانات غير قابلة للتغيير = شفافية مرجعية


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

 function sum(a, b) { return a + b; } 

نحن نسميها مثل هذا:

 sum(3, sum(5, 8)); 

استدعاء sum(5, 8) يعطي دائما 13 . لذلك ، يمكن إعادة كتابة المكالمة أعلاه على النحو التالي:

 sum(3, 13); 

هذا التعبير ، بدوره ، يعطي دائما 16 . نتيجة لذلك ، يمكن استبداله بعدد ثابت وثابتة .

وظائف ككائنات من الدرجة الأولى


إن فكرة إدراك الوظائف ككائنات من الدرجة الأولى هي أنه يمكن اعتبار هذه الوظائف بمثابة قيم والعمل معها كبيانات. يمكن تمييز الميزات التالية للوظائف:

  • يمكن تخزين الإشارات إلى الوظائف في ثوابت ومتغيرات ومن خلالها الوصول إلى الوظائف.
  • يمكن تمرير وظائف إلى وظائف أخرى كمعلمات.
  • يمكن إرجاع وظائف من وظائف أخرى.

بمعنى ، إنه يتعلق بدراسة الوظائف كقيم ومعاملتها مثل البيانات. باستخدام هذا النهج ، يمكنك الجمع بين الوظائف المختلفة في عملية إنشاء وظائف جديدة تنفذ ميزات جديدة.

تخيل أن لدينا دالة تضيف قيمتين رقميتين مرت عليهما ، ثم تضربهما في 2 وتُرجع ما اتضح:

 function doubleSum(a, b) { return (a + b) * 2; } 

نكتب الآن دالة تطرح الثانية من القيمة العددية الأولى التي تم تمريرها إليها ، وتضاعف ما حدث ب 2 ، وتُرجع القيمة المحسوبة:

 function doubleSubtraction(a, b) { return (a - b) * 2; } 

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

 function sum(a, b) { return a + b; } function subtraction(a, b) { return a - b; } function doubleOperator(f, a, b) { return f(a, b) * 2; } doubleOperator(sum, 3, 1); // 8 doubleOperator(subtraction, 3, 1); // 4 

كما ترى ، فإن وظيفة doubleOperator() لديها الآن معلمة f ، ويتم استخدام الوظيفة التي تمثلها لمعالجة المعلمتين a و b . دالات sum() و substraction() تم تمريرها إلى دالة doubleOperator() ، في الواقع ، تتيح لك التحكم في سلوك وظيفة doubleOperator() ، وتغييرها وفقًا للمنطق المطبق فيها.

وظائف النظام العالي


عند الحديث عن الوظائف ذات الترتيب العالي ، فإننا نعني بالوظائف التي تتميز بواحدة من الميزات التالية على الأقل:

  • تأخذ الدالة وظيفة أخرى كوسيطة (قد يكون هناك العديد من هذه الوظائف).
  • ترجع الدالة وظيفة أخرى كنتيجة لعملها.

قد تكون بالفعل على دراية filter() أساليب مجموعة JS القياسية filter() ، و map() reduce() . دعنا نتحدث عنهم.

ra مصفاة الترشيح وطريقة المرشح ()


افترض أن لدينا مجموعة معينة من العناصر التي نريد ترشيحها بواسطة بعض سمات عناصر هذه المجموعة وتشكيل مجموعة جديدة. تتوقع وظيفة filter() الحصول على بعض المعايير لتقييم العناصر ، والتي على أساسها تحدد ما إذا كان سيتم تضمين عنصر في المجموعة الناتجة أم لا. يتم تعريف هذا المعيار بواسطة الدالة التي تم تمريرها إليها ، والتي تُرجع true إذا كان يجب أن تتضمن وظيفة filter() عنصرًا في المجموعة النهائية ، وإلا فإنها ترجع إلى false .

تخيل أن لدينا مجموعة من الأعداد الصحيحة ونريد ترشيحها عن طريق الحصول على صفيف جديد يحتوي على أرقام زوجية فقط من الصفيف الأصلي.

نهج حتمى


عند تطبيق نهج ضروري لحل هذه المشكلة باستخدام JavaScript ، نحتاج إلى تنفيذ تسلسل الإجراءات التالي:

  • إنشاء مجموعة فارغة للعناصر الجديدة (دعنا نسميها حتى evenNumbers ).
  • تكرار عبر الصفيف الأصلي من الأعداد الصحيحة (دعنا نسميها numbers ).
  • ضع الأرقام الزوجية الموجودة في صفيف numbers evenNumbers صفيف evenNumbers .

إليك ما يبدو عليه تنفيذ هذه الخوارزمية:

 var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var evenNumbers = []; for (var i = 0; i < numbers.length; i++) { if (numbers[i] % 2 == 0) {   evenNumbers.push(numbers[i]); } } console.log(evenNumbers); // (6) [0, 2, 4, 6, 8, 10] 

بالإضافة إلى ذلك ، يمكننا كتابة دالة (دعنا نسميها even() ) ، والتي إذا كان الرقم متساويًا ، تُرجع true ، وإذا كانت غريبة ، تُرجع false ، ثم تُمرر إلى صفيف filter() ، والتي ، عن طريق التحقق معها ، كل عنصر من عناصر المصفوفة ، سوف تشكل مجموعة جديدة تحتوي على أرقام زوجية فقط:

 function even(number) { return number % 2 == 0; } let listOfNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; listOfNumbers.filter(even); // [0, 2, 4, 6, 8, 10] 

هنا ، بالمناسبة ، هو الحل لمشكلة واحدة مثيرة للاهتمام فيما يتعلق بفلترة المصفوفة ، والتي أكملتها أثناء العمل على مهام البرمجة الوظيفية على تصنيف هاكر . وفقًا لحالة المشكلة ، كان من الضروري تصفية مجموعة من الأعداد الصحيحة ، مع عرض العناصر التي تكون أقل من قيمة معينة فقط x .

قد يبدو الحل الضروري لهذه المشكلة في JavaScript كما يلي:

 var filterArray = function(x, coll) { var resultArray = []; for (var i = 0; i < coll.length; i++) {   if (coll[i] < x) {     resultArray.push(coll[i]);   } } return resultArray; } console.log(filterArray(3, [10, 9, 8, 2, 7, 5, 1, 3, 0])); // (3) [2, 1, 0] 

جوهر النهج الحتمي هو أننا نوضح تسلسل الإجراءات التي تؤديها الوظيفة. وهي وصف البحث عن الصفيف ، ومقارنة العنصر الحالي للصفيف مع x ووضع هذا العنصر في صفيف resultArray إذا resultArray في الاختبار.

النهج التعريفي


كيفية التبديل إلى نهج تعريفي لحل هذه المشكلة والاستخدام المطابق لطريقة filter() ، وهي وظيفة ذات ترتيب أعلى؟ على سبيل المثال ، قد يبدو كالتالي:

 function smaller(number) { return number < this; } function filterArray(x, listOfNumbers) { return listOfNumbers.filter(smaller, x); } let numbers = [10, 9, 8, 2, 7, 5, 1, 3, 0]; filterArray(3, numbers); // [2, 1, 0] 

قد تجد أنه من غير المعتاد استخدام this في الدالة smaller() في هذا المثال ، ولكن لا يوجد شيء معقد هنا. this هي الوسيطة الثانية لأسلوب filter() . في مثالنا ، هذا هو الرقم 3 تمثله المعلمة x من filterArray() . يشار إلى هذا الرقم this .

يمكن استخدام نفس الطريقة إذا كان الصفيف يحتوي على كيانات لها بنية معقدة إلى حد ما ، على سبيل المثال ، كائنات. افترض أن لدينا صفيفًا لتخزين الكائنات يحتوي على أسماء الأشخاص الذين يمثلهم خاصية name ، ومعلومات حول عمر هؤلاء الأشخاص الذين يمثلون في خاصية age . إليك ما يشبه الصفيف:

 let people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; 

نريد تصفية هذه المجموعة عن طريق تحديد فقط تلك الكائنات التي تجاوز عمرها 21 عامًا. إليك كيفية حل هذه المشكلة:

 function olderThan21(person) { return person.age > 21; } function overAge(people) { return people.filter(olderThan21); } overAge(people); // [{ name: 'TK', age: 26 }, { name: 'Kazumi', age: 30 }] 

هنا لدينا مجموعة من الأشياء التي تمثل الناس. نتحقق من عناصر هذه المصفوفة باستخدام olderThan21() . في هذه الحالة ، عند التدقيق ، نشير إلى الخاصية age لكل عنصر ، ونتحقق مما إذا كانت قيمة هذه الخاصية تتجاوز 21 . نقوم بتمرير هذه الوظيفة إلى طريقة filter() التي تقوم بتصفية الصفيف.

elements معالجة عناصر الصفيف وطريقة الخريطة ()


يتم استخدام الطريقة map() لتحويل عناصر الصفيف. يقوم بتطبيق الوظيفة التي تم تمريرها على كل عنصر من عناصر الصفيف ، ثم يقوم بإنشاء صفيف جديد يتكون من العناصر التي تم تغييرها.

دعنا نواصل التجارب مع مجموعة people الذين تعرفهم بالفعل. الآن لن نقوم بتصفية هذه المجموعة بناءً على خاصية الكائنات age . نحتاج إلى إنشاء قائمة بسطور النموذج TK is 26 years old . في هذا النهج ، سيتم إنشاء الأسطر التي تتحول إليها العناصر وفقًا للقالب p.name is p.age years old ، حيث p.name و p.age قيم الخصائص المقابلة لعناصر مجموعة people .

تبدو الطريقة الحتمية لحل هذه المشكلة في جافا سكريبت كما يلي:

 var people = [ { name: "TK", age: 26 }, { name: "Kaio", age: 10 }, { name: "Kazumi", age: 30 } ]; var peopleSentences = []; for (var i = 0; i < people.length; i++) { var sentence = people[i].name + " is " + people[i].age + " years old"; peopleSentences.push(sentence); } console.log(peopleSentences); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old'] 

إذا لجأت إلى نهج تعريفي ، فستحصل على ما يلي:

 function makeSentence(person) { return `${person.name} is ${person.age} years old`; } function peopleSentences(people) { return people.map(makeSentence); } peopleSentences(people); // ['TK is 26 years old', 'Kaio is 10 years old', 'Kazumi is 30 years old'] 

في الواقع ، الفكرة الرئيسية هنا هي أنك تحتاج إلى القيام بشيء مع كل عنصر من عناصر الصفيف الأصلي ، ثم وضعها في صفيف جديد.

إليك مهمة أخرى مع تصنيف Hacker ، وهو مخصص لتحديث القائمة . وهي نتحدث عن تغيير قيم عناصر المصفوفة العددية الحالية إلى قيمها المطلقة. لذلك ، على سبيل المثال ، عند معالجة صفيف [1, 2, 3, -4, 5] سوف يأخذ النموذج [1, 2, 3, 4, 5] لأن القيمة المطلقة -4 هي 4 .

فيما يلي مثال على حل بسيط لهذه المشكلة ، عندما نكررها على صفيف ونغير قيم عناصرها إلى قيمها المطلقة.

 var values = [1, 2, 3, -4, 5]; for (var i = 0; i < values.length; i++) { values[i] = Math.abs(values[i]); } console.log(values); // [1, 2, 3, 4, 5] 

هنا ، لتحويل قيم عناصر الصفيف ، يتم استخدام الأسلوب Math.abs() ، تتم كتابة العناصر التي تم تغييرها في نفس المكان الذي كانت عليه قبل التحويل.

.

, , , . . , , , .

, , map() . ?

, abs() , , .

 Math.abs(-1); // 1 Math.abs(1); // 1 Math.abs(-2); // 2 Math.abs(2); // 2 

, , .

, , Math.abs() map() . , ? map() . :

 let values = [1, 2, 3, -4, 5]; function updateListMap(values) { return values.map(Math.abs); } updateListMap(values); // [1, 2, 3, 4, 5] 

, , , , , .

▍ reduce()


reduce() .

. , -. Product 1 , Product 2 , Product 3 Product 4 . .

, . على سبيل المثال ، قد يبدو كالتالي:

 var orders = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; var totalAmount = 0; for (var i = 0; i < orders.length; i++) { totalAmount += orders[i].amount; } console.log(totalAmount); // 120 

reduce() , ( sumAmount() ), , reduce() :

 let shoppingCart = [ { productTitle: "Product 1", amount: 10 }, { productTitle: "Product 2", amount: 30 }, { productTitle: "Product 3", amount: 20 }, { productTitle: "Product 4", amount: 60 } ]; const sumAmount = (currentTotalAmount, order) => currentTotalAmount + order.amount; function getTotalAmount(shoppingCart) { return shoppingCart.reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120 

shoppingCart , , sumAmount() , ( order , amount ), — currentTotalAmount .

reduce() , getTotalAmount() , sumAmount() , 0 .

map() reduce() . «»? , map() shoppingCart , amount , reduce() sumAmount() . :

 const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart   .map(getAmount)   .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 120 

getAmount() amount . map() , , , [10, 30, 20, 60] . , reduce() , .

▍ filter(), map() reduce()


, , filter() , map() reduce() . , , .

-. , :

 let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] 

. :

  • type , , books .
  • , .
  • .

, :

 let shoppingCart = [ { productTitle: "Functional Programming", type: "books", amount: 10 }, { productTitle: "Kindle", type: "eletronics", amount: 30 }, { productTitle: "Shoes", type: "fashion", amount: 20 }, { productTitle: "Clean Code", type: "books", amount: 60 } ] const byBooks = (order) => order.type == "books"; const getAmount = (order) => order.amount; const sumAmount = (acc, amount) => acc + amount; function getTotalAmount(shoppingCart) { return shoppingCart   .filter(byBooks)   .map(getAmount)   .reduce(sumAmount, 0); } getTotalAmount(shoppingCart); // 70 

ملخص


JavaScript-. , .

أعزائي القراء! ?



Source: https://habr.com/ru/post/ar434112/


All Articles