درود دوستان! یکی از هدف‌های جاوااسکریپت در زمان‌های قدیم و قبل از اینکه این زبان اینقدر پیشرفت کنه و محبوب بشه، راحتی کار بود. راحتی‌ای که به برنامه‌نویس اجازه می‌داد با سخت‌گیری کمتری نسبت به زبان‌های دیگه فعالیت کنه. اما این راحت‌گیری باعث بروز خطاهایی می‌شد. کدهایی که به ظاهر کاملاً منطقی بودن و جاوااسکریپت اونها رو معتبر می‌دونست، بعداً یقهٔ برنامه‌نویس رو می‌گرفت. مثلاً اجازه‌ی مقدار دادن به یک متغیر تعریف نشده که توی اکثر زبان‌ها یک خطای منطقی به حساب میاد:

x = 5;

alert(x);

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

 

Strict Mode چیه؟ 🤔

Strict Mode یا حالت سخت‌گیرانه حالتی هست که با تعریف کردن دستور "use strict" فعال میشه و جاوااسکریپت رو موظف می‌کنه تا برنامه‌ی ما رو با سخت‌گیری بیشتری پردازش کنه. اگه با حالت سخت‌گیرانه کدنویسی کنیم، کدهای بهینه‌تر و سریع‌تری نسبت به حالت غیر سخت‌گیرانه خواهیم داشت. وظیفه‌ی حالت سخت‌گیرانه نمایش ارورهای خاموش هست.

 

ارور خاموش چیه؟

ارور خاموش به اروری گفته میشه که جاوااسکریپت از اونها گذشت می‌کنه تا باعث توقف برنامه نشن.

 

فعال کردن حالت سخت‌گیرانه

توی یک برنامه‌ی جاوااسکریپتی، میشه همزمان هم کدهای سخت‌گیرانه داشت و هم غیر سخت‌گیرانه. کافیه از رشته‌ی "use strict" استفاده کنیم. دستور "use strict" رو می‌تونیم توی دو اسکوپ تعریف کنیم‌:

 

۱. گلوبال اسکوپ (حوزه سراسری)

اگه بالاترین قسمت فایل از "use strict" استفاده کنیم، حالت سخت‌گیرانه برای همه‌ی قسمت‌های فایل فعال میشه. حتی داخل توابع.

از این دستور بصورت زیر استفاده می‌کنیم:

"use strict";

const x = 4;
const y = 29;

function welcome() {

}

دقت کنین که برای فعال کردن حالت سخت‌گیرانه بصورت سراسری، دستور "use strict" باید اول فایل تعریف شده باشه. در غیر این صورت این حالت فعال نمیشه:

x = 4;
"use strict"; // Doesn't work
const y = 29;

 

۲. لوکال اسکوپ (حوزه محلی، تابعی)

اگه می‌خوایم حالت سخت‌گیرانه فقط توی یک تابع فعال باشه، کافیه این دستور رو اول تابع تعریف کنیم:

function welcome() {
  "use strict";
  const x = 429;
}

اینطوری حوزه سراسری تحت تاثیر قرار نمی‌گیره.

نکته: حالت سخت‌گیرانه برای ماژول‌های جاوااسکریپت (Import/Export) بصورت خودکار فعال هست.

خب حالا وقت اینه که بررسی کنیم‌ "use strict" روی چه چیزایی حساس هست.

 

۱. مقدار دادن به متغیر تعریف‌نشده

اگه تلاش کنیم به متغیری که از قبل با var یا let و const تعریف نشده مقدار بدیم، خطا می‌گیریم:

function test() {
  "use strict";
  x = 4; // ReferenceError: assignment to undeclared variable x
}

test();

این کد در حالت عادی و غیر سخت‌گیرانه با اضافه کردن متغیر x به آبجکت سراسری، یک متغیر سراسری درست می‌کرد:

function test() {
  x = 4;
}
	 
test();
alert(x); // 4

 

۲. تغییر دادن مقدارهای readonly

با حالت سخت‌گیرانه، نمیشه به مقدارهای readonly یا فقط‌خواندنی مثل NaN و undefined مقدار داد:

function f() {
  "use strict";
  NaN = 8; // TypeError: "NaN" is read-only
}

f();

جالبه که در حالت عادی اروری نمی‌گیریم.

همچنین برای تغییر دادن پراپرتی‌های readonly هم خطا می‌گیریم:

function f() {
  "use strict";
  const person = {};

  Object.defineProperty(person, "name", {
    value: "Jack",
    writable: false,
  });

  person.name = "John"; // TypeError: "name" is read-only
}

f();

 

توی کد بالا با استفاده از defineProperty از آبجکت سراسری Object با امکانات بیشتری تونستیم به آبجکت‌هامون پراپرتی اضافه کنیم. مثلاً پراپرتی‌هایی که غیر قابل تغییر هستن.

برای تغییر دادن پراپرتی‌های getter-only هم خطا می‌گیریم:

function f() {
  "use strict";

  const person = {
    get name() {
      return "Jack";
    }
  }

  person.name = "John"; // TypeError: setting getter-only property "name"
}

f();

 

۳. حذف متغیر و توابع با delete

حذف یک متغیر با کلمه کلیدی delete به ما خطا میده و میگه این کار منسوخ شده:

function f() {
  "use strict";
  const x = 429;
  delete x; // SyntaxError: applying the 'delete'
            // operator to an unqualified name is deprecated;
}

f();

برای حذف توابع با delete هم این ارور وجود داره.

برای حذف پراپرتی‌های غیرقابل حذف هم خطا می‌گیریم:

function f() {
  "use strict";
  delete Object.prototype;  // TypeError: property "prototype" is
                            // ‍non-configurable and can't be deleted
}

f();

 

۴. پارامترهای تکراری یک تابع

یک تابع نمی‌تونه پارامترهایی با اسم تکراری داشته باشه:

"use strict";

function foo(<<x>>, <<x>>) {
  
}

// SyntaxError: duplicate formal argument x

 

۵. اعداد Octal

توی جاوااسکریپت اگر عدد صفر قبل از یک عدد دیگه قرار بگیره، جاوااسکریپت اون رو به عنوان عدد اوکتال در نظر می‌گیره:

alert(010);       // 8
alert(019 > 021); // true!

با حالت سخت‌گیرانه اجازه‌ی نوشتن صفر قبل از یک عدد (پایه ۱۰) رو نداریم:

"use strict";

const x = 010; // SyntaxError: "0"-prefixed octal literals and
               // octal escape sequences are deprecated;
               // for octal literals use the "0o" prefix instead

 

۶. تعریف متغیر در تابع eval

در حالت عادی اگه توی تابع eval یک متغیر تعریف کنیم، این متغیر می‌تونه جایگزین متغیری بشه که از قبل تعریف شده:

var x = 10;
eval('var x = 50');

alert(x); // 50

که این می‌تونه خطرناک باشه و باعث ایجاد باگ بشه. برای جلوگیری از این کار، داخل eval ابتدا یک use strict می‌نویسیم:

var x = 10;

const evaluatedX = eval("'use strict'; var x = 50; x");

alert(x); // 10
alert(evaluatedX); // 50

 

تابع eval چیه؟ 🤔

تابع eval یک رشته رو به عنوان ورودی می‌گیره و مثل کدهای جاوااسکریپت اون رو اجرا می‌کنه. رشته‌ای که پاس داده شده باید شامل کدهای معتبر جاوااسکریپت باشه:

eval("let x = 10; alert(x)");

استفاده از eval یک کار ریسکی و خطرناک هست. چون این تابع هر چیزی که توی ورودی باشه اجرا می‌کنه و زمانی خطرناک میشه که این ورودی وابسته به کاربر باشه. کاربر خیلی راحت می‌تونه کدهای خودش رو وارد کنه و اگه این ورودی شامل کدهای مخرب باشه، می‌تونه باعث از کار افتادن برنامه بشه. توی مثال ابتدایی دیدیم که چطوری متغیری که تعریف کرده بودیم، توسط کدهای eval تحت تأثیر قرار گرفت.

 

۷. مقدار دادن به arguments و eval

به arguments و eval نمیشه مقدار نسبت داد:

"use strict";

eval = 17;
arguments++;
++eval;
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };

توی همه‌ی موارد بالا خطا می‌گیریم.

 

۸. در رفتار this توابع

توی یک تابع معمولی، در حالت عادی this به آبجکت سراسری (توی مرورگرها window) اشاره می‌کنه:

function liskov() {
    return this;
}

alert(liskov()); // window

اما با فعال کردن حالت سخت‌گیرانه، مقدار this توی توابع undefined خواهد بود:

function f() {
  "use strict";

  function liskov() {
    return this;
  }

  alert(liskov()); // undefined
}

f();

البته this توی توابع همیشه این مقدارها رو نداره و بستگی به شرایط مقدارش عوض میشه. برای اطلاع بیشتر درباره this این پست رو بخونید.

 

چرا strict mode با نوشتن یک رشته فعال میشه؟

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

 

چه زمانی از حالت سخت‌گیرانه استفاده کنیم؟

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

امیدوارم از این قسمت هم استفاده کرده باشین. روزتون خوش! ✌️😉

 

https://stackoverflow.com/questions/8651415/what-is-strict-mode-and-how-is-it-used

https://www.w3schools.com/js/js_strict.asp

https://javascript.info/strict-mode

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval