نکاتی بر 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'
// -> ERRORvar دارای محدوده تابع است، در حالی که 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: falsevalue و 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) // 21Hoisting
زمانی که از 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
}