کتاب آماده سازی مصاحبه جاوا اسکریپت – موضوعات ضروری برای دانستن + نمونه کد
جاوا اسکریپت زبانی است که به طور گسترده در توسعه وب مورد استفاده قرار می گیرد و به ویژگی های تعاملی تقریباً هر وب سایتی قدرت می دهد. جاوا اسکریپت ایجاد صفحات وب پویا را ممکن می سازد و بسیار متنوع است.
جاوا اسکریپت همچنان یکی از پرتقاضاترین زبان های برنامه نویسی در سال 2024 است. بسیاری از شرکت ها به دنبال مهارت در جاوا اسکریپت و یکی از چارچوب های آن مانند Angular و React هستند. اگر شما یک توسعهدهنده وب مشتاق هستید، درک آنچه این شرکتها در مصاحبهها به دنبال آن هستند، کلید باز کردن فرصتهای عالی است.
در این کتاب راهنما، چندین مفهوم اساسی جاوا اسکریپت را که باید قبل از رفتن به مصاحبه آماده کنید، تحلیل خواهم کرد. با تجهیز به اصول و مفاهیم زیر، خود را به عنوان یک نامزد چشمگیر در چشم انداز توسعه وب قرار خواهید داد.
فهرست مطالب
نحوه استفاده از کلمات کلیدی var
، let
و const
.
بسته شدن ها چگونه کار می کنند؟
this
کلمه کلیدی چگونه کار می کند؟
نحوه استفاده از روشهای call
، apply
و bind
.
نمونه های اولیه و وراثت اولیه چیست؟
نحوه استفاده از اپراتور Spread.
ساختار آرایه و شیء چگونه کار می کند؟
نحوه استفاده از کلمات کلیدی async
و await
.
انتشار رویداد چگونه کار می کند - حباب و ضبط.
نحوه پیاده سازی Polyfills برای Array.map()
, Array.reduce()
و Array.filter()
نحوه استفاده از کلمات کلیدی var
، let
و const
در جاوا اسکریپت میتوانید یک متغیر را به سه روش اعلام کنید: var
، let
و const
. درک تفاوت بین این سه ضروری است.
متغیر var
دارای دامنه جهانی و سطح عملکرد است. اگر متغیر به صورت سراسری اعلان شده باشد، در هر جایی می توان به آن دسترسی داشت و اگر در داخل یک تابع اعلان شود، در هر نقطه از تابع قابل دسترسی است.
var a=5 function fun() { var b=4 } console.log(a) // 5 console.log(b) // throws ReferenceError
let
متغیر دارای محدوده سطح بلوک است. این متغیر اگر در داخل یک بلوک اعلام شود، خارج از آن قابل دسترسی نیست. مثلا:
var a = 5; if (a > 1) { var b = 6; let c = 7; } console.log(a); // prints 5 console.log(b); // prints 6 console.log(c); // throws ReferenceError
در اینجا، متغیرهای a
و b
دارای دامنه جهانی هستند، پس می توان به آنها در هر نقطه دسترسی داشت. متغیر c
خارج از بلوک if
قابل دسترسی نیست زیرا let
فقط محدوده سطح بلوک دارد.
const
برای اعلام ثابت استفاده می شود. هنگامی که یک متغیر با const
اعلان شد، نمی توان آن را تغییر داد.
const x = 5; x = 6; // Throws an error
با این حال، می توانید ویژگی های یک شی یا عناصر یک آرایه را تغییر دهید.
const obj = { name: 'kunal', age: 21 }; obj.name = 'alex'; console.log(obj); // { name: 'alex', age: 21 } const arr = [1, 2, 3]; arr[1] = 4; console.log(arr); // [ 1, 4, 3 ]
بالا بردن چیست؟
Hoisting به رفتار پیشفرض جاوا اسکریپت اشاره دارد که همه متغیرها و اعلانهای تابع را به بالا منتقل میکند. این بدان معناست که می توانید قبل از اعلام آنها از آنها استفاده کنید.
x=5 console.log(x) // prints 5 var x
در کد بالا، جاوا اسکریپت اعلان متغیر را به بالای بلوک کد منتقل کرده است. یعنی: شبیه به اعلام x
در خط اول است.
در مورد توابع، اعلان ها نیز به بالا منتقل می شوند:
function foo() { console.log("foo called"); } foo(); // foo called
با این حال، این با let
و const
کار نمی کند.
x = 5; // throws ReferenceError let x; fiz(); // throws ReferenceError const fiz = () => { console.log("fiz called") };
بسته شدن چگونه کار می کند؟
بستن یک مفهوم مهم در جاوا اسکریپت است. وقتی تابعی در داخل تابع دیگری دارید، تابع داخلی به تمام متغیرهای تابع خارجی دسترسی دارد.
اما وقتی این تابع درونی توسط تابع خارجی برگردانده می شود، تابع درونی را می توان در هر جایی خارج از تابع بیرونی فراخوانی کرد و همچنان می تواند به آن متغیرها دسترسی داشته باشد.
function fun() { let count = 0; return () => { count++; console.log(count); }; } const innerFun = fun(); innerFun(); // prints 1 innerFun(); // prints 2 innerFun(); // prints 3
در اینجا fun()
یک count
متغیر را اعلام و مقداردهی اولیه می کند. سپس، یک تابع داخلی را برمیگرداند که قبل از چاپ آن، افزایشها count
. اکنون، زمانی که innerFun()
در هر جایی خارج از متد fun()
فراخوانی می کنید، همچنان می تواند به count
دسترسی داشته باشد و آن را افزایش دهد.
این مفهوم بسته شدن است. در پست زیر توسط ماتیاس هرناندز می توانید اطلاعات بیشتری در مورد بسته شدن کسب کنید.
نحوه پیاده سازی Debouncing
Debouncing تکنیکی است که فراخوانی تابع را چند ثانیه به تاخیر می اندازد و تضمین می کند که همیشه تاخیری بین فراخوانی تابع و اجرا وجود دارد.
هنگامی که یک تابع را فراخوانی می کنید، پس از یک تاخیر اجرا می شود. با این حال، اگر در آن تأخیر دوباره عملکرد را فراخوانی کنید، تماس قبلی لغو می شود و یک تایمر جدید شروع می شود. همین فرآیند برای هر فراخوانی تابع بعدی تکرار می شود.
بیایید نحوه اجرای آن را ببینیم:
function debounce(func, delay) { let timeout = null; return (...args) => { if (timeout) clearTimeout(timeout); timeout = setTimeout(() => { func(...args); timeout = null; }, delay); }; }
Debouncing یک تابع و یک تاخیر را به عنوان پارامتر می گیرد و یک تابع جدید و بازگردانده شده را برمی گرداند. وقتی تابع debounced را فراخوانی می کنید، پس از میلی ثانیه delay
اجرا می شود. اگر تابع در آن زمان فراخوانی شود، تماس قبلی را لغو می کند و دوباره منتظر delay
می ماند.
بیایید این رفتار را آزمایش کنیم:
function fun(a, b) { console.log(`This is a function with arguments ${a} and ${b}`); } const debouncedFun = debounce(fun, 500); debouncedFun(2, 3); debouncedFun(2, 3); debouncedFun(2, 3); // This is a function with arguments 2 and 3
دو تماس اول اجرا نمی شود، در حالی که سومی بعد از 500 میلی ثانیه اجرا می شود. Debouncing از مفهوم بسته شدن استفاده می کند، پس مهم است که ابتدا آنها را درک کنید.
Debouncing برنامه های زیادی دارد که محبوب ترین آنها قابلیت تکمیل خودکار در نوارهای جستجو است. من در پست زیر به تفصیل در مورد دباونینگ توضیح داده ام:
نحوه اجرای Throttling
Throttling تکنیکی است که سرعت فراخوانی یک تابع را محدود می کند. یک تابع throttled برای اولین بار اجرا می شود و فقط پس از یک تاخیر مشخص می توان دوباره آن را فراخوانی کرد. اگر با تاخیر فراخوانی شود، هیچ اتفاقی نمی افتد.
بیایید نحوه اجرای آن را ببینیم:
function throttle(func, delay) { let timeout = null; return (...args) => { if (!timeout) { func(...args); timeout = setTimeout(() => { timeout = null; }, delay); } }; }
throttle()
یک تابع و یک تاخیر را به عنوان پارامتر می گیرد و یک تابع throttled را برمی گرداند. وقتی تابع throttled را فراخوانی میکنید، برای اولین بار اجرا میشود و با delay
زمانبندی را شروع میکند. در این مدت، مهم نیست که چند بار تابع throttled را فراخوانی کنید، اجرا نمی شود.
بیایید این رفتار را آزمایش کنیم:
function fun(a, b) { console.log(`This is a function with arguments ${a} and ${b}`); } const throttledFun = throttle(fun, 500); throttledFun(2, 3); // This is a function with arguments 2 and 3 throttledFun(2, 3); setTimeout(() => { throttledFun(2, 3); }, 300); setTimeout(() => { throttledFun(2, 3); // This is a function with arguments 2 and 3 }, 600);
در اینجا، اولین تماس بلافاصله اجرا می شود و برای 500 میلی ثانیه بعدی، هیچ فراخوانی تابعی اجرا نمی شود. آخرین مورد اجرا می شود زیرا بعد از 500ms فراخوانی می شود.
Throttling همچنین از مفهوم closures استفاده می کند. من در پست خود به طور مفصل در مورد throttling توضیح داده ام، پس آن را تحلیل کنید:
Currying چیست؟
Currying تکنیکی است که در آن یک تابع با چندین آرگومان به دنبالهای از توابع تبدیل میشود و هر تابع یک آرگومان واحد را میگیرد و تابع دیگری را برمیگرداند. برای مثال تابع زیر را در نظر بگیرید:
function add(a, b, c) { return a + b + c; }
با Currying، تابع فوق را می توان به صورت زیر نوشت:
function curryAdd(a) { return function(b) { return function(c) { return a + b + c; }; }; }
در اینجا، هر تابع داخل curryAdd
یک آرگومان را می گیرد و تا زمانی که همه آرگومان ها جمع شوند، تابع دیگری را برمی گرداند. curryAdd
به عنوان یک تابع مرتبه بالاتر نیز شناخته می شود.
Currying به شما امکان می دهد تا از پیاده سازی های جزئی یک تابع استفاده مجدد کنید. اگر همه آرگومانها را در دسترس ندارید، میتوانید در ابتدا برخی از آرگومانهای تابع را اصلاح کنید و یک تابع قابل استفاده مجدد را برگردانید.
// Reusable function const addTwo = curryAdd(2); console.log(addTwo); // prints the function // Calling final result const result1 = addTwo(5)(10); console.log(result1); // 17 const result2 = addTwo(3)(5); console.log(result2); // 10
addTwo
یک تابع قابل استفاده مجدد است که می تواند بعداً، زمانی که آرگومان های اضافی در دسترس هستند، استفاده شود.
پس ، Currying ماژولار بودن و انعطافپذیری کد را با استفاده از عملکرد جزئی افزایش میدهد. همچنین به شما اجازه می دهد تا عملکردهایی ایجاد کنید که مطابق با نیازهای خاص مطابق با مثال بالا هستند.
Currying عملکردهای پیچیده را با تقسیم کردن آنها به بخش های ساده تر و قابل مدیریت ساده تر می کند. این منجر به کدهای تمیزتر و قابل خواندن می شود.
تفاوت بین ==
و ===
چیست؟
این بسیار ساده است، اما در مصاحبه ها بسیار رایج است.
let a = 1; let b = "1"; console.log(a == b); // true console.log(a === b); // false
==
فقط مقدار a
و b
را مقایسه می کند،
===
هم مقدار و هم نوع داده a
و b
را با هم مقایسه می کند
this
کلمه کلیدی چگونه کار می کند؟
this
کلمه کلیدی شیئی است که شما در حال حاضر به آن ارجاع می دهید. مقدار آن به زمینه ای که در آن از آن استفاده می کنید تنظیم می شود. هنگامی که به صورت سراسری ارجاع داده می شود، به شی پنجره اشاره this
.
console.log(this) // prints window {}
this
می تواند برای دسترسی به ویژگی های یک شی استفاده شود.
const obj = { name: 'kunal', age: 21, getInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); } }; obj.getInfo();
برای کسب اطلاعات بیشتر در مورد this
کلمه کلیدی به اسناد مراجعه کنید.
نحوه استفاده از روشهای call
، apply
و bind
هنگامی که this
در داخل یک تابع استفاده می کنید، مقدار آن به شیئی که تابع روی آن فراخوانی می شود تنظیم می شود. بیایید مثالی بزنیم:
function getInfo() { console.log(`Name: ${this.name}, Age: ${this.age}`); }
برای تنظیم مقدار this
کلمه کلیدی در یک متد call
, apply
و bind
استفاده می شود.
call
برای فراخوانی تابع getInfo()
روی یک شی، از تابع call
استفاده کنید. بیایید دو شی ایجاد کنیم و getInfo()
را روی این اشیاء فراخوانی کنیم.
const ob1 = { name: 'alex', age: 25 }; const ob2 = { name: 'marcus', age: 23 }; getInfo.call(ob1); // Name: alex, Age: 25 getInfo.call(ob2); // Name: marcus, Age: 23
call
مقدار this
کلمه کلیدی را در داخل یک تابع تنظیم می کند.
apply
متد apply
مشابه call
است، اما در نحوه ارسال آرگومان ها متفاوت است. تابعی را با آرگومان در نظر بگیرید:
function getInfo(a, b) { console.log(`Name: ${this.name}, Age: ${this.age}`); console.log(`Args: ${a} and ${b}`); } const obj = { name: 'alex', age: 25 }; getInfo.call(obj, 2, 3); getInfo.apply(obj, [2, 3]);
bind
bind
برای ایجاد یک تابع جدید استفاده می شود که this
کلیدی آن روی یک شی تنظیم شده است. اجازه دهید از تابع getInfo
بالا به عنوان مثال استفاده کنیم.
const obj = { name: 'alex', age: 25 }; const objGetInfo = getInfo.bind(obj, 2, 3); objGetInfo();
هنگامی که bind
در تابع getInfo()
فراخوانی می شود، تابع جدیدی را برمی گرداند که مقید به obj
است. اکنون، هر بار که تابع objGetInfo()
فرا میخوانید، this
کلمه کلیدی به obj
اشاره میکند.
هر سه روش مشابه هستند. یعنی مقدار this
کلمه کلیدی را تعیین می کنند. با این حال، یک تفاوت کلیدی در bind
این است که یک تابع جدید را برمی گرداند، در حالی که call
و apply
به سادگی فقط تابع را فراخوانی می کند.
نمونه های اولیه و وراثت اولیه چیست؟
وراثت مفهومی در برنامه نویسی شی گرا است که به یک شی اجازه می دهد تا ویژگی ها و روش ها را از یک شی دیگر به ارث ببرد. با این حال، وراثت در جاوا اسکریپت متفاوت عمل می کند.
در جاوا اسکریپت، هر شی دارای یک ویژگی است که به یک شی دیگر به نام نمونه اولیه پیوند می دهد. نمونه اولیه خود یک شی است که می تواند نمونه اولیه خود را داشته باشد، پس یک زنجیره نمونه اولیه را تشکیل می دهد. این زنجیره زمانی به پایان می رسد که به یک نمونه اولیه برابر با null
برسیم.
نمونه اولیه به شما اجازه می دهد تا متدها و ویژگی ها را از یک شی دیگر به ارث ببرید. هنگامی که یک ویژگی روی یک شی وجود ندارد، جاوا اسکریپت نمونه اولیه آن را جستجو می کند و به همین ترتیب تا زمانی که به انتهای زنجیره نمونه اولیه برسد.
بیایید با یک مثال بفهمیم.
let animal = { eats: true, walk() { console.log("Animal is walking"); } }; const rabbit = Object.create(animal); rabbit.jumps = true; rabbit.walk(); // Animal is walking
Object.create
یک rabbit
شی جدید با نمونه اولیه آن روی animal
ایجاد می کند. همچنین می توانید ویژگی های اضافی شی جدید را تنظیم کنید.
همچنین، متد walk()
در rabbit
وجود ندارد، پس نمونه اولیه animal
را جستجو میکند. این بدان معنی است که شی rabbit
خواص و روش های شی animal
را به ارث برده است.
همچنین می توانید از متد ES6 Object.setPrototypeOf
بر روی هر شیئی استفاده کنید.
const dog = { bark() { console.log("Dog barking"); } }; Object.setPrototypeOf(dog, animal); console.log(dog.eats); // true dog.walk(); // Animal is walking
همچنین می توانید از یک تابع به عنوان سازنده استفاده کنید و نمونه اولیه آن را با استفاده از ویژگی prototype
تنظیم کنید.
function Animal(name) { this.name = name; } Animal.prototype.walk = function () { console.log(`${this.name} is walking`); }; const dog = new Animal("Dog"); console.log(dog); // Animal { name: 'Dog' } dog.walk(); // Dog is walking
میتوانید در پست زیر توسط German Cocca درباره نمونههای اولیه و وراثت در جاوا اسکریپت اطلاعات بیشتری کسب کنید.
نحوه استفاده از اپراتور Spread
عملگر spread برای پخش کردن محتویات یک آرایه یا شی به عناصر جداگانه یا جمع آوری دسته ای از عناصر در یک شیء واحد استفاده می شود. موارد استفاده زیر را دارد:
عملگر Spread می تواند برای کپی کردن یک آرایه به آرایه جدید استفاده شود:
const arr1 = [2, 4, 5]; const arr2 = [...arr1]; console.log(arr1); // [2, 4, 5] console.log(arr2); // [2, 4, 5] console.log(arr1 == arr2); // false
همانطور که با عملگر برابری نشان داده شده است، arr1
و arr2
اشیاء کاملاً متفاوتی هستند.
همچنین میتوانید در حین ایجاد یک شی جدید، از فیلدهای یک شی استفاده مجدد کنید:
const obj1 = { name: 'kunal', age: 23 }; const obj2 = { ...obj1, gender: 'male', city: 'Mumbai' }; console.log(obj2); // { name: 'kunal', age: 23, gender: 'male', city: 'Mumbai' }
شما می توانید چندین آرگومان ارسال شده به یک تابع را در یک آرایه جمع آوری کنید.
function fun1(...args) { console.log(args); } fun1(1, 2, 3, 4, 5); // [ 1, 2, 3, 4, 5 ]
یا می توانید عناصر یک آرایه را به عنوان آرگومان های جداگانه به یک تابع ارسال کنید.
function fun2(a, b) { console.log(`${a} and ${b}`); } const numbers = [1, 2]; fun2(...numbers);
ساختار آرایه و شیء چگونه کار می کند؟
مشابه عملگر spread، می توانید عناصر یک آرایه یا یک شی را به متغیرهای جداگانه تقسیم کنید.
const arr = [1, 2, 3]; const [a, b, c] = arr; console.log(a); // 1 console.log(b); // 2 console.log(c); // 3
این برای اشیاء یکسان است:
const obj = { name: 'kunal', age: 22, gender: 'male' }; const {name, age, gender} = obj; console.log(name); // kunal console.log(age); // 22 console.log(gender); // male
وعده ها چیست؟
وعده ها یک مفهوم بسیار مهم در جاوا اسکریپت هستند که تقریباً در مصاحبه ها پرسیده می شوند. Promises برای عملیات ناهمزمان در جاوا اسکریپت مانند مهلت زمانی یا تماس های API استفاده می شود.
Promises از یک شی Promise استفاده می کند که در یکی از سه حالت وجود دارد: در حال انتظار، محقق شده (حل شده) و رد شده. هنگامی که یک عملیات ناهمزمان به پایان می رسد، یک وعده می تواند حل شود (موفق) یا رد شود (شکست).
بیایید یک مثال ساده بزنیم:
function asyncOperation() { return new Promise((resolve, reject) => { const x = 1 + Math.random() * 10; if (x < 5) resolve("Successful"); else reject("Error"); }); }
تابع فوق وعده ای را برمی گرداند که یک عملیات ناهمزمان را انجام می دهد.
در صورت موفقیت آمیز بودن عملیات، متد resolve
فراخوانی می شود تا نشان دهد که وعده محقق شده است.
اگر عملیات با شکست مواجه شود، روش reject
فراخوانی می شود تا نشان دهد که وعده رد شده است.
در این مثال، این روش ها به صورت تصادفی فراخوانی می شوند. برای رسیدگی به این وعده در کد خود، از روش های then
و catch
استفاده کنید.
asyncOperation() .then((res) => { console.log(res); }) .catch((err) => { console.log(err); });
متد then
یک تابع callback می گیرد که در صورت حل شدن وعده اجرا می شود. یک شی پاسخ را به عنوان آرگومان می گیرد که برابر با شی ای است که در متد resolve
ارسال می کنید.
متد catch
یک تابع callback می گیرد که در صورت رد شدن وعده اجرا می شود و یک شی خطا را به عنوان آرگومان می گیرد که در متد reject
ارسال می شود.
کد بالا "موفق" را چاپ می کند و "خطا" به صورت تصادفی.
جدا از اصول اولیه، شی Promise همچنین حاوی متدهای مفیدی است که با چندین وعده کار می کنند: Promise.all()
, Promise.any()
, Promise.race()
.
آموزش زیر را بخوانید تا با جزئیات بیشتر در مورد وعده ها بیاموزید:
نحوه استفاده از کلمات کلیدی async
و await
await
اجرای یک تابع را تا زمانی که یک وعده حل یا رد شود متوقف می کند. await
فقط می تواند در داخل یک تابع async
استفاده شود. بیایید مثالی بزنیم:
function dataPromise() { return new Promise(resolve => { setTimeout(() => resolve("Data retrieved"), 500); }); } async function fetchData() { try { const res = await dataPromise(); console.log(res); // Data retrieved (after 500ms) } catch(err) { console.log(err); } } fetchData();
هنگامی که dataPromise()
فراخوانی می شود، اجرای تابع به مدت 500 میلی ثانیه مکث می کند. بعد از قطعی شدن قول اجرا ادامه می یابد. برای رسیدگی به خطاها، کد را با یک بلوک try-catch
احاطه کنید.
کلمه کلیدی await
همچنین کار با چندین وعده را که یکی پس از دیگری اجرا می شوند آسان تر می کند.
function promise1() { return new Promise(resolve => { setTimeout(() => resolve("Promise 1 resolved"), 500); }); } function promise2() { return new Promise(resolve => { setTimeout(() => resolve("Promise 2 resolved"), 300); }); } async function fetchData() { try { const res1 = await promise1(); console.log(res1); // Promise 1 resolved (after 500ms) const res2 = await promise2(); console.log(res2); // Promise 2 resolved (after 300ms) } catch(err) { console.log(err); } } fetchData();
async
و await
کار با وعدهها را آسانتر میکند و همچنین کد شما را با حذف تودرتو در کد پاکتر و خوانا میکند.
حلقه رویداد چیست؟
حلقه رویداد مکانیسم عملیات ناهمزمان و مدیریت رویداد را توضیح می دهد. این یک مفهوم مهم در جاوا اسکریپت است که مدل زمان اجرا آن را توضیح می دهد و پس یکی از رایج ترین سوالات در مصاحبه ها است.
به جای ارائه توضیح مختصر، به نظر من باید آن را با جزئیات یاد بگیرید و آن را کاملا درک کنید. اسناد MDN را بخوانید تا با کمک نمودار، حلقه رویداد را با جزئیات درک کنید.
اگر ویدیوها را ترجیح می دهید، می توانید ویدیوی زیر توسط فیلیپ رابرتز را نیز تماشا کنید:
انتشار رویداد چگونه کار می کند - حباب و ضبط
انتشار رویداد زمانی اتفاق میافتد که یک رویداد توسط عنصر هدف و تمام اجداد آن ضبط و مدیریت شود. مثال زیر را در نظر بگیرید:
<body> <div id="box"> <button id="button">Click Me</button> </div> <script src="script.js"></script> </body>
وقتی روی دکمه کلیک می کنید، عنصر div
را نیز body
کلیک کرده اید. این رویداد در سراسر درخت DOM منتشر می شود. بیایید به تمام عناصر فوق، کنترل کننده ها را اضافه کنیم:
document.body.addEventListener("click", () => { console.log("Body clicked"); }); document.getElementById("box").addEventListener("click", () => { console.log("div clicked"); }); document.getElementById("button").addEventListener("click", () => { console.log("Button clicked"); });
انتشار رویداد به دو صورت انجام می شود:
حباب رویداد
هنگامی که روی دکمه کلیک می شود، اولین کنترل کننده رویداد دکمه فراخوانی می شود. سپس، رویداد حباب می شود تا درخت DOM و کنترلکنندههای رویداد والدین بهطور متوالی از والد بلافصل تا بالاترین جد فراخوانی میشوند. یعنی: به ترتیب عناصر div
و body
.
ثبت رویداد
این شبیه به حباب رویداد عمل می کند، اما برعکس. رویداد ابتدا توسط عنصر ریشه گرفته می شود، سپس از درخت DOM به سمت عنصر هدف حرکت می کند.
کنترل کننده های رویداد به ترتیب از عنصر ریشه تا عنصر هدف فراخوانی می شوند. این را می توان با ارسال true
به عنوان سومین پارامتر در تابع addEventListener()
بدست آورد
document.body.addEventListener("click", () => { console.log("Body clicked"); }, true);
با این حال، به نظر می رسد که این غیر سازنده است. پس از همه، کاربر فقط می خواهد روی دکمه کلیک کند، آنها هیچ ایده ای از ساختار درخت DOM ندارند. پس ، برای جلوگیری از این رفتار، میتوانیم از متد stopPropagation()
استفاده کنیم.
توابع ژنراتور چیست؟
توابع ژنراتور نوع خاصی از توابع هستند که می توانند اجرای خود را متوقف کرده و بعداً از سر بگیرند. آنها همچنین هر بار که اجرا را متوقف می کنند یک مقدار برمی گردانند.
توابع ژنراتور را می توان برای برگرداندن دنباله ای از مقادیر به روشی تکرار شونده بر خلاف توابع عادی که فقط یک بار برمی گردند استفاده کرد.
در زیر یک مثال اساسی از یک تابع ژنراتور آورده شده است:
function* generatorFunction() { yield 1; yield 2; }
یک تابع مولد با function*
اعلان می شود و از کلمه کلیدی yield
برای توقف اجرا و برگرداندن یک مقدار استفاده می شود. سینتکس بالا یک شی GeneratorFunction ایجاد می کند.
const gen = generatorFunction()
این شی از یک تکرار کننده برای اجرای یک تابع مولد استفاده می کند. iterator یک متد next()
ارائه می دهد که بدنه تابع را تا دستور بازده بعدی اجرا می کند و یک شی حاوی مقدار بازده و یک خاصیت done
(بولی) را برمی گرداند که نشان می دهد آیا تابع مولد به پایان خود رسیده است یا خیر.
بیایید تابع مولد را صدا کنیم:
console.log(gen.next().value); // 1 console.log(gen.next()); // { value: 2, done: false } console.log(gen.next()); // { value: undefined, done: true }
در اینجا، اولین فراخوانی به next()
1 و دومین فراخوانی 2 را به دست میدهد. آخرین فراخوانی چیزی را نشان نمیدهد و پرچم done
را روی true تنظیم میکند، زیرا دیگر عبارات yield
وجود ندارد.
همچنین می توانید با استفاده از حلقه for
روی یک تابع مولد حلقه بزنید:
for(value of generatorFunction()) { console.log(value) }
به این ترتیب می توانید اجرای یک تابع مولد را با ورود و خروج هر زمان که بخواهید کنترل کنید.
نحوه پیاده سازی Polyfills برای Array.map()
، Array.reduce()
و Array.filter()
جاوا اسکریپت از زمان پیدایش تا کنون بسیار تکامل یافته است. چندین روش و ساختار به جاوا اسکریپت اضافه شده است که قبلا وجود نداشت. اکثر مرورگرهای مدرن از آخرین نسخه جاوا اسکریپت استفاده می کنند.
با این حال، چندین برنامه هنوز در مرورگرهای قدیمیتری که از نسخههای قدیمی جاوا اسکریپت استفاده میکنند، در حال اجرا هستند. روشهای آرایه مانند map
، reduce
و filter
ممکن است در دسترس نباشند. پس ، ممکن است مجبور شوید برای این روش ها پلی فیل تهیه کنید.
Polyfills قطعه کدی است که عملکرد مدرنی را برای مرورگرهای قدیمیتری که از آن پشتیبانی نمیکنند، ارائه میکند. این تضمین می کند که کد شما در مرورگرها و نسخه های مختلف اجرا می شود.
اکثر شرکتها وبسایتهایی دارند که هنوز به کاربران و سیستمهایی که مرورگرهای قدیمی را اجرا میکنند، پاسخ میدهند. پس ، دانستن نحوه نوشتن polyfills برای روشهای پرکاربرد مهم است.
در مورد ما، ما قصد داریم برای متدهای Array.map
، Array.reduce
و Array.filter
polyfills بنویسیم. این بدان معنی است که ما به جای استفاده از موارد پیش فرض، پیاده سازی های خود را می نویسیم.
Array.map
این متد یک تابع callback را به عنوان پارامتر می گیرد، آن را روی هر عنصر آرایه اجرا می کند و یک آرایه جدید و اصلاح شده را برمی گرداند.
تابع callback سه آرگومان می گیرد: عنصر آرایه، شاخص و خود آرایه. دو آرگومان آخر اختیاری هستند.
Array.prototype.map = function(callback) { var newArray = []; for (var i = 0; i < this.length; i++) { newArray.push(callback(this[i], i, this)); } return newArray; };
منطق ساده است. تابع هر عنصر آرایه را فراخوانی کرده و هر مقدار را به آرایه جدید اضافه کنید. کلمه کلیدی this
شیئی است که تابع را بر روی آن فراخوانی می کنید، در این مورد، آرایه.
Array.filter
این روش همچنین تابع callback را به عنوان پارامتر می گیرد. تابع callback یک شرط را روی هر عنصر آرایه اجرا می کند و یک مقدار Boolean برمی گرداند. روش filter
یک آرایه فیلتر شده جدید حاوی عناصری را برمی گرداند که شرایط را برآورده می کند.
این تابع فراخوانی سه آرگومان می گیرد: عنصر آرایه، شاخص و خود آرایه. دو آرگومان آخر اختیاری هستند.
Array.prototype.filter = function(callback) { var filteredArr = []; for (var i = 0; i < this.length; i++) { var condition = callback(this[i], i, this); if (condition) { filteredArr.push(this[i]); } } return filteredArr; };
در اینجا، از مقدار Boolean برگردانده شده توسط تابع callback برای گفت ن عناصر به آرایه جدید استفاده کنید.
Array.reduce
این روش یک تابع callback و یک مقدار اولیه را به عنوان پارامتر می گیرد و آرایه را به یک مقدار کاهش می دهد. این کار با اجرای تابع روی آکومولاتور و مقدار جریان و ذخیره نتیجه در انباشته انجام می شود.
تابع callback چهار آرگومان می گیرد: انباشتگر، عنصر جاری، ایندکس و خود آرایه. دو آرگومان آخر اختیاری هستند.
Array.prototype.reduce = function(callback, initialValue) { var accumulator = initialValue; for (var i = 0; i < this.length; i++) { if (accumulator !== undefined) { accumulator = callback(accumulator, this[i], i, this); } else { accumulator = this[i]; } } return accumulator; };
در ابتدا، انباشته را روی مقدار اولیه تنظیم کنید. تابع callback را برای هر عنصر آرایه اجرا کنید و نتیجه را در accumulator ذخیره کنید. اگر انباشت کننده تعریف نشده است، آن را روی خود عنصر تنظیم کنید.
بیایید این روش ها را آزمایش کنیم:
const arr = [1, 2, 3]; console.log(arr.map(ele => ele * 2)); // [ 2, 4, 6 ] console.log(arr.filter(ele => ele < 2)); // [ 1 ] console.log(arr.reduce((total, ele) => total + ele, 0)); // 6
توجه: قبل از گفت ن یک polyfill برای هر ویژگی، همیشه تحلیل کنید که آیا این ویژگی در نمونه اولیه شی وجود دارد یا خیر، یا ممکن است رفتار موجود را لغو کنید. مثلا:
if (!Array.prototype.map)
افکار اضافی
قبل از پایان، من می خواهم چند فکر اضافی را به اشتراک بگذارم. شکستن مصاحبه جاوا اسکریپت فقط به خاطر سپردن مفاهیم نیست. اطمینان حاصل کنید که در هر موضوع عمیقاً غوطه ور شده اید و آن را به طور کامل درک کرده اید، از جمله کاربردهای آن.
علاوه بر این، اهمیت مهارت های نرم مانند ارتباط را دست کم نگیرید. انتقال افکار خود به طور واضح به مصاحبه کننده به اندازه دانستن مطالب خود مهم است.
هنگامی که در مورد هر یک از مفاهیم بالا از شما سؤال می شود، با توضیح مختصر مفهوم شروع کنید و سپس از یک مثال برای توضیح دقیق تر استفاده کنید.
توضیح با مثال برای پاسخ به هر سوال مصاحبه بسیار مهم است. درک فرآیند فکر شما برای مصاحبه کنندگان آسان تر می شود. در این پست، من نیز در هنگام توضیح هر مفهوم، از الگوی مشابهی پیروی کردم.
در نهایت، این کتابچه راهنما را در طول آماده سازی مصاحبه خود مرور کنید. به راحتی از آن به عنوان یک برگه تقلب استفاده کنید. اگر توسعهدهندهای با تجربه هستید، این کتاب راهنما به شما کمک میکند تا این مفاهیم را مجدداً مرور کرده و تقویت کنید.
نتیجه
مصاحبه ها می توانند کاملا ترسناک و غیرقابل پیش بینی باشند. در حالی که تقاضای زیادی برای مهارت های جاوا اسکریپت وجود دارد، رقابت به همان اندازه شدید است. ایجاد یک پایه قوی برای آماده سازی موفقیت آمیز مصاحبه بسیار مهم است.
در این کتاب راهنما، چندین موضوع مهم را برای آماده شدن برای مصاحبه بعدی جاوا اسکریپت شما بیان کرده ام و توضیحات مفصلی برای هر مفهوم ارائه کرده ام. در حالی که این یک فهرست جامع نیست، چندین مفهوم مهم را پوشش می دهد. اگر فکر می کنید موضوعات مهمی را از دست داده ام، لطفاً به من اطلاع دهید.
اگر نمی توانید مطالب را درک کنید یا توضیح را رضایت بخش نمی دانید، نظرات خود را در زیر کامنت کنید. ایده های جدید همیشه قدردانی می شوند! با خیال راحت با من در توییتر ارتباط برقرار کنید.
در مصاحبه هایتان موفق باشید!!!
ارسال نظر