11 مفهموم جاوااسکریپت که توانایی‌های شما را افزایش می‌دهد

گردآوری و تالیف : مهدی عقیقی
تاریخ انتشار : 01 اسفند 1398
دسته بندی ها : جاوا اسکریپت

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

این لیست به طور مداوم در این پروژه‌ی گیتهاب آپدیت می‌شود.

مقدارها در مقابل تعیین متغیر مرجع

برای نوشتن یک برنامه‌ی بدون باگ در جاوااسکریپت؛ لازم است بفهمیم که جاوااسکریپت چگونه به متغیرها مقداردهی می‌کند. اگر شما این را نمی‌دانید؛به راحتی می‌توانید کدی بنویسید که مقدار متغیرها را سهوا تغییر دهد.

جاوااسکریپت همیشه با استفاده از مقدارها متغیرها را مقداردهی می‌کند. اما یک چیز خیلی مهم است؛ وقتی مقدار، یکی از پنج نوع ابتدایی است؛ یعنی (Booleam, null, undefined, String, Number) همیشه مقدار واقعی اختصاص داده می‌شود. اما وقتی مقدار؛ یک آرایه، آبجکت، یا تابع باشد، یک رفرنس (مرجع) به آن شیء در حافظه، به متغیر اختصاص داده می‌شود.

مثال: در کد زیر var2 برابر با var1 قرار داده شده است. تا زمانی که var1 یکی از 5 نوع اصلی می‌باشد، var2 فقط برابر با مقدار رشته‌ی var1 قرار داده شده است و می‌توان آن را کاملا جدا از var1 دانست. بر این اساس، تغییر مقدار var2 روی var1 تاثیری نمی‌گذارد.

let var1 = 'My string';
let var2 = var1;var2 = 'My new string';console.log(var1);
// 'My string'
console.log(var2);
// 'My new string'

حالا بیایید با حالت Object آن را مقایسه کنیم.

let var1 = { name: 'Jim' }
let var2 = var1;var2.name = 'John';console.log(var1);
// { name: 'John' }
console.log(var2);
// { name: 'John' }

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

  • Closures

Closure ها یکی از الگوهای مهم جاوااسکریپت برای دادن دسترسی خصوصی به یک متغیر می‌باشد. در مثال زیر، تابع createGreeting یک تابع ناشناس برمی‌گرداند که به متغیر greeting، یعنی “Hello” دسترسی دارد. برای تمام استفاده‌های آینده، تابع sayHello می‌تواند از آن greeting استفاده کند.

function createGreeter(greeting) {
  return function(name) {
    console.log(greeting + ', ' + name);
  }
}const sayHello = createGreeter('Hello');
sayHello('Joe');
// Hello, Joe

در یک مثال واقعی می‌تونید تصور کنید که شما یک تابع apiConnect دارید، که پارامتر apiKey را برای همه‌ی درخواست‌ها ارسال می‌کند. حالا در این مثال شما با استفاده از این مفهموم می‌توانید apiKey را فقط یک بار به apiConnect بدهید و در این تابع انواع متخلف تابع‌های درخواستی را پیاده سازی کنید.

function apiConnect(apiKey) {
  function get(route) {
    return fetch(`${route}?key=${apiKey}`);
  }  function post(route, params) {
    return fetch(route, {
      method: 'POST',
      body: JSON.stringify(params),
        headers: {
          'Authorization': `Bearer ${apiKey}`
        }
      })
  }  return { get, post }
}const api = apiConnect('my-secret-key');// No need to include the apiKey anymore
api.get('http://www.example.com/get-endpoint');
api.post('http://www.example.com/post-endpoint', { name: 'Joe' });
  • Destructuring

یک روش خیلی راحت برای گرفتن پارامترهای خاص در جاوااسکریپت؛ Destructuring است. به مثال زیر توجه کنید.

const obj = {
  name: 'Joe',
  food: 'cake'
}const { name, food } = obj;
console.log(name, food);
// 'Joe' 'cake'

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

const obj = {
  name: 'Joe',
  food: 'cake'
}const { name: myName, food: myFood } = obj;
console.log(myName, myFood);
// 'Joe' 'cake'

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

const person = {
  name: 'Eddie',
  age: 24
}function introduce({ name, age }) {
  console.log(`I'm ${name} and I'm ${age} years old!`);
}
console.log(introduce(person));
// "I'm Eddie and I'm 24 years old!"
  • Spread Syntax

یکی دیگر از مفاهیمی که می‌تواند افراد را از جاوااسکریپت دور کند اما نسبتا ساده است؛ Spread Operator ها هستند. در مثال پایین، تابع  Math.max نمی‌تواند یک آرایه به عنوان پارامتر دریافت کند بلکه باید هر عدد را جداگانه به عنوان یک پارامتر به آن داد. Spread Operator می‌تواند برای بیرون کشیدن ایندکس‌های مختلف از یک آرایه استفاده شود.

const arr = [4, 6, -1, 3, 10, 4];
const max = Math.max(...arr);
console.log(max);
// 10
  • Rest Syntax

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

function myFunc(...args) {
  console.log(args[0] + args[1]);
}
myFunc(1, 2, 3, 4);
// 3
  • تابع‌های آرایه‌ها

تابع‌های آرایه‌های جاوااسکریپت در بیشتر مواقع می‌تواند به شما راه‌‌حل‌های ظریف و باورنکردنی دهد تا اطلاعات خودتان را هرطور که می‌خواهید تغییر دهید. به عنوان یک مشارکت کننده در Stack Overflow می‌توانم این را بگویم که بیشتر مواقع سوال‌هایی را می‌بینم که مربوط به دستکاری اطلاعات مربوط به داخل آرایه‌ها و آبجکت‌ها می‌شوند.

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

1. Map, Filter, Reduce

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

  • Map: این تابع، آرایه‌ای را برمی‌گرداند که اطلاعات در آن دستکاری شده و توسط فانکشن مشخص شده باشد.
const arr = [1, 2, 3, 4, 5, 6];
const mapped = arr.map(el => el + 20);console.log(mapped);
// [21, 22, 23, 24, 25, 26]
  • Filter: این تابع، آرایه‌ای از المنت‌ها را زمانی که تابع مشخص شده؛ true را برمی‌گرداند را به شما می‌دهد.
const arr = [1, 2, 3, 4, 5, 6];
const filtered = arr.filter(el => el === 2 || el === 4);console.log(filtered);
// [2, 4]
  • Reduce: مقادیر را طوری که تابع مشخص کرده در آرایه می‌ریزد.

find, findIndex, indexOf .2 

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

  • Find: اولین مقداری را که با شرط شما سازگار باشد برمی‌گرداند. البته برای پیدا کردن بقیه مثال‌ها پیشروی نمی‌کند.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const found = arr.find(el => el > 5);console.log(found);
// 6
  • findIndex: این تابع هم همانند find برای پیدا کردن عمل می‌کند. اما به جای برگرداندن اولین مقدار مطابق، ایندکس آن مقدار را برمی‌گرداند. به مثال زیر که به جای اعداد از اسم‌ها استفاده می‌کند، توجه کنید.
const arr = ['Nick', 'Frank', 'Joe', 'Frank'];
const foundIndex = arr.findIndex(el => el === 'Frank');
console.log(foundIndex);
// 1
  • indexOf: مانند findIndex عمل می‌کند؛ اما به جای دریافت یک تابع به عنوان پارامتر، یک مقدار ساده را دریافت می‌کند. شما می‌توانید از indexOf زمانی که شروط راحت‌تری دارید و برای پیدا کردن المنت نیازی به تابع ندارید استفاده کنید.
const arr = ['Nick', 'Frank', 'Joe', 'Frank'];
const foundIndex = arr.indexOf('Frank');
console.log(foundIndex);
// 1

3. Push, pop, shift, unshift

راه‌های بسیار عالی برای اضافه یا کم کردن المنت‌ها از آرایه.

  • Push: این فقط یک متد ساده است که مقداری را به انتهای آرایه شما اضافه می‌کند. این تابع همان آرایه قبلی را ویرایش می‌کند و خود فانکشن مقدار اضافه شده را برمی‌گرداند.
let arr = [1, 2, 3, 4];
const pushed = arr.push(5);console.log(arr);
// [1, 2, 3, 4, 5]
console.log(pushed);
// 5
  • Pop: این متد آخرین اندیس آرایه را حذف می‌کند و مانند تابع بالایی همان آرایه قبلی را ویرایش می‌کند و مقدار پاک شده را برمی‌گرداند.
let arr = [1, 2, 3, 4];
const popped = arr.pop();console.log(arr);
// [1, 2, 3]
console.log(popped);
// 4
  • Shift: این متد اولین مقدار آرایه را حذف می‌کند و مانند توابع بالا خود آرایه را ویرایش می‌کند و مقدار پاک شده را برمی‌گرداند.
let arr = [1, 2, 3, 4];
const shifted = arr.shift();console.log(arr);
// [2, 3, 4]
console.log(shifted);
// 1
  • Unshift: این تابع یک آیتم به اول آرایه اضافه می‌کند و مانند بالا خود آرایه را ویرایش می‌کند؛ اما بر خلاف توابع بالا، تعداد آیتم‌های داخل آرایه را برمی‌گرداند.

4. Slice, splice

این توابع زیرآرایه‌هایی، از آرایه‌های اصلی را برمی‌گردانند.

  • Splice: محتویات داخل آرایه را با حدف کردن یا جایگزین کردن المنت‌ها یا اضافه کردن المنت‌های جدید تغییر می‌دهد. این تابع خود آرایه را ویرایش می‌کند.

نمونه کد زیر را می‌توان به این حالت خواند: در موقعیت 1 از آرایه ، 0 عنصر را حذف کرده و b را وارد کنید. 

let arr = ['a', 'c', 'd', 'e'];
arr.splice(1, 0, 'b')
  • Slice: یک کپی از آرایه، با شروع از نقطه‌ی خاص و پایان در نقطه‌ی خاص را برمی‌گرداند که اگر نقطه پایان تعریف نشود؛ بقیه‌ی آرایه را برمی‌گرداند. و البته بر خلاف متدهای بالا این تابع روی خود آرایه تاثیری نمی‌گذارد و آرایه‌ای که به دست می‌آورد را برمی‌گرداند.
let arr = ['a', 'b', 'c', 'd', 'e'];
const sliced = arr.slice(2, 4);console.log(sliced);
// ['c', 'd']
console.log(arr);
// ['a', 'b', 'c', 'd', 'e']

5. Sort

  • Sort: آرایه را بر اساس تابعی که به آن می‌دهیم مرتب می‌کند؛ که آن تابع آرگومان اول و دوم را می‌گیرد و همان تابع را ویرایش می‌کند. اگر عدد منفی یا 0 برگرداند؛ ترتیب آیتم‌ها تفاوتی نکرده است، اما اگر عدد مثبت برگرداند؛ یعنی ترتیب را عوض کرده.
let arr = [1, 7, 3, -1, 5, 7, 2];
const sorter = (firstEl, secondEl) => firstEl - secondEl;
arr.sort(sorter);console.log(arr);
// [-1, 1, 2, 3, 5, 7, 7]

Generators .6

از * نترسید. جنراتورها مشخص می‌کنند که بار بعدی که متد next() فراخوانی شد، چه چیزی را برگردانند. که می‌تواند تعداد بی نهایتی yield داشته باشد. و هر کجا که undefined برگرداند yield ها تمام شده است.

function* greeter() {
  yield 'Hi';
  yield 'How are you?';
  yield 'Bye';
}const greet = greeter();console.log(greet.next().value);
// 'Hi'
console.log(greet.next().value);
// 'How are you?'
console.log(greet.next().value);
// 'Bye'
console.log(greet.next().value);
// undefined

و برای استفاده از جنراتورها برای yield های بی‌نهایت:

function* idCreator() {
  let i = 0;
  while (true)
    yield i++;
}const ids = idCreator();console.log(ids.next().value);
// 0
console.log(ids.next().value);
// 1
console.log(ids.next().value);
// 2
// etc... 

7. نشانه‌ی هویت (===) و نشانه‌ی برابری (==)

مطمئن باشید که تفاوت بین === و == را می‌دانید. === همیشه قبل از مقایسه‌ی مقدارها؛ اول نوع داده‌ها را مقایسه می‌کند در صورتی که == مقایسه بین نوع داده‌ها را انجام نمی‌دهد.

console.log(0 == '0');
// true
console.log(0 === '0');
// false

8. مقایسه آبجکت‌ها

یکی از اشتباهاتی که تازه‌کاران در جاوااسکریپت زیاد انجام می‌دهند، مقایسه کردن آبجکت‌ها به طور مستقیم است. متغیرها مراجعی برای رسیدن به آبجکت‌ها در مموری هستند، نه خود آبجکت‌ها! یکی از کارهایی که برای مقایسه آبجکت‌ها می‌توانید انجام دهید؛ تبدیل آنها به JSON و مقایسه‌ی آن‌هاست. این کار یک مشکل دارد: ترتیب پراپرتی‌ها تضمینی نیست و ممکن است فرق کند. یکی از کارهایی که می‌توانید انجام بدهید این است که از یک کتابخانه‌ی خارجی مانند loadash استفاده کنید.

در مثال پایین آبجکت‌ها به نظر با هم مساوی هستند اما در واقع آن‌ها به دو مرجع مختلف تعلق دارند.

const joe1 = { name: 'Joe' };
const joe2 = { name: 'Joe' };console.log(joe1 === joe2);
// false

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

const joe1 = { name: 'Joe' };
const joe2 = joe1;
console.log(joe1 === joe2);
// true

9. Callback ها

بسیاری از مردم از کالبک‌های جاوااسکریپت می‌ترسند. آنها بسیار ساده هستند. این مثال را ببینید.

Console.log() به عنوان یک کالبک به myFunc پاس داده می‌شود. این یعنی هر وقت setTimeout تمام شود، console.log() انجام می‌شود.

به همین سادگی.

function myFunc(text, callback) {
  setTimeout(function() {
    callback(text);
  }, 2000);
}myFunc('Hello world!', console.log);
// 'Hello world!'

10. Promises

حالا که شما مفهموم callback ها رو فهمیدین، احتمالا دچار "جهنم callback های تو در تو" بشوید. این‌جا جایی‌ست که promise ها کمک می‌کنند. تمام برنامه‌ی async خود را داخل یک Promise بگذارید و در حالت موفقیت resolve و وقتی ناموفق بود reject کنید.

از then برای رسیدگی به مواقعی که موفقیت آمیز بود و از catch برای مواقعی که کار موفقیت آمیز نبود استفاده کنید.

const myPromise = new Promise(function(res, rej) {
  setTimeout(function(){
    if (Math.random() < 0.9) {
      return res('Hooray!');
    }
    return rej('Oh no!');
  }, 1000);
});myPromise
  .then(function(data) {
    console.log('Success: ' + data);
   })
   .catch(function(err) {
    console.log('Error: ' + err);
   });
   
// If Math.random() returns less than 0.9 the following is logged:
// "Success: Hooray!"
// If Math.random() returns 0.9 or greater the following is logged:
// "Error: On no!"

11. Asnyc, await

وقتی promise ها را یاد گرفتید. باید از async و await نیز خوشتان بیاید.که یه سینتکس پراستفاده در promise ها است. در مثال زیر، ما یک تابع async درست می‌کنیم و در میان آن پرامیس greeter را await می‌کنیم.

const greeter = new Promise((res, rej) => {
  setTimeout(() => res('Hello world!'), 2000);
})async function myFunc() {
  const greeting = await greeter;
  console.log(greeting);
}myFunc();
// 'Hello world!'

نتیجه

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

منبع

مقالات پیشنهادی

  • 11 تکنیک جاوااسکریپت

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

    ارسطو عباسی