تمرکز فصل دوم کتاب بر چگونه کار کردن زبان js هستش و برخی از زمینه های اصلی زبان رو مورد بررسی قرار میده تا زبان رو بهتر بشناسیم و بتونیم با اعتماد به نفس بیشتری برنامه هامون رو بنویسیم.
این فصل جزیئات زیادی داره و برای هر بخش نیازه که وقت کافی در نظر گرفته شه و با دقت تمرین شه. همونطوری که خود کتاب هم میگه :
The best way to learn JS is to start writing JS.
معمولا هر website یا web application شامل چند فایل js مختلف هستش (معمولا با پسوند .js ). به طور معمول کل برنامه رو به عنوان یک برنامه در نظر میگیرن که این در js درست نیست. در js هر فایل برنامه مستقل خودش رو داره . دلیل این امر در درجه اول مربوط به error handling هستش این ویژگی باعث میشه اگه یک فایل در زمان تجزیه یا اجرا به مشکلی بر خورد و fail شد کل برنامه دچار مشکل نشه و بقیه ی فایل ها اگه مشکلی نداشته باشن اجرا بشن . بنابراین خیلی مهم هستش که مطمئن بشیم همه فایل ها دارن به درستی اجرا میشن.
شاید در نظر گرفتن هر فایل js به عنوان برنامه ای جدا عجیب باشه در واقع فایل های js به عنوان فایل های همکار در کنار هم عمل میکنن و یه app بزرگ رو میسازن.
در بسیاری از پروژه ها هم از ابزار build process استفاده میکنن که باعث میشه همه ی فایل های js موجود با هم ترکیب بشن و نهایتا یه فایل js برای ارسال به صفحه وب استفاده بشه که با این فایل ترکیبی منفرد به عنوان کل برنامه برخورد میشه.
تنها راهی که چندین فایل .js به عنوان یک برنامه واحد عمل کنن اشتراک گذاری وضعیت آنها از طریق "global scope" هستش.
از زمان ES6 علاوه بر قالب برنامه استاندارد js از یک قالب module هم پشتیبانی شده که اگه یک فایل از مکانیسم module-loading مانند import یا استفاده از <script type=module> استفاده کرد تمام کد به عنوان یک module واحد در نظر گرفته بشه.
اساسی ترین واحد اطلاعات در یک برنامه value ها هستند. value ها داده ها هستند یا برای حفظ حالت ها اسفاده میشن . value ها به دو دسته primitive و object تقسیم میشن.
در برنامه ها value ها با literal خاصی تعبیه شدن.
greeting("My name is Kyle.");
در برنامه بالا "My name is Kyle." یک primitive string literal هستش. string ها مجموعه ای از characters هستند که برای نمایش کلمات و جملات استفاده میشن. برای محاصره کردن، جدا کردن یا تعریف کردن یک str از double-quote " یا single-quote ' استفاده میشه. همچنین میشه از back-tick ` هم استفاده کرد که در طول برنامه برای حفظ خوانایی کد و بر اساس قابلیت استفاده میتونیم انتخاب و استفاده کنیم. به کد زیر و نتیجه هر سه مورد توجه کنید :
console.log("My name is ${ firstName }."); // My name is ${ firstName }. console.log('My name is ${ firstName }.'); // My name is ${ firstName }. console.log(`My name is ${ firstName }.`); // My name is Kyle.
فرض کنید برای firstName مقدار 'Kyle' تعریف شده در حالت سوم که از ` استفاده شده با گذاشتن value داخل {...}$ مقدار فعلی firstName درنتیجه دیده میشه. که به این interpolation میگن.
به جز String ها برنامه ها js حاوی primitive literal values های دیگه ای از جمله Boolean و number ها هستن.
while (false) { console.log(3.141592); }
با استفاده از while می تونیم یه حلقه (loop) رو نمایش بدیم. در قطعه کد بالا هرگز حلقه اجرا نخواهد شد چون از مقدار Boolean false برای شرط حلقه استفاده کردیم. دقت کنید که مقدار Boolean true هستش که باعث اجرای حلقه میشه.
مقادیر number هم در برنامه ها برای حفظ مقادیر عددی و همچنین برای شمارش (مثلا تعداد تکرار حلقه ها) به کار میرن.
علاوه بر String , Boolean , Number دو primitive values دیگر هم در js داریم که null و undefined هستند. در حالی که بین این دو تفاوت هایی وجود داره ولی در بیشتر قسمت ها هر دو به معنی تهی بودن یا عدم وجود ارزش هستند. با این حال بهتر این هستش که از undefined برای single empty value ها استفاده بشه.
اخرین primitive value که باید بهش توجه کرد symbol ها هستند که منحصرا به عنوان کلیدهای شاخص در object استفاده میشن.
hitchhikersGuide[ Symbol("meaning of life") ]; // 42
در برنامه های معمولی js معمولا از symbol ها استفاده مستقیم نمیکنیم و ازشون تو کدهای سطح پایین مثل libraries و frameworks استفاده میشه.
علاوه بر primitive ها نوع دیگه ای از value ها object ها هستند. Array ها هم یک نوع خاصی از object ها هستند که از یک لیست داده مرتب شده و فهرست بندی شده عددی تشکیل شدن.
var names = [ "Frank", "Kyle", "Peter", "Susan" ]; names.length; // 4 names[0]; // Frank names[1]; // Kyle
توجه کنید که از 1 برای element که در position دوم هستش استفاده میکنیم در واقع index های آرایه ها 0-based هستند یعنی 0 موقعیت اول را نشان می دهد.
آرایه های JS می توانند هر نوع value یعنی primitive یا object رو نگه دارن. Objects ها به نسبت Array عمومی تر هستند و به جای موقعیت عددی با استفاده از str هایی که نام اون موقعیت ها هستند که بهشون key یا property گفته میشه به element ها دسترسی دارین.
var me = { first: "Kyle", last: "Simpson", age: 39, specialties: [ "JS", "Table Tennis" ] }; console.log(`My name is ${ me.first }.`);
در اینجا me یک object رو نمایش میده و first نام مکانی اطلاعاتی در این شی رو نشون میده (value collection). روش دیگر برای دسترسی به اطلاعات استفاده از property/key هستش که داخل square-brackets [] استفاده میشه به عنوان مثال:
me["first"]
برای مشخص کردن نوع value می تونیم از اپراتور typeof کمک بگیریم . باید توجه کنیم که برای value هایی که primitive هستن نوع داخلیش رو مشخص میکنه و در غیر اینصورت میگه که object هستش. به مثال زیر دقت کنین:
typeof 42; // "number" typeof "abc" // "string" typeof true; // "boolean" typeof undefined; // "undefined" typeof null; // "object" -- oops, bug! typeof { "a": 1 }; // "object" typeof [1,2,3]; // "object" typeof function hello(){}; // "function"
همونطور که میبینین برای حالت null انتظار میره که نوعش رو null بگه در حالی که object تعریف کرده. همچنین این حالت برای array هم هستش و اون هم object گفته.
به Variables (متغییر) مانند ظرفی برای نگه داشتن value ها فکر کنید. برای استفاده باید variable ها اعلام بشن (declare , create) . برای این کار identifiers (شناسه) های مختلف وجود داره که هر کدوم رفتارهای متفاوت خودشون رو دارن.
مثلا var رو در نظر بگیرین :
var myName = "Kyle" var age;
کلمه کلیدی var بهمون این امکان رو میده که از یک متغییر تو یه قسمت از کد بتونیم استفاده کنیم. همچنین زمان تعریف متغیر میتونیم بهش مقدار اولیه هم بدیم.
کلمه کلیدی بعدی که مشابه var هستش let :
let myName = "Kyle" let age;
کلمه کلیدی let به نسبت var دسترسی محدود تری رو میده در واقع دسترسیش از نوع "block scoping" هستش. به کد زیر توجه کنین تا تفاوتش واضح تر بشه:
var adult = true; if (adult) { var myName = "Kyle" let age = 39; console.log("Shhh, this is a secret!"); } console.log(myName); // Kyle console.log(age); // Error!
همونطور که میبینین متغییر myName خارج از block هم قابلیت دسترسی داره ولی برای age این امکان وجود نداره. میتونیم اینطور نتیجه بگیرم که برای جلوگیری از همپوشانی مقادیر اسم متغییرهای مشابه میتونیم از let استفاده کنیم و برای دسترسی وسیع تر به یه متغییر هم میتونیم از var کمک بگیریم .
سومین فرم declaration هم const هستش . این شناسه مثل let هستش با این تفاوت که یه محدودیت دیگه هم داره که حتما باید یه مقدار اولیه براش تعریف کرد و بعدا هم در طول برنامه نمیشه مقدارش رو تغییر داد. به کد زیر توجه کنید:
const myBirthday = true; let age = 39; if (myBirthday) { age = age + 1; // OK! myBirthday = false; // Error! }
متغییر myBirthday چون const هستش نمیشه مقدار جدید بهش re-assigned کرد.
بهترین زمان استفاده از const برای حالتی هستش که شما یه مقدار ساده primitive دارین و می خوایین یه اسم مفید هم بهش بدین.const باعث میشه برنامه تون راحت تر خونده بشه و خوانایی کد افزایش پیدا کنه.
علاوه بر var و let و const ، شکل های نحوی دیگری نیز وجود داره که شناسه ها (متغیرها) را در حوزه های مختلف declare می کنن . مثلا:
function hello(myName) { console.log(`Hello, ${ myName }.`); } hello("Kyle"); // Hello, Kyle.
شناسه hello یه scope خارجی هستش که با function هم همراه هستش و میشه فراخوانی کرد و ازش استفاده کرد اما پارامتر myName داخل خود function فقط قابل استفاده هستش.
نحو دیگری که متغییر رو declare میکنه catch کردن هستش:
try { someError(); } catch (err) { console.log(err); }
مقدار err یک متغییر block-scoped هستش که فقط داخل catch قابل استفاده ست و مثل این میمونه که با let تعریفش کنیم .
برای اینکه فصل خیلی طولانی هستش ترجیح میدم ادامه فصل رو تو پست بعد بگم.
از مباحث اصلی ادامه فصل مبحث function ها و class ها هستش.