۵۰ درصد تخفیف ویژه برای همه دوره‌ها از شنبه

نحوه برخورد و استفاده از توابع در جاوااسکریپت بسیار جالب است. آن‌ها بسیار انعطاف‌پذیر هستند، یعنی ما می‌توانیم توابع را به عنوان یک مقدار به یک متغیر اختصاص دهیم، آن‌ها را به عنوان یک مقدار از یک تابع دیگر return کنیم و همینطور به عنوان یک آرگومان به یک تابع دیگر منتقل نماییم. در این مقاله، به این موضوع می‌پردازیم که توابع Callback و Higher Order چیست و چگونه در جاوااسکریپت کار می‌کنند.

اهمیت توابع در جاوااسکریپت

توابع به عنوان آبجکت‌های درجه یک در جاوااسکریپت تعریف می‌شوند زیرا با آن‌ها همانند متغیرها رفتار می‌گردد.

این بدان معنی است که توابع در جاوااسکریپت می‌توانند:

درک نحوه برخورد با توابع در جاوااسکریپت ضروری است. زیرا، آن‌ها به عنوان یک بلاک سازنده برای درک توابع Callback و Higher Order در جاوااسکریپت، و روش کار آن‌ها عمل می‌کنند.

منظور از توابع Higher Order در جاوااسکریپت چیست؟

توابع Higher Order توابعی هستند که توابع دیگری را به عنوان آرگومان می‌گیرند و همچنین یک تابع را به عنوان مقدار بازگشتی return می‌کنند.

تعداد زیادی توابع Higher Order داخلی در جاوااسکریپت وجود دارد. ما برخی از آن‌ها را بررسی کرده و با نحوه عملکردشان آشنا خواهیم شد. همچنین در بخش‌های بعدی روش ساخت توابع Higher Order را نیز بررسی خواهیم کرد.

ابتدا، با چند نمونه از توابع Higher Order داخلی آشنا می‌شویم.

متدهای آرایه

متدهای آرایه معمولاً اولین توابع Higher Order هستند که یک توسعه‌دهنده در هنگام یادگیری جاوااسکریپت با آن‌ها مواجه می‌شود. این توابع عبارتند از:

map
map،
filter
filter،
forEach
forEach،
find
find،
findIndex
findIndex،
some
someو هر متد آرایه دیگری که توسط جاوااسکریپت ارائه شده است.

این متدها یا توابع آرایه اشتراکات زیادی باهم دارند، اما یکی از رایج‌ترین ویژگی‌ها این است که همه آن‌ها یک تابع را به عنوان آرگومان ورودی دریافت می‌کنند. در ادامه یک قطعه کد داریم که نحوه عملکرد متد آرایه

forEach
forEachرا نشان می‌دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const people = [
{ firstName: "Jack", year: 1988 },
{ name: "Kait", year: 1986 },
{ name: "Irv", year: 1970 },
{ name: "Lux", year: 2015 },
];
people.forEach(function (person) {
console.log(person);
});
// Output: Logs every person object in the array
const people = [ { firstName: "Jack", year: 1988 }, { name: "Kait", year: 1986 }, { name: "Irv", year: 1970 }, { name: "Lux", year: 2015 }, ]; people.forEach(function (person) { console.log(person); }); // Output: Logs every person object in the array
const people = [
  { firstName: "Jack", year: 1988 },
  { name: "Kait", year: 1986 },
  { name: "Irv", year: 1970 },
  { name: "Lux", year: 2015 },
];

people.forEach(function (person) {
  console.log(person);
});

// Output:  Logs every person object in the array

باتوجه به قطعه کد بالا، می‌بینیم که متد

forEach
forEach تابعی را به عنوان آرگومان ورودی می‌پذیرد که در هر تکرار روی آرایه، آن را فراخوانی می‌کند. بنابراین متد آرایه
forEach
forEach یک تابع Higher Order است.

Eventهای Timer

مجموعه دیگری از توابع داخلی که معمولاً مورد استفاده قرار می‌گیرند توابع

setInterval
setIntervalو
setTimeout
setTimeoutهستند که به عنوان eventهای timer در جاوااسکریپت شناخته می‌شوند.

هر تابع یک تابع دیگر را به عنوان یکی از آرگومان‌های ورودی خود می‌پذیرد و از آن برای ایجاد یک رویداد زمان‌بندی شده استفاده می‌کند.

در قطعه کد زیر نحوع کارکرد تابع

setTimeout
setTimeoutرا می‌بینیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setTimeout(function () {
console.log("This is a higher order function");
}, ۱۰۰۰);
// Output: "This is a higher order function" after 1000ms / 1 second
setTimeout(function () { console.log("This is a higher order function"); }, ۱۰۰۰); // Output: "This is a higher order function" after 1000ms / 1 second
setTimeout(function () {
  console.log("This is a higher order function");
}, ۱۰۰۰);

// Output: "This is a higher order function" after 1000ms / 1 second

مثال بالا ابتدایی‌ترین مثال از نحوه عملکرد یک تابع

setTimeout
setTimeoutمی‌باشد که تابع و یک مدت زمان بر حسب میلی ثانیه را دریافت کرده و پس از گذشت مدت زمان ارائه شده، تابع را اجرا می‌کند.

در نهایت عبارت

This is a higher order function
This is a higher order functionپس از ۱۰۰۰ میلی ثانیه (۱ ثانیه) در کنسول چاپ می‌شود.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setInterval(function () {
console.log("This is a higher order function");
}, ۱۰۰۰);
// Output: "This is a higher order function" after every 1000ms / 1 second
setInterval(function () { console.log("This is a higher order function"); }, ۱۰۰۰); // Output: "This is a higher order function" after every 1000ms / 1 second
setInterval(function () {
  console.log("This is a higher order function");
}, ۱۰۰۰);

// Output: "This is a higher order function" after every 1000ms / 1 second

تابع

setInterval
setIntervalمانند متدهای آرایه، مشابه تابع
setTimeout
setTimeoutاست اما از نظر عملکردی تفاوت‌هایی نیز وجود دارد. اما الگوی مشترکی که وجود دارد این است که هر دو این توابعی، تابعی را به عنوان یکی از پارامترهای خود قبول می‌کنند.

تفاوتی که وجود دارد این است

setTimeout
setTimeoutتابع را پس از سپری شدن مدت زمان ارائه شده اجرا می‌کند، اما
setInterval
setIntervalهر ۱۰۰۰ میلی ثانیه (۱ ثانیه) یک بار، بارها و بارها این تابع را اجرا می‌کند.

نحوه ایجاد و استفاده از تابع Higher Order

توابع higher order فقط به توابع داخلی ارائه شده توسط جاوااسکریپت محدود نمی‌شوند.

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

در مثال‌های زیر، چند تابع می‌سازیم. آن‌ها نام مشتری و یک پیام را می‌پذیرند و سپس آن اطلاعات را در کنسول چاپ می‌کنند.

در ابتدا یک تابع ساده داریم که هر دوی این کارها را انجام می‌دهد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function greetCustomer(firstName, lastName, salutation) {
const fullName = `${firstName} ${lastName}`;
console.log(`${salutation} ${fullName}`);
}
greetCustomer("Franklin", "Okolie", "Good Day");
// Output: "Good Day Franklin Okolie"
function greetCustomer(firstName, lastName, salutation) { const fullName = `${firstName} ${lastName}`; console.log(`${salutation} ${fullName}`); } greetCustomer("Franklin", "Okolie", "Good Day"); // Output: "Good Day Franklin Okolie"
function greetCustomer(firstName, lastName, salutation) {
  const fullName = `${firstName} ${lastName}`;

  console.log(`${salutation} ${fullName}`);
}

greetCustomer("Franklin", "Okolie", "Good Day");

// Output: "Good Day Franklin Okolie"

greetCustomer
greetCustomer سه آرگومان را دریافت می‌کند: نام، نام خانوادگی و salutation(متن پیام). سپس یک پیام به مشتری را به کنسول چاپ می‌کند.

اما این تابع یک مشکل دارد، یعنی به طور هم‌زمان به جای انجام یک کار، دو کار را انجام می‌دهد: نوشتن نام کامل مشتری و همچنین چاپ متن پیام.

این کار روش درستی نیست زیرا، تابع باید یک کار را به بهترین شکل ممکن انجام دهد. بنابراین ما کد خود را دوباره اصلاح می‌کنیم.

به این صورت که یک تابع دیگری باید وجود داشته باشد تا نام مشتری را بنویسد و تابع

greetCustomer
greetCustomerفقط باید متن پیام را در کنسول چاپ نماید. به این ترتیب تابعی می‌نویسیم که این کار را مدیریت کند:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function composeName(firstName, lastName) {
const fullName = `${firstName} ${lastName}`;
return fullName;
}
function composeName(firstName, lastName) { const fullName = `${firstName} ${lastName}`; return fullName; }
function composeName(firstName, lastName) {
  const fullName = `${firstName} ${lastName}`;

  return fullName;
}

اکنون تابعی داریم که نام و نام خانوادگی مشتری را ترکیب می‌کند. بنابراین می‌توانیم از آن در داخل تابع

greetCustomer
greetCustomerاستفاده نماییم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function greetCustomer(composerFunc, firstName, lastName, salutation) {
const fullName = composerFunc(firstName, lastName);
console.log(`${salutation} ${fullName}`);
}
greetCustomer(composeName, "Franklin", "Okolie", "Good Day");
// Output: "Good Day Franklin Okolie"
function greetCustomer(composerFunc, firstName, lastName, salutation) { const fullName = composerFunc(firstName, lastName); console.log(`${salutation} ${fullName}`); } greetCustomer(composeName, "Franklin", "Okolie", "Good Day"); // Output: "Good Day Franklin Okolie"
function greetCustomer(composerFunc, firstName, lastName, salutation) {
  const fullName = composerFunc(firstName, lastName);

  console.log(`${salutation} ${fullName}`);
}

greetCustomer(composeName, "Franklin", "Okolie", "Good Day");

// Output: "Good Day Franklin Okolie"

اکنون این کد تمیزتر به نظر می‌رسد و هر تابع فقط یک کار را انجام می‌دهد. تابع

greetCustomer
greetCustomerاکنون چهار آرگومان را می‌پذیرد، و از آنجایی که یکی از آن آرگومان‌ها یک تابع است، بنابراین یک تابع higher order می‌باشد.

اکنون در ادامه این موضوع را بررسی می‌کنیم که چگونه یک تابع را داخل تابع دیگر فراخوانی می‌کنیم و چرا این کار را انجام می‌دهیم.

Return کردن یک تابع به عنوان value

باید این موضوع را به یاد داشته باشیم که توابع higher order یا یک تابع را به عنوان پارامتر می‌گیرند و یا این که یک تابع را به عنوان مقدار بازگشتی return می‌کنند.

در این بخش قصد داریم تا تابع

greetCustomer
greetCustomerرا مجدداً تغییر دهیم تا از آرگومان‌های کم‌تری استفاده کرده و یک تابع return کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function getGreetingsDetails(composerFunc, salutation) {
return function greetCustomer(firstName, lastName) {
const fullName = composerFunc(firstName, lastName);
console.log(`${salutation} ${fullName}`);
};
function getGreetingsDetails(composerFunc, salutation) { return function greetCustomer(firstName, lastName) { const fullName = composerFunc(firstName, lastName); console.log(`${salutation} ${fullName}`); };
function getGreetingsDetails(composerFunc, salutation) {
  return function greetCustomer(firstName, lastName) {
    const fullName = composerFunc(firstName, lastName);

    console.log(`${salutation} ${fullName}`);
  };

آخرین نسخه

greetCustomer
greetCustomerآرگومان‌های زیادی را پذیرفته است. چهار آرگومان زیاد نیست، اما اگر ترتیب آن‌ها را رعایت نکنیم مشکلات زیادی ایجاد خواهد شد. به طور کلی، هر چه تعداد آرگومان‌ها کم‌تر باشد، بهتر است.

بنابراین در مثال بالا، تابعی به نام

getGreetingDetails
getGreetingDetailsداریم که
composerFunc
composerFuncو
salutation
salutationرا از طرف تابع داخلی
greetCustomer
greetCustomerدریافت می‌کند. سپس تابع داخلی
greetCustomer
greetCustomerرا return می‌کند که خود،
firstName
firstNameو
lastName
lastNameرا به عنوان آرگومان می‌پذیرد.

با انجام این کار، به طور کلی تابع

greetCustomer
greetCustomerتعداد آرگومان‌های کم‌تری می‌گیرد.

اکنون نحوه استفاده از تابع

getGreetingDetails
getGreetingDetailsرا بررسی می‌کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const greet = getGreetingsDetails(composeName, "Happy New Year!");
greet("Quincy", "Larson");
// Output: "Happy New Year Quincy Larson"
const greet = getGreetingsDetails(composeName, "Happy New Year!"); greet("Quincy", "Larson"); // Output: "Happy New Year Quincy Larson"
const greet = getGreetingsDetails(composeName, "Happy New Year!");

greet("Quincy", "Larson");

// Output: "Happy New Year Quincy Larson"

بیایید نحوه عملکرد همه چیز تا این مرحله را با هم مرور کنیم. تابع higher order به نام

getGreetingDetails
getGreetingDetailsدو آرگومان دارد: یک تابع برای نوشتن نام و نام خانوادگی مشتری و یک salutation. سپس تابعی به نام
greetCustomer
greetCustomerرا return می‌کند که نام و نام خانوادگی مشتری را به عنوان آرگومان می‌پذیرد.

تابع

greetCustomer
greetCustomerکه return شده است نیز از آرگومان پذیرفته شده توسط
getGreetingDetails
getGreetingDetailsبرای اجرای برخی اقدامات استفاده می‌کند.

در این مرحله ممکن است سوالی مطرح شود مبنی بر این که چگونه یک تابع return شده می‌تواند از آرگومان‌های ارائه شده به یک تابع parent، به خصوص با توجه به نحوه عملکرد context اجرای تابع استفاده کند؟

این موضوع به دلیل closure امکان‌پذیر است. در ادامه با closure بیشتر آشنا می‌شویم.

Closure چیست؟

closure تابعی است که به متغیر موجود در scopeای که در آن ایجاد شده است دسترسی دارد. این دسترسی حتی پس از اینکه scope، دیگر در context اجرا وجود نداشته باشد هم برقرار است. این موضوع یکی از مکانیسم‌های اساسی توابع callback است. زیرا callbackها همچنان می‌توانند به متغیرهای ایجاد شده در یک تابع بیرونی پس از بسته شدن آن تابع بیرونی ارجاع داده و از آن استفاده کنند.

به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
function getTwoNumbers(num1, num2) {
return function add() {
const total = num1 + num2;
console.log(total);
};
}
const addNumbers = getTwoNumbers(5, 2);
addNumbers();
//Output: 7;
function getTwoNumbers(num1, num2) { return function add() { const total = num1 + num2; console.log(total); }; } const addNumbers = getTwoNumbers(5, 2); addNumbers(); //Output: 7;
function getTwoNumbers(num1, num2) {
  return function add() {
    const total = num1 + num2;
    console.log(total);
  };
}

const addNumbers = getTwoNumbers(5, 2);

addNumbers();

//Output: 7;

کدی که در مثال بالا داریم تابعی به نام

getTwoNumbers
getTwoNumbersرا تعریف می‌کند و به ما نشان می‌دهد که closureها چگونه کار می‌کنند.

  1. getTwoNumbers
    getTwoNumbersبه عنوان تابعی تعریف می‌شود که دو پارامتر
    num1
    num1و
    num2
    num2را می‌گیرد.
  2. در داخل
    getTwoNumbers
    getTwoNumbers، تابع دیگری را return می‌کند که یک تابع درونی به نام
    add
    addاست.
  3. تابع
    add
    add هنگام فراخوانی، مجموع
    num1
    num1 و
    num2
    num2 را محاسبه کرده و نتیجه را در کنسول نمایش می‌دهد.
  4. خارج از تابع
    getTwoNumbers
    getTwoNumbers، متغیری به نام
    addNumbers
    addNumbersایجاد می‌کنیم و نتیجه فراخوانی
    getTwoNumbers (5، ۲)
    getTwoNumbers (5، ۲)را به آن تخصیص می‌دهیم. این کار به طور موثر closureای را ایجاد می‌کند که در آن
    addNumbers
    addNumbersاکنون مقادیر
    ۵
    ۵و
    ۲
    ۲را به عنوان
    num1
    num1و
    num2
    num2دریافت می‌کند.
  5. در نهایت،
    addNumbers()
    addNumbers()را برای اجرای تابع داخلی
    add
    addفراخوانی می‌کنیم. از آنجایی که
    addNumbers
    addNumbersیک closure است، همچنان به مقادیر
    num1
    num1و
    num2
    num2دسترسی دارد که به ترتیب روی
    ۵
    ۵و
    ۲
    ۲تنظیم شده بودند. در آخر هم مجموع آن‌ها را محاسبه کرده و
    ۷
    ۷را در کنسول نمایش می‌دهد.

به تابع higher orderای که داشتیم برمی‌گردیم. تابع بازگشتی

greetCustomer
greetCustomerبه عنوان مقداری return می‌شود که آن را در متغیری به نام
greet
greetذخیره می‌کنیم. انجام این کار باعث می‌شود تا متغیر
greet
greet خود یک تابع باشد. به این معنی که می‌توانیم آن را به عنوان یک تابع فراخوانی کرده و آرگومان‌هایی را برای نام و نام خانوادگی ارسال نماییم.

اکنون که درک عمیقی در مورد نحوه عملکرد توابع higher order داریم، در ادامه در مورد توابع callback صحبت خواهیم کرد.

منظور از توابع callbackدر جاوااسکریپت چیست؟

تابع callback تابعی است که به عنوان آرگومان، به تابع دیگری ارسال می‌شود. باز هم یکی از عوامل تعیین کننده اهمیت بالای توابع، توانایی آن‌ها برای انتقال به عنوان آرگومان به تابع دیگری است. این ویژگی عمل ارسال callbackها نام دارد.

در مثال زیر، دوباره تابع

setTimeout
setTimeoutرا داریم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setTimeout(function () {
console.log("This is a higher order function");
}, ۱۰۰۰);
// Output: "This is a higher order function" after 1000ms / 1 seconds
setTimeout(function () { console.log("This is a higher order function"); }, ۱۰۰۰); // Output: "This is a higher order function" after 1000ms / 1 seconds
setTimeout(function () {
  console.log("This is a higher order function");
}, ۱۰۰۰);

// Output: "This is a higher order function" after 1000ms / 1 seconds

ما ثابت کردیم که تابع

setTimeout
setTimeout یک تابع higher order است زیرا تابع دیگری را به عنوان آرگومان دریافت می‌کند.

تابعی که به عنوان آرگومان به تابع

setTimeout
setTimeout ارسال می‌شود، تابع callback نام دارد. این موضوع به این دلیل است که در داخل تابع higher orderای که به آن منتقل شده است فراخوانی یا اجرا می‌گردد.

برای درک بهتر توابع callback، نگاهی دیگر به تابع

greetCustomer
greetCustomerکه از قبل داشتیم می‌اندازیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// THIS IS A CALLBACK FUNCTION
// IT IS PASSED AS AN ARGUMENT TO A FUNCTION
function composeName(firstName, lastName) {
const fullName = `${firstName} ${lastName}`;
return fullName;
}
// THIS IS A HIGHER ORDER FUNCTION
// IT ACCPEPTS A FUNCTION AS A ARGUMENT
function greetCustomer(composerFunc, firstName, lastName, salutation) {
const fullName = composerFunc(firstName, lastName);
console.log(`${salutation} ${fullName}`);
}
greetCustomer(composeName, "Franklin", "Okolie", "Good Day");
// Output: "Good Day Franklin Okolie"
// THIS IS A CALLBACK FUNCTION // IT IS PASSED AS AN ARGUMENT TO A FUNCTION function composeName(firstName, lastName) { const fullName = `${firstName} ${lastName}`; return fullName; } // THIS IS A HIGHER ORDER FUNCTION // IT ACCPEPTS A FUNCTION AS A ARGUMENT function greetCustomer(composerFunc, firstName, lastName, salutation) { const fullName = composerFunc(firstName, lastName); console.log(`${salutation} ${fullName}`); } greetCustomer(composeName, "Franklin", "Okolie", "Good Day"); // Output: "Good Day Franklin Okolie"
// THIS IS A CALLBACK FUNCTION
// IT IS PASSED AS AN ARGUMENT TO A FUNCTION

function composeName(firstName, lastName) {
  const fullName = `${firstName} ${lastName}`;

  return fullName;
}

// THIS IS A HIGHER ORDER FUNCTION
// IT ACCPEPTS A FUNCTION AS A ARGUMENT

function greetCustomer(composerFunc, firstName, lastName, salutation) {
  const fullName = composerFunc(firstName, lastName);

  console.log(`${salutation} ${fullName}`);
}

greetCustomer(composeName, "Franklin", "Okolie", "Good Day");

// Output: "Good Day Franklin Okolie"

composeName
composeName یک تابع callback است که به عنوان آرگومان به تابع
greetCustomer
greetCustomerکه یک تابع higher order ارسال شده و در داخل این تابع اجرا می‌شود.

تفاوت بین توابع Higher Order و توابع Callback

مهم است که تفاوت بین این دو عبارت را درک کنیم تا بتوانیم با هم تیمی‌های خود و در طول مصاحبه‌های فنی، ارتباط واضح‌تری برقرار کنیم:

جمع‌بندی

همانطور که در این مقاله دیدیم، توابع در جاوااسکریپت بسیار انعطاف‌پذیر هستند و می‌توانند به روش‌های مفید زیادی مورد استفاده قرار بگیرند. این انعطاف‌پذیری همچنین منجر به دو اصطلاح فنی رایج در جاوااسکریپت می‌شود که عبارتند از: توابع higher order و توابع callback.