بازگشت به دوره

حالت Strict mode

همانطور که در بخش قبل اشاره شد، اشکال‌زدایی برنامه‌ها در زبان‌های مفسری نسبت به زبان‌های کامپایلری دشوارتر است. زیرا بسیاری از روش‌هایی که در زبان‌های مفسری به کار می‌روند و مستعد بروز خطا هستند، در زبان‌های کامپایلری قابل استفاده نیستند. به همین دلیل زبان‌های کامپایلری از بروز بسیاری از خطاها جلوگیری می‌کنند. زیرا از نوشتن برنامه‌های دارای اشکال (Bug) جلوگیری می‌کنند.

به عنوان مثال در زبان‌های کامپایلری، متغیرها قبل از مقداردهی شدن حتماً باید تعریف شده باشند. همچنین لازم است تا نوع متغیرها در زمان تعریف مشخص باشد و تا پایان اجرای برنامه نیز نباید نوع آنها تغییر کند. اما این محدودیت‌ها در اکثر زبان‌های مفسری وجود ندارد. مثلاً در جاوا اسکریپت دستورات زیر کاملاً معتبر هستند.


x = 10;
console.log(x);
← 10

در این دستورات ابتدا یک متغیر سراسری (خاصیتی از شئ window) به نام x و با مقدار اولیه‌ی ۱۰ تعریف می‌شود. سپس مقدار آن در کنسول نمایش داده می‌شود. توجه کنید که متغیر x بدون استفاده از let یا const یا var به کار رفته است (یعنی تعریف نشده است). و می‌دانیم که در جاوا اسکریپت این دستور معتبر است و x را به عنوان یک خاصیت از شئ window در نظر می‌گیرد. پس می‌بینید که در جاوا اسکریپت می‌توان متغیرها را بدون تعریف قبلی، مقداردهی کرد. ضمناً در این مثال متغیر x در ابتدا از نوع عددی است. اما در هر بخشی از برنامه می‌توان نوع داده‌ی جدیدی را در این متغیر ذخیره کرد.

مقداردهی متغیرها بدون تعریف آنها، و همچنین تغییر نوع متغیرها در طول اجرای برنامه، ممکن است منجر به بروز خطا در زمان اجرای برنامه شود. ضمناً این نوع خطاها در برخی موارد به سادگی قابل کشف نیستند و ممکن است پس از بارها اجرا شدن برنامه کشف شوند.

با توجه به اینکه زبان‌های کامپایلری اجازه‌ی مقداردهی به یک متغیر تعریف نشده را نمی‌دهند. در نتیجه نمی‌توان برنامه‌ای نوشت که دارای چنین اشکالی باشد. یعنی برنامه‌ای که دارای چنین اشکالی باشد در زبان‌های کامپایلری قابل اجرا نیست. پس قبل از اجرای برنامه حتماً باید این اشکالات برطرف شوند. این ویژگی از وقوع خطاهای احتمالی بعدی جلوگیری می‌کند.

اما از آنجایی که نوشتن چنین برنامه‌هایی در جاوا اسکریپت مجاز است. ممکن است برنامه‌نویس متوجه وجود اشکال در برنامه نشده و برنامه‌هایی بنویسد که به ظاهر کاملاً صحیح اجرا می‌شوند، اما در عمل حاوی اشکالاتی هستند که ممکن است در آینده منجر به بروز خطا شوند. اگر برنامه‌نویس قبل از انتشار برنامه با هیچ خطایی مواجه نشود و برنامه را به دست کاربر نهایی برساند. در این صورت احتمالاً کاربر نهایی با خطاهای حاصل از این اشکالات مواجه خواهد شد که این بدترین حالت ممکن است. پس هرچه زبان برنامه‌نویسی سختگیرانه‌تر با چنین اشکالاتی برخورد کند، احتمال وقوع خطا در آینده کمتر خواهد شد.

البته این تنها یکی از ویژگی‌های زبان‌های کامپایلری است که موجب ساده‌تر شدن اشکال‌زدایی در آنها می‌شود. ویژگی‌های دیگری نیز در زبان‌های کامپایلری وجود دارد که اشکال‌زدایی آنها را ساده‌تر می‌کند، اما معرفی تمام این ویژگی‌ها خارج از محدوده‌ی این کتاب است.

در ECMAScript 5 ویژگی جدیدی به نام Strict mode به جاوا اسکریپت اضافه شده است که امکان استفاده از برخی ویژگی‌های زبان‌های کامپایلری را در جاوا اسکریپت فراهم می‌کند. در صورتی که برنامه‌ها در حالت Strict mode نوشته شوند، مفسر جاوا اسکریپت سختگیرانه‌تر با کدها رفتار می‌کند و خطاهای بیشتری در زمان اجرا تولید می‌کند. یعنی استفاده از برخی روش‌ها که در حالت عادی در جاوا اسکریپت معتبر هستند، در حالت Strict mode نامعتبر بوده و موجب بروز خطا می‌شوند. این ویژگی کمک زیادی به اشکال‌زدایی برنامه‌ها می‌کند. زیرا بسیاری از اشکالات را می‌توان در همان مراحل اولیه کشف و رفع کرد. از جمله مواردی که در حالت Strict mode با آن مقابله می‌شود، مقدار دهی متغیرها قبل از تعریف آنها است.

برای استفاده از حالت Strict mode کافی است در ابتدای فایل جاوا اسکریپت از رشته‌ی "use strict" به عنوان اولین دستور استفاده شود. در این صورت تمام دستورات موجود در آن فایل در حالت Strict mode تفسیر و اجرا می‌شوند. به عنوان مثال اگر دستورات قبلی را در حالت Strict mode و به صورت زیر بنویسیم. برنامه اجرا نخواهد شد و با خطای زیر مواجه می‌شویم.


"use strict";
x = 10;
← Uncaught ReferenceError: x is not defined
console.log(x);

مشاهده می‌کنید که در این حالت امکان مقداردهی به متغیری که تعریف نشده است وجود ندارد. همچنین می‌توان دستور "use strict" را در ابتدای بدنه‌ی یک تابع قرار داد. در این صورت فقط دستورات همان تابع در حالت Strict mode اجرا می‌شوند. مثال زیر نحوه‌ی استفاده از این روش را نشان می‌دهد.


function myFunc(){
	"use strict";
	x = 10;
}
myFunc();
← Uncaught ReferenceError: x is not defined
console.log(x);
همانطور که مشاهده می‌کنید این برنامه نیز با خطا مواجه شده و اجرا نمی‌شود.

نکته : در صورت وقوع خطا در یک برنامه‌ی جاوا اسکریپت، اجرای برنامه متوقف می‌شود. به همین دلیل در مثال فوق دستور "console.log(x)" به هیج وجه اجرا نمی‌شود. زیرا قبل از رسیدن به این دستور، به دلیل وقوع خطا در تابع myFunc، اجرای برنامه متوقف شده است. لازم به ذکر است که مرورگرها در زمان وقوع خطاها، هیچ پیامی به کاربر نمایش نمی‌دهند. بنابراین کاربران عادی معمولاً متوجه وقوع خطاها و توقف برنامه نمی‌شوند. البته برنامه‌نویسان می‌توانند با مراجعه به بخش Console از ابزار Developer Tools پیام‌های صادر شده از طرف مفسر جاوا اسکریپت را مشاهده کنند.

اما ممکن است در مثال فوق، برنامه‌نویس واقعاً قصد تعریف یک متغیر سراسری به نام x را داشته باشد. می‌دانیم که اگر داخل تابع myFunc از یکی از کلمات کلیدی let یا const یا var برای تعریف متغیر x استفاده شود، حوزه‌ی این متغیر سراسری نخواهد بود. در چنین شرایطی باید این متغیر صراحتاً به صورت یک خاصیت از شئ window تعریف شود. یعنی باید برنامه را به شکل زیر اصلاح کرد.


function myFunc(){
	"use strict";
	window.x = 10;
}
myFunc();
console.log(x);
← 10

البته این فقط یکی از ویژگی‌های حالت Strict mode است. در حالت Strict mode تغییرات دیگری نیز در رفتار مفسر به وجود می‌آید.

تغییر دیگری که در حالت Strict mode در رفتار مفسر به وجود می‌آید، تغییر در رفتار کلمه‌ی کلیدی this است. در این حالت کلمه‌ی کلیدی this در توابعی که به صورت متدی از یک شئ فراخوانی نشده‌اند برابر با undefined است. در صورتی که در حالت عادی کلمه‌ی کلیدی this در چنین توابعی به شئ window اشاره می‌کند. به عنوان مثال دستورات زیر را در نظر بگیرید.


const person = {
	myName: 'Abbas',
	showName: myFunc
}

function myFunc(){
	console.log(this.myName);
}
person.showName();
← "Abbas"
myFunc();
← undefined

در برنامه‌ی فوق فراخوانی متد showName، منجر به اجرای تابع myFunc می‌شود. به عبارت دیگر فراخوانی متد showName و فراخوانی تابع myFunc، هر دو منجر به اجرای دستورات یکسانی می‌شوند. اما با توجه به استفاده از کلمه‌ی کلیدی this در تابع myFunc، نحوه‌ی فراخوانی این تابع مهم است. مشاهده می‌کنید که در فراخوانی تابع myFunc به عنوان متدی از شئ person، کلمه‌ی کلیدی this به شئ person اشاره می‌کند. بنابراین مقدار this.myName برابر با "Abbas" است. اما در فراخوانی همین تابع به صورت مستقل، کلمه‌ی کلیدی this به شئ window اشاره می‌کند و با توجه به اینکه شئ window خاصیتی به نام myName ندارد، مقدار this.myName برابر با undefined است.

اما اگر همین برنامه در حالت Strict mode اجرا شود، نتیجه متفاوت خواهد بود. نتیجه‌ی اجرای این برنامه در حالت Strict mode به صورت زیر می‌باشد.


"use strict";
const person = {
	myName: 'Abbas',
	showName: myFunc
}

function myFunc(){
	console.log(this.myName);
}
person.showName();
← "Abbas"
myFunc();
← Uncaught TypeError: Cannot read property 'myName' of undefined

در این حالت برنامه با خطا مواجه می‌شود. زیرا در فراخوانی تابع myFunc به صورت مستقل، کلمه‌ی کلیدی this برابر با undefined است و این مقدار نمی‌تواند خاصیتی به نام myName داشته باشد. در واقع مقدار undefined از انواع داده‌ی اولیه یا primitve است و نمی‌تواند یک خاصیت داشته باشد. به همین دلیل دستور آخر با خطا مواجه می‌شود. در فصل ۱۳ در رابطه با کلمه‌ی کلیدی this و اهمیت آن بیشتر صحبت خواهیم کرد.

به عنوان نمونه‌ای دیگر از تغییر رفتار مفسر در حالت Strict mode به مثال زیر توجه کنید. در این مثال تابعی به نام sum برای جمع کردن دو عدد وجود دارد. توجه کنید که این تابع دارای دو پارامتر ورودی با نام یکسان است.


function sum(num , num){
	return num + num;
}
sum(10 , 20);
← 40

مشخص است که چنین تابعی نمی‌تواند خروجی صحیحی تولید کند. در واقع در چنین تابعی فقط مقدار آرگومان دوم مهم است و این تابع مقدار آرگومان دوم را دو برابر کرده و بازمی‌گرداند. اگر برنامه‌نویس سهواً چنین اشتباهی را در برنامه مرتکب شود، در حالت عادی هیچ خطایی تولید نخواهد شد. در نتیجه برنامه‌نویس متوجه اشتباه خود نخواهد شد. اما در حالت Strict mode چنین برنامه‌ای قابل اجرا نیست و با خطا مواجه شده و متوقف خواهد شد. لذا برنامه‌نویس در همان مراحل اولیه متوجه اشتباه خود خواهد شد. نحوه‌ی اجرای این مثال در حالت Strict mode به شکل زیر است.


"use strict";
function sum(num , num){
	return num + num;
}
sum(10 , 20);
← Uncaught SyntaxError: Duplicate parameter name not allowed in this context

مشاهده می‌کنید که در این حالت امکان استفاده از پارامترهای همنام در یک تابع وجود ندارد و چنین کاری موجب تولید خطا و توقف برنامه می‌شود.

به طور کلی استفاده از حالت Strict mode باعث تسریع فرآیند اشکال‌زدایی می‌شود و استفاده از این حالت در برنامه‌نویسی می‌تواند بسیار مفید باشد. با این حال در ادامه‌ی این فصل از این حالت استفاده نخواهیم کرد. همچنین نکات دیگری در رابطه با این حالت وجود دارد که به مرور با این نکات آشنا خواهید شد.