نکاتی بر JS که همیشه از ذهن من و اطرافیانم پاک می شوند و نیاز به یادآوری دارند. بعضی موارد در js هستند که در دیگر زبان ها وجود ندارند یا بصورت استاندارد تری وجود دارند و درک آنها در دنیای js سخت است چنین مواردی را هم اینجا اضافه میکنم.
destructuring آبجکت های تودرتو
const obj = {
title: 'foo',
child: {
title2: 'bar'
}
}
let {
title,
child,
child: { title2 }
} = obj
console.log(title) // foo
console.log(child) // [object Object]
console.log(title2) // bar
تفاوت let
و const
و var
var
اجازه می دهد تا یک متغیر با همان نام را در یک محدوده بدون ایجاد خطا مجدداً تعریف کنیم. ولی const
و let
دراین مورد خطا می دهد.
var name = 'John'
var name = 'Jim'
console.log(name) // -> 'Jim'
const name = 'John'
const name = 'Jim'
// -> ERROR
var
دارای محدوده تابع است، در حالی که const
/let
دارای محدوده بلوکی هستند. بلوک کد ساختار {...}
است که از دستورات تابع، if، for، while و غیره پیروی می کند
var
اعلان ها را به بالای دامنه منتقل می کند، که متغیرها را در کل محدوده در دسترس قرار می دهد. به این کار hoisting گفته می شود و می تواند باعث ایجاد Error شود. اگر به متغیر const
/let
قبل از اعلان ارجاع دهید، خطا می دهد.
تفاوت اساسی const
/let
این است که درواقع هر متغییر یک بچسبی است برای یک ویژگی زمانی که به یک ویژگی برچسبی را با const
تعریف کنیم دیگر این برچسب قابل تغییر نیست.
ولی این را تضمین نمیکند که ویژگی ثابت بماند فقط تضمین میکند که برچسب ثابت خواهد ماند.
function f1() {
// code block starts
var name // declaration is invisibly hoisted here when the code is processed
console.log(name) // -> undefined
var name = 'John'
} // code block ends
function f2() {
// no hoisting with let
console.log(name) // -> ERROR
const name = 'John'
}
const
و let
رفتار یکسانی دارند، با این تفاوت که پس از اینکه مقدار اولیه به const
داده شد، نمی توان آن را مجدداً تعیین کرد. پیش فرض شما باید const
باشد، از let
در مواردی که نیاز به تغییر مقدار متغیر در زمان اجرا دارید استفاده کنید. و ازvar
باید اجتناب شود.
const name = 'John'
name = 'Jim'
// -> ERROR
توابع بازگشتی (Recursion)
توابعی که خود را فراخوانی می کند. و باید داخل چنین توابعی از شرط ها استفاده کرد در غیر اینصورت تابع بازگشتی بصورت نامحدود به فراخوانی خود ادامه میدهد. قظعه کدی برای محاسبه فاکتوریل با توابع بازگشتی:
// program to find the factorial of a number
function factorial(x) {
// if number is 0
if (x === 0) {
return 1
}
// if number is positive
else {
return x * factorial(x - 1)
}
}
console.log(factorial(3)) // 6
اپراتور ==
و ===
اپراتور مساوی برابر بودن دو عملوند را چک کرده و بصورت Boolean
جوابی برمیگرداند.
- "==" فقط برابری ویژگی ها را چک می کند
- "===" علاوه بر چک ویژگی ها نوع آنها را هم چک می کند
let num1 = 1
let num1s = "1"
let res1 = num1 == num1s
let res2 = num1 === num1s
console.log(`== oprator: ${res1}, === operator: ${res2}`)
// Output: == oprator: true, === operator: false
value و reference types
ما در جاوا اسکریپت دو دسته تایپ داریم یکی value types هست و در بخش بعدی Refrence types هستند زمانی که refrence type باشد در حقیقت به رفرنس اون در مموری اشاره میکند ولی اگر value type باشد به خود آن متغیر اشاره میکند. بصورت خلاصه Primitive ها با ویژگی هایشان کپی می شوند و Object ها با ارجاع
let number = 10;
function inc(number) {
number++;
}
inc(number);
console.log(number)
درصورت اجرای این کد خروجی 10 خواهد بود چرا که در بالا اشاره شد Primitive ها با ویژگی هایشان کپی می شوند.
حال اگر از Object استفاده کنیم:
let obj = { value: 10 }
function inc(obj) {
obj.value++
}
inc(obj)
console.log(obj)
درصورت اجرا خروجی { value: 11 }
خواهد بود چرا که Object ها با ارجاع کپی می شوند.
متد reduce()
این متد چهار آرگیومنت دارد که معمولا فقط دوتای اول را مورد استفاده قرار می دهند.
accumulator
مقدار برگشتی تکرار قبلیcurrentValue
آیتم فعلی در آرایهindex
ایندکس آیتم فعلیarray
آرایه اصلی کهreduce()
بر روی آن فراخوانی شده استinitialValue
این آرگیومنت اختیاری است. در صورت ارائه، به عنوان مقدار اولیهaccumulator
درنظر گرفته می شود.
const numbers = [1, 2, 3, 4, 5, 6]
let summation = numbers.reduce(
(accumulator, currentValue) => accumulator + currentValue
)
console.log(summation) // 21
Hoisting
زمانی که از let
و const
برای تعریف متغیر استفاده میکنیم قبل تعریف آن نمیتوانیم مقداردهی کنیم و با ارور ReferenceError
مواجه می شویم.(اگر از var برای تعریف متغیر استفاده کنید با ارور undfined
روبرو می شوید)
var x = 5
var y
elem = document.getElementById("demo")
elem.innerHTML = x + " " + y
y = 7
// Output: x is 5 and y is undefined
قضیه درمورد توابع متفاوت است. درتوابع declared می شود بالاتر از تعریف تابع آنرا فراخوانی کرد.
hoisted() // Output: "This function has been hoisted."
function hoisted() {
console.log('This function has been hoisted.')
}
ولی اگر تابع بصورت expression باشد باید بعد تعریف تابع عمل فراخوانی انجام بگیرد.
expression() //Output: "TypeError: expression is not a function
var expression = function () {
console.log('Will this work?')
}
اگر هردو نوع تابع را باهم ترکیب کنیم باز هم باید بعد تعریف تابع عمل فراخوانی انجام بگیرد.
expression() // Ouput: TypeError: expression is not a function
var expression = function hoisting() {
console.log('Will this work?')
}
درمورد کلاس ها هم فرقی ندارد به چه صورتی تعریف کنید در نهایت باید از قانون hoisting پیروی کنید.
var Polygon = class Polygon {
constructor(height, width) {
this.height = height
this.width = width
}
}
var Square = new Polygon()
Square.height = 10
Square.width = 10
console.log(Square)
Property Descriptors
پراپرتی های آبجک شامل سه ویژگی خاص است.که آنها را flag می نامند.
- writable در صورتی که
true
باشد، مقدار را می توان تغییر داد. در غیر این صورت، فقط خواندنی در نظر گرفته می شود. - enumerable وقتی
true
است، در داخل حلقه قابل فهرست شدن است. در غیر این صورت قابل فهرست شدن نیست. - configurable اگر
true
باشد، ویژگی قابل حذف کردن است در غیر این صورت قابل حذف نیست.
let person = { name: 'Husen' }
Object.defineProperty(person, 'name', {
writable: false,
enumerable: false,
configurable: false
})
person.name = 'John'
console.log(person) // Object { name: "Husen" }
console.log(Object.keys(person)) // Array []
delete person.name
console.log(person) // Object { name: "Husen" }
توابع و آبجکت ها
در جاوا اسکریپت همه فانکشن ها آبجکت هستند. و برای ساخت آجکت هم میشه استفاده کرد.
function make_person(firstname, lastname, age) {
person = {}
person.firstname = firstname
person.lastname = lastname
person.age = age
return person
}
make_person('Joe', 'Smith', 23)
// {firstname: "Joe", lastname: "Smith", age: 23}
یک تابع میتواند به this
ارجاع دهد و اگر با عملگر new
فراخوانی شود، یک آبجکت با ویژگی هایی که بر روی this
تعریف شده اند را برمیگرداند.
this
در چنین مواردی به شی جدیدی که ما ایجاد می کنیم اشاره می کند.
function make_person_object(firstname, lastname, age) {
this.firstname = firstname
this.lastname = lastname
this.age = age
// Note, we did not include a return statement
}
تفاوت کلیدی بین make_person
و make_person_object
این است که اگر ما make_person
را با کلمه کلیدی this
تعریف کنیم هیچ تفاوتی ایجاد نخواهد کرد. با این حال، فراخوانی make_person_object()
بدون عملگر new
، این ویژگی های ما را در این شیء فعلی مشخص می کند (به طور کلی window
اگر در مرورگر کار بکنیم.)
var Joe = make_person_object('Joe', 'Smith', 23)
console.log(Joe) // undefined
console.log(window.firstname) // "Joe" (oops)
var John = new make_person_object('John', 'Smith', 45)
console.log(John) // {firstname: "John", lastname: "Smith", age: 45}
توابع regular و constructor
متد .bind()
توابع genrator
genrator ها نوع خاصی از تابع هستند که الگوریتم iteration را تعریف می کند و اجرای آن پیوسته نیست اما می تواند متوقف شود و بعداً از سر گرفته شود. context آن بین ورودی های متوالی به خاطر سپرده می شود.
توابع genrator با کلمه کلیدی function*
تعریف می شوند.
function* daysGenerator() {
const daysList = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
for (let i = 0; i < daysList.length; i++) {
yield daysList[i]
}
}
const daysGen = daysGenerator()
console.log(daysGen.next())
تابع genrator وقتی برای اولین بار فراخوانی می شود، بلافاصله اجرا نمی شود اما یک آبجکت genrator را برمی گرداند.
هنگامی که متد next()
روی genrator فراخوانی می شود، تابع genrator تا زمانی که با اولین کلمه کلیدی yield
مواجه شود اجرا می شود.
کلمه کلیدی yield
اجرای تابع را متوقف می کند و مقدار عبارتی را که بعد از کلمه کلیدی yield
است برمی گرداند. زمانی که متد next()
صدا زده می شود، genrator اجرا را با دستور پس از yield
از سر می گیرد. کد را تا زمانی که به yield
بعدی یا انتهای genrator برسد اجرا می شود.
function* simpleGenerator() {
yield 1
yield 3
yield 5
}
const simpleGen = simpleGenerator()
console.log(simpleGen.next()) // { value: 1, done: false }
console.log(simpleGen.next()) // { value: 3, done: false }
console.log(simpleGen.next()) // { value: 5, done: false }
console.log(simpleGen.next()) // { value: undefined, done: true }
متد .every()
و .some()
متد .every()
چک میکند که آیا همه آیتم ها شرط لازم دا دارند یا نه. و متد .some()
چک میکند که حداقل یکی از آیتم ها شرط لازم را داشته باشد.
const ages = [32, 33, 16, 40]
console.log(ages.some(checkAge)) // true
console.log(ages.every(checkAge)) // false
function checkAge(age) {
return age > 18
}