pazirehsarafraz
pazirehsarafraz
خواندن ۴ دقیقه·۶ سال پیش

51 – جاوااسکریپت: Functional Programming

داشتن ویژگی first class function در زبان برنامه نویسی جاوااسکریپت باعث میشود که بتوانیم برنامه نویسی تابعگرا را در آن استفاده کنیم، یعنی فکر کردن و کد زدن مان بر اساس تابع ها باشد. این روش را نمیتوانیم به راحتی در زبان های برنامه نویسی دیگری که ویژگی first class function ندارند انجام دهیم.

var arr1 = [1, 2, 3];

console.log(arr1);

فرض کنید میخواهیم یک آرایه دیگر از روی آرایه arr1 ایجاد کنیم.

var arr2 = [ ];

for (var i=0; i<arr1.lenght; i++) {

arr2.push(arr1[i]*2);

}

console.log(arr2);

خروجی کد بالا:

[1, 2, 3]

[2, 4, 6]

برای این کار کد زیادی زده ایم. به عنوان یک برنامه نویس همیشه دنبال این هستیم که آنچه تایپ کرده ایم کمترین حالت باشد و کمترین تکرار را داشته باشیم. بنابراین از تابع ها استفاده میکنیم. در زبان های برنامه نویسی که تابع ها first class نیستند محدودیت هایی در مورد مقدار چیزی که میتوان در تابع قرار داد وجود دارد. اما با first class function میتوانیم کار متفاوتی انجام دهیم. فرض کنید تابعی داریم که یک آرایه و یک تابع را میگیرد. چون با first class function کار میکنیم میتوانیم تابع را به عنوان پارامتر به تابع پاس دهیم:

function mapForEach(arr, fn) {

var newArr = [ ];

for (int i=0; i<arr.length; i++){

newArr.push( fn(arr[i]) );

}

return newArr;

}

var arr1 = [1, 2, 3];

console.log(arr1);

var arr2 = mapForEach(arr1, function(item){return item*2;});

console.log(arr2);

کلمه map یعنی یک آرایه گرفتیم، کاری با آن انجام داده ایم (اینجا ضربدر 2 کرده ایم) و یک آرایه جدید از آن گرفته ایم. چیزی که داخل push نوشته ایم جایی است که از functional programming استفاده کرده ایم. خروجی کد بالا:

[1, 2, 3]

[2, 4, 6]

در این کد کل حلقه for را که آرایه جدید ایجاد میکرد در یک تابع سگمنت کردیم. سپس موقع فراخوانی، آن کار را به هر آیتم آرایه دادیم. این روش بسیار قدرتمند است. میتوانم از آن تابع یک بار دیگر هم استفاده کنم:

var arr3 = mapForEach(arr1, function(item){return item>2;});

console.log(arr3);

خروجی این قسمت میشود:

[false, false, true]

پس توانستیم به راحتی و با پاس دادن یک تابع، از تابع mapForEach کاملا به صورت متفاوتی دوباره استفاده کنیم. این یک مثال کلاسیک از functional programming و استفاده از فواید first class function است.

حالا میخواهیم از تابع mapForEach استفاده کنیم تا چک کنیم آیا چیزی از آستانه ای گذشته یا نه. یعنی در مثال بالا به جای مقدار 2 در item>2 یک آستانه به آن بدهیم تا قابل استفاده مجدد باشد:

var checkPastLimit = function(limiter, item) { return item>limiter };

مشکلی که اینجا داریم این است که تابع checkPastLimit دو پارامتر میگیرد ولی در کدی که بالا زده شد، تابع fn فقط یک ورودی میگیرد. باید طوری از تابع استفاده کنیم که یک پارامتر آن از قبل ست شده باشد. برای این کار میتوانیم از bind استفاده کنیم:

var arr4 = mapForEach(arr1, checkPastLimit.bind(this, 2));

پس on the fly یک کپی از تابع checkPastLimit ایجاد کردیم و 2 را به عنوان limiter به آن دادیم. اگر در کنسول arr4 را ببینیم، خروجی [false, false, true] میشود. شاید به نظر برسد که استفاده از bind به صورت همیشگی کمی آزار دهنده است، بهتر است همیشه فقط limiter را به عنوان تنها پارامتر پاس دهیم. چطور میتوانیم این کار را بکنیم و چنین تابعی ایجاد کنیم که فقط limiter را به عنوان ورودی میگیرد؟

var checkPastLimitSimplified = function(limiter) {

return function(limiter, item) { return item>limiter }.bind(this, limiter);

};

در تابع checkPastLimitSimplified باید مقدار اولیه اولین پارامتر را از قبل ست کنیم. برای همین در ادامه از bind استفاده کرده ایم. در این کد تابع داریم که پارامتر limiter را میگیرد و یک تابع برمیگرداند. از bind هم برای ست کردن limiter استفاده کردیم. پس زمانی که checkPastLimitSimplified را اجرا میکنیم مثل حالت بالاتر که از bind استفاده کرده بودیم عمل میکند. در کد بالا یک پارامتر limiter خط اول تکه کد داریم و یک پارامتر limiter خط دوم کد داریم. پارامتر limiter در خط اول به limiter خط دوم پاس داده میشود. پس برای استفاده از این قطعه کد:

var arr5 = mapForEach(arr1, checkPastLimitSimplified(2));

خروجی آن [false, false, true] است.

نباید فقط برای سگمنت کردن و ساده کردن کدنویسی از تابع ها استفاده کنیم. باید این طور فکر کنیم که چگونه به تابع هایمان تابع دهیم و تابع برگردانیم. دقت داشته باشیم که همه تابع ها بخصوص تابع های کوچکی که به هم پاس میدهیم، نباید داده ای را mutate کنند. پس همیشه بهتر است که داده ها را در بالاترین سطح ممکن زنجیره تابع ها mutate کنیم. Functional programming یک درس مجزا است. اینجا فقط خواستیم این ایده را در js معرفی کنیم. چون first class function و funcationa programming در js باعث میشود که js به سطح بالاتری برسد. میتوانیم علاوه بر کدنویسی مثل کدنویسی با زبان جاوا یا php از این ویژگی در js استفاده های زیادی داشته باشیم.


52 – جاوااسکریپت: Functional Programming

کتابخانه underscore.js کتابخانه خیلی معروفی در js است که به ما کمک میکند با آرایه ها و مجموعه ای شیء ها کار کنیم. علاوه بر مفید بودن، یک نکته خیلی خوب در مورد این کتابخانه این است که تلاش کرده نشان دهد چگونه پیاده سازی شده است. کدهای خیلی زیادی به صورت کتابخانه و framework وجود دارند و به صورت رایگان در دسترس هستند و source code آنها عالی است. میتوانیم source code آنها را بخوانیم و چیزهای زیادی یاد بگیریم. با خواندن کدهای خیلی خوب در js میتوانیم خودمان هم خیلی خوب کد بزنیم. کتابخانه underscore.js دو نسخه دارد: development و produnction. نسخه development کامنتهای بیشتری دارد. پس برای مطالعه از آن استفاده میکنیم. همچنین میتوانیم قسمت Annotated Source را دانلود کنیم که در آن کامنتها در ستونهایی کنار کد قرار گرفته اند و میتوانیم آن را بخوانیم یا در آن کد بزنیم. در این کتابخانه از مفاهیم functional programming خیلی استفاده شده است. در این کتابخانه معمولا اسم تابع هایی که به هم پاس داده شده اند predicate یا iteratee است.

کتابخانه دیگری هم به اسم lodash هست. از بیرون خیلی مشابه کتابخانه قبلی است اما ویژگی های اضافی دیگری را هم پیاده سازی کرده و برخی از utility ها را دوباره نوشته و سریعتر اجرا میشود. به همین دلیل برخی ترجیح میدهند از lodash استفاده کنند.

میخواهیم از underscore استفاده کنیم. فایل کتابخانه آن را دانلود میکنیم. داخل آن با

(function(){

شروع شده است. یعنی کل کد در یک IIFE م wrap شده است تا کدهای داخل این کتابخانه با کدهای دیگر در پروژه تصادمی نداشته باشد. فایل کتابخانه را قبل از فایل js خودمان اتچ میکنیم. پس اول کل کد داخل IIFE کتابخانه اجرا میشود و سپس کد global ای که خودمان نوشته ایم. در کتابخانه underscore شیئی به نام _ داریم.

// underscore

var arr6 = _.map(arr1, function(item){return item*3});

console.log(arr6);

کدی که در underscore نوشته شده بهتر است چون سناریوهای بیشتری را شامل میشود. خروجی کد بالا [3, 6, 9] خواهد بود. شیء _ در global قرار گرفته و همه جا قابل دسترس است.

var arr7 = _.filter([2,4,5,6,7], function(item){return item%2===0;});

console.log(arr7);

خروجی [2, 4, 6] میشود. در underscore متدهای خیلی زیادی وجود دارد. خواندن این کتابخانه به ما کمک میکند تا از ایده ها و مفاهیمی که تا کنون آموخته ایم در کد خودمان استفاده کنیم.

شاید از این پست‌ها خوشتان بیاید