بس

بستار در JavaScript

1 تمرین

درباره‌ی بستار

«بستار» یک الگوی برنامه‌نویسی در JavaScript است که اجازه می‌دهد از متغیرهای یک دامنه‌ی واژگانی بیرونی درون یک «بلوک کد» تودرتو استفاده شود. JavaScript از بستارها به‌صورت شفاف پشتیبانی می‌کند و اغلب بدون آنکه بدانیم چیستند از آن‌ها استفاده می‌شود.

// Top-level declarations are global-scope
const dozen = 12;

// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
  return dozen * n;
}

نام بستار از نظر تاریخی از λ-calculus گرفته شده و scheme آن را رواج داده است (منبع) تا به اتصال‌های متغیرِ باز و بسته‌ی یک تابع اشاره کند.

دلایل استفاده از بستارها در JavaScript

  1. حریم خصوصی داده‌ها / کپسوله‌سازی داده‌ها

    • برخلاف زبان‌های دیگر، در سال ۲۰۲۰ راهی برای مشخص کردن متغیرهای خصوصی وجود نداشت. بنابراین می‌توان از بستارها برای شبیه‌سازی مؤثر متغیرهای خصوصی استفاده کرد (پیشنهادی برای معرفی نشانه‌گذاری متغیر خصوصی وجود داشت که ممکن است تا زمانی که این را می‌خوانید استاندارد شده باشد).
    // Consider this function:
    
    const saveNumber = (number) => {
      // The saved value is unaccessible by the outer lexical scope.
      const value = number;
    
      // We can provide access to the primitive value with a function, but the original will never change
      return () => value;
    };
    
    // Attempting to set the variable outside of its lexical scope results in an error
    value = 42;
    
  2. اعمال جزئی

    • توابع ممکن است توابعی را برگردانند و زمانی که یک تابع برگردانده‌شده از آرگومان تابعی که آن را ساخته استفاده می‌کند، این نمونه‌ای از به‌کارگیری بستار برای انجام اعمال جزئی است. گاهی به این کار کارینگ یک تابع می‌گویند.
    // The arguments are applied one-by-one, using closures to arrive at the final result
    function partialBuildUri(scheme) {
      return function (domain) {
        return function (path) {
          return `${scheme}://${domain}/${path}`;
        };
      };
    }
    
    // A function could apply them all at once.
    function buildUri(scheme, domain, path) {
      return partialBuildUri(scheme)(domain)(path);
    }
    
    // Or apply a few to create a flexible system of functions for reuse.
    function buildHttpsExercismUri(path) {
      return partialBuildUri('https')('exercism.org');
    }
    

دامنه‌ی واژگانی برای متغیرها

در هر زبان برنامه‌نویسی، بستارها ارتباط نزدیکی با دامنه‌ی متغیرها دارند. در JavaScript سه نوع دامنه‌ی واژگانی برای متغیرها وجود دارد:

  1. دامنه‌ی سراسری: متغیرهایی هستند که بیرون از توابع و بلوک‌ها اعلام می‌شوند.
  2. دامنه‌ی تابع: متغیرهایی هستند که درون یک تابع با کلیدواژه‌ی var اعلام می‌شوند.
  3. دامنه‌ی بلوک: متغیرهایی هستند که درون یک بلوک ({...}) با کلیدواژه‌های let و const اعلام می‌شوند.

در عمل، بستار اغلب زمانی رخ می‌دهد که یک تابع (یا بلوک) از متغیری در دامنه‌ی بیرونی استفاده کند. در JavaScript این کار به‌صورت شفاف پشتیبانی می‌شود، به همین دلیل اغلب بستارها بدون اشاره‌ی مشخص به کار می‌روند.

دامنه‌ی تابع

کلیدواژه‌ی var یک متغیر با دامنه‌ی تابع تعریف می‌کند. یعنی متغیرهایی که با var تعریف می‌شوند، در هر جای تابعی که در آن تعریف شده‌اند و در هر دامنه‌ی واژگانی تودرتوی دیگری (چه تابع و چه بلوک) در دسترس هستند.

{
  var five = 5;
}

// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;

دامنه‌ی بلوک

کلیدواژه‌های let و const متغیرهایی با دامنه‌ی بلوک تعریف می‌کنند. let یک متغیر تغییرپذیر تعریف می‌کند که مقدارش ممکن است چندین بار تنظیم شود و ممکن است تعریف شود ولی مقداردهی اولیه نشود. const متغیری تعریف می‌کند که هنگام تعریف باید مقداردهی اولیه شود و پس از آن مقدارش نمی‌تواند تغییر کند. اگر متغیر const به یک مقدار ابتدایی مقداردهی شود، نمی‌توان آن را تغییر داد. اگر متغیر const به یک مقدار ارجاعی، مانند یک آرایه یا شیء، مقداردهی شود، خود ارجاع نمی‌تواند تغییر کند اما می‌توان محتویات آرایه را تغییر داد.

این بلوک را در نظر بگیرید:

// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;

{
  // This succeeds, as 'two' is available from the outer lexical scope
  const oneLess = two - 1;
}

// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);

هرچند می‌توانیم از آکولادها ({...}) به‌صورت دلخواه برای ساختن بلوک استفاده کنیم، بیشتر اوقات آن‌ها را پیرامون دستورهای شرطی، توابع و کلاس‌ها می‌بینیم.

const PI = 3.14;

// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
  return PI * radius * radius;
}
ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

بستار را یاد بگیرید