بستار یک الگوی برنامهنویسی در JavaScript است که اجازه میدهد متغیرها از یک دامنهی واژگانی بیرونی درون یک بلوک کد تودرتو به کار گرفته شوند. JavaScript از بستارها بهشکل شفاف پشتیبانی میکند و اغلب بدون آنکه بدانید چه هستند، از آنها استفاده میشود.
// Top-level declarations are global-scope
const dozen = 12;
{
// Braces create a new block-scope
// Referencing the outer variable is a closure.
const twoDozen = dozen * 2;
}
// Because of the block-scope declaration, twoDozen is not available here.
twoDozen;
// => Uncaught ReferenceError: twoDozen is not defined
علاوه بر آکولادها {}، توابع (و کلاسها) هم دامنههای تازهای میسازند و میتوانند مقادیر را در خود محصور کنند:
const dozen = 12;
// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
// This is declared inside the function scope, and uses the top-level scope.
// This works, and encloses the value 12.
const twoDozen = dozen * 2;
// This only uses the locally declared variable and the passed argument to the parameter `n`
return (twoDozen / 2) * n;
}
// Because of the function-scope declaration, twoDozen is not available here.
twoDozen;
// => Uncaught ReferenceError: twoDozen is not defined
همانطور که مثالهای twoDozen نشان میدهند، مقادیر را میتوان در یک دامنهی تودرتو (تابع، بلوک و غیره) محصور کرد، اما نمیتوان آنها را از آن بافت بیرون کشید.
در بیشتر موارد، در JavaScript مدرن اینگونه در نظر گرفته شده که یک مقدار به دامنهی بیرونی نشت نکند.
استفاده از یک اعلان متغیر تغییرپذیر (مانند let یا var) امکان حفظ بخشی از وضعیت را فراهم میکند:
let counter = 0;
// This function closure increments the counter's state in the outer lexical context.
// This way the counter can be shared between many calling contexts.
export function increment() {
counter += 1;
return counter;
}
increment();
// => 1
counter;
// => 1
با ترکیب این دو ایده، یعنی محصور کردن یک مقدار برای حفظ وضعیت و اینکه مقادیر محصورشده به بیرون نشت نمیکنند، میتوان مقادیر خصوصی ساخت.
رایجترین روش این است که تابعی بسازیم که تابعی را برمیگرداند که وضعیتی را در خود محصور میکند.
export function makeCounter() {
let counter = 0;
// This returns a new function that encloses the local variable counter
return function increment() {
counter += 1;
return counter;
};
}
// Counter did not leak
counter;
// => Uncaught ReferenceError: counter is not defined
// This creates a new counter.
// This assigns the increment function to the variable myFirstCounter.
const myFirstCounter = makeCounter();
typeof myFirstCounter;
// => function
myFirstCounter.name;
// => increment
myFirstCounter();
// => 1
myFirstCounter();
// => 2
// This creates new counter (with new, separate local state / enclosed counter variable)
const mySecondCounter = makeCounter();
mySecondCounter();
// => 1
// It does not affect the first counter.
myFirstCounter();
// => 3
بسیاری از برنامهنویسان بستار را مفهومی دشوار میدانند و در همهی زبانهای برنامهنویسی، برگرداندن یک تابع از داخل یک تابع رایج نیست یا حتی امکانپذیر هم نیست. اگر به مطالب بیشتری برای مطالعه نیاز دارید، راهنمای MDN دربارهی بستارها بسیار جامع است.
شرکت طراحی شما تا به حال بیشتر با تبدیلهای CSS برای ساخت صفحات وب کار میکرده است. پس از گفتوگوهایی، تصمیم گرفته میشود که از این پس از JavaScript برای انجام برخی محاسبات بهصورت پویا استفاده شود. برخی از همتیمیهای شما تجربهی کمتری با JavaScript دارند، بنابراین تصمیم میگیرید از یک «بستار» تابع استفاده کنید تا تبدیلی قابل استفادهی مجدد برای جفتهای مختصات {x, y} بسازید.
تابع translate2d را پیادهسازی کنید که تابعی را برمیگرداند که با استفاده از یک بستار، انتقال دوبعدی تکرارپذیر یک جفت مختصات را انجام میدهد.
در هندسه، انتقال به جابهجا کردن نقاط، بردارها یا شکلها به یک فاصله در یک جهت گفته میشود. میتوان آن را افزودن یک مقدار ثابت به هر نقطه تفسیر کرد.
const moveCoordinatesRight2Px = translate2d(2, 0);
const result = moveCoordinatesRight2Px(4, 8);
// result => [6, 8]
تابع scale2d را پیادهسازی کنید که تابعی را برمیگرداند که با استفاده از یک بستار، مقیاس دوبعدی تکرارپذیر یک جفت مختصات را انجام میدهد.
در هندسه، مقیاسکردن یکنواخت به بزرگ یا کوچک کردن بردارها یا شکلها در یک جهت گفته میشود. میتوان آن را ضرب هر نقطه در یک مقدار ثابت (ضریب مقیاس) تفسیر کرد.
در این تمرین، فرض کنید مقادیر مقیاس فقط مثبتاند.
const doubleScale = scale2d(2, 2);
const result = doubleScale(6, -3);
// result => [12, -6]
دو تابع تبدیل را ترکیب کنید تا تبدیلی تکرارپذیر انجام دهید. به این کار اغلب ترکیب توابع میگویند؛ در آن، نتیجهی تابع اول 'f(x)' بهعنوان ورودی تابع دوم 'g(x)' استفاده میشود.
const moveCoordinatesRight2Px = translate2d(2, 0);
const doubleCoordinates = scale2d(2, 2);
const composedTransformations = composeTransform(
moveCoordinatesRight2Px,
doubleCoordinates,
);
const result = composedTransformations(0, 1);
// result => [4, 2]
تابع memoizeTransform را پیادهسازی کنید. این تابع، تابعی را برای حافظهسپاری میگیرد و سپس تابع جدیدی برمیگرداند که ورودیهای تابع دادهشده را به خاطر میسپارد، به این ترتیب آخرین مقدار بازگشتی «به خاطر سپرده میشود» و اگر با همان آرگومانها دوباره فراخوانی شود، فقط یکبار محاسبه میشود.
حافظهسپاری گاهی در برنامهنویسی پویا استفاده میشود. این کار اجازه میدهد عملیات پرهزینه فقط یکبار انجام شوند، چون نتایجشان به خاطر سپرده میشوند. توجه کنید که در این تمرین فقط آخرین نتیجه به خاطر سپرده میشود، برخلاف برخی راهحلها در برنامهنویسی پویا که همهی نتایج را حافظهسپاری میکنند.
const tripleScale = scale2d(3, 3);
const memoizedScale = memoizeTransform(tripleScale);
memoizedScale(4, 3); // => [12, 9], this is computed since it hasn't been computed before for the arguments
memoizedScale(4, 3); // => [12, 9], this is remembered, since it was computed already
در Exercism ثبتنام کنید تا JavaScript را همراه با 37 مفهوم159 تمرین و مربیگری انسانی واقعی یاد بگیرید و در آن استاد شوید، همهی اینها رایگان.