در ابتدا، جاوا اسکریپت ممکن است بسیار ساده به نظر برسد. با این حال، این زبان بسیار ظریفتر، قدرتمندتر و پیچیدهتر از آن چیزی است که در ابتدا تصور میشد. بسیاری از ظرافتهای جاوا اسکریپت منجر به تعدادی از مشکلات رایج، که 10 مورد از آنها در اینجا، میشود که باعث میشود کد آنطور که در نظر گرفته شده عمل نکند. مهم است که در تلاش برای تبدیل شدن به یک توسعهدهنده جاوا اسکریپت از این مشکلات آگاه باشید و از آن اجتناب کنید.
امروزه، جاوا اسکریپت هسته اصلی تمامی برنامه های کاربردی وب مدرن است. به همین دلیل است که مشکلات جاوا اسکریپت و یافتن اشتباهاتی که باعث آنها می شود، در خط مقدم توسعه دهندگان وب قرار دارد.
کتابخانه ها و فریمورک های قدرتمند مبتنی بر جاوا اسکریپت برای توسعه برنامه های کاربردی تک صفحه ای (SPA)، گرافیک و انیمیشن، و پلتفرم های جاوا اسکریپت سمت سرور چیز جدیدی نیستند. جاوا اسکریپت در دنیای توسعه برنامه های وب در همه جا حاضر شده است و بنابراین مهارت مهمی برای تسلط بر آن است.
در ابتدا، جاوا اسکریپت ممکن است بسیار ساده به نظر برسد. در واقع، ساختن قابلیتهای اساسی جاوا اسکریپت در یک صفحه وب برای هر توسعهدهنده نرمافزار باتجربه، حتی اگر تازه کار با جاوا اسکریپت باشد، کار نسبتاً سادهای است. با این حال، این زبان به طور قابل توجهی ظریفتر، قدرتمندتر و پیچیدهتر از آن چیزی است که در ابتدا تصور میشود. در واقع، بسیاری از ظرافتهای جاوا اسکریپت میتواند به تعدادی از مشکلات رایج منجر شود که آن را از کار کردن باز میدارد، 10 مورد از آنها در اینجا بحث میکنیم. مهم است که از این مسائل در سفر خود برای تبدیل شدن به یک توسعهدهنده جاوا اسکریپت آگاه باشید و از آن اجتناب کنید.
شماره 1 جاوا اسکریپت: ارجاعات نادرست به this
هیچ کمبودی در میان توسعه دهندگان جاوا اسکریپت در مورد کلمه کلیدی "this
" جاوا اسکریپت وجود ندارد.
از آنجایی که تکنیکهای کدنویسی جاوا اسکریپت و الگوهای طراحی در طول سالها به طور فزایندهای پیچیده شدهاند، افزایش متناظر در گسترش دامنههای خودارجاعی در بازخوانیها و بستهها وجود دارد، که منبع نسبتاً رایج سردرگمی «this
confusion» هستند که باعث مشکلات جاوا اسکریپت میشوند.
این نمونه کد را در نظر بگیرید:
const Game = function() {
this.clearLocalStorage = function() {
console.log("Clearing local storage...");
};
this.clearBoard = function() {
console.log("Clearing board...");
};
};
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(function() {
this.clearBoard(); // What is "this"?
}, 0);
};
const myGame = new Game();
myGame.restart();
اجرای کد بالا با خطای زیر مواجه می شود:
Uncaught TypeError: this.clearBoard is not a function
چرا؟ همه چیز در مورد Context است. دلیل دریافت این خطا این است که وقتی ()setTimeout
را فراخوانی می کنید، در واقع ()window.setTimeout
را فراخوانی می کنید. در نتیجه، تابع ناشناس که به ()setTimeout
ارسال میشود، در context شی window
تعریف میشود که متد ()clearBoard
ندارد.
یک راه حل سنتی و سازگار با مرورگر قدیمی این است که به سادگی مرجع (reference) خود را به آن در متغیری ذخیره کنید که می تواند با بسته شدن به ارث برسد، به عنوان مثال:
Game.prototype.restart = function () {
this.clearLocalStorage();
const self = this; // Save reference to 'this', while it’s still this!
this.timer = setTimeout(function(){
self.clearBoard(); // Oh, OK, I do know who 'self' is!
}, 0);
};
همچنین، در مرورگرهای جدیدتر، میتوانید از متد ()bind
برای ارسال در مرجع مناسب استفاده کنید:
Game.prototype.restart = function () {
this.clearLocalStorage();
this.timer = setTimeout(this.reset.bind(this), 0); // Bind to 'this'
};
Game.prototype.reset = function(){
this.clearBoard(); // OK, back in the context of the right 'this'!
};
شماره 2 فکر اینکه scope Block-level وجود دارد
یک منبع سردرگمی رایج در میان توسعه دهندگان جاوا اسکریپت (و بنابراین منبع رایج اشکالات) این است که جاوا اسکریپت یک scope جدید برای هر بلوک کد ایجاد کند. اگرچه این در بسیاری از زبان های دیگر صادق است، اما در جاوا اسکریپت درست نیست.
برای مثال کد زیر را در نظر بگیرید:
for (var i = 0; i < 10; i++) {
/* ... */
}
console.log(i); // What will this output?
اگر حدس میزنید که فراخوانی ()console.log
یا undefined خروجی میدهد یا خطایی ایجاد میکند، اشتباه حدس زدهاید. باور کنید یا نه، خروجی 10 می دهد. چرا؟
در بیشتر زبانهای دیگر، کد بالا منجر به خطا میشود، زیرا «زندگی» (یعنی دامنه) متغیر i
به بلوک for
محدود میشود. اما در جاوا اسکریپت اینطور نیست و متغیر i
حتی پس از تکمیل حلقه for
در محدوده باقی می ماند و آخرین مقدار خود را پس از خروج از حلقه حفظ می کند. (این رفتار به عنوان variable hoisting شناخته می شود.)
پشتیبانی از محدوده های سطح بلوک در جاوا اسکریپت از طریق کلمه کلیدی let
در دسترس است. کلمه کلیدی let
سالهاست که به طور گسترده توسط مرورگرها و موتورهای جاوا اسکریپت پشتیبان مانند Node.js پشتیبانی میشود.
شماره 3 جاوا اسکریپت: ایجاد نشت حافظه
نشت حافظه در جاوا اسکریپت تقریباً اجتناب ناپذیر است، اگر آگاهانه برای جلوگیری از آنها کدنویسی نکنید. راه های زیادی برای رخ دادن آنها وجود دارد، بنابراین ما فقط دو مورد از رخدادهای رایج آنها را برجسته می کنیم.
نشت حافظه مثال 1: Dangling References به آبجکت های از بین رفته
توجه داشته باشید که این مثال فقط برای موتورهای جاوا اسکریپت قدیمی کاربرد دارد، موتورهای مدرن جمعآوری زباله (GC) دارند که برای رسیدگی به این مورد به اندازه کافی هوشمند هستند.
کد زیر را در نظر بگیرید:
var theThing = null;
var replaceThing = function () {
var priorThing = theThing; // Hold on to the prior thing
var unused = function () {
// 'unused' is the only place where 'priorThing' is referenced,
// but 'unused' never gets invoked
if (priorThing) {
console.log("hi");
}
};
theThing = {
longStr: new Array(1000000).join('*'), // Create a 1MB object
someMethod: function () {
console.log(someMessage);
}
};
};
setInterval(replaceThing, 1000); // Invoke 'replaceThing' once every second
اگر کد بالا را اجرا کنید و میزان مصرف حافظه را کنترل کنید، متوجه خواهید شد که نشت حافظه قابل توجهی دارید، یک مگابایت کامل در ثانیه! و حتی یک زباله جمع کن دستی کمکی نمی کند. بنابراین به نظر می رسد که ما هر بار که replaceThing
فراخوانی می شود، longStr
را صدا می زنیم. اما چرا؟
نشت حافظه در جاوا اسکریپت تقریباً اجتناب ناپذیر است، اگر آگاهانه برای جلوگیری از آنها کدنویسی نکنید.
بیایید موارد را با جزئیات بیشتر بررسی کنیم:
هر شیء theThing
شامل شیء 1 مگابایتی longStr
خود است. هر ثانیه، هنگامی که ما replaceThing
را فرا میخوانیم، به یک ارجاع به شیء قبلی theThing
در priorThing
نگاه میدارد. اما ما هنوز فکر نمیکنیم که این مشکلی باشد، زیرا، هر بار که قبلاً ارجاع داده شده بود، ارجاع داده نمیشود (زمانی که priorThing
از طریق priorThing = theThing
بازنشانی میشود). علاوه بر این، فقط در بدنه اصلی replaceThing
و در تابع unused
ارجاع داده می شود که در واقع هرگز استفاده نمی شود.
بنابراین ما دوباره تعجب می کنیم که چرا در اینجا نشت حافظه وجود دارد.
برای درک آنچه در حال وقوع است، باید عملکرد درونی جاوا اسکریپت را بهتر درک کنیم. Closure ها معمولاً توسط هر شیء تابعی که به یک شی به سبک dictionary-style پیوند می خورد که محدوده کلمه های آن را نشان می دهد، اجرا می شود. اگر هر دو تابع تعریف شده در داخل replaceThing
واقعاً از priorThing
استفاده میکردند، مهم است که هر دو یک شیء مشابه را دریافت کنند، حتی اگر priorThing
بارها و بارها تخصیص داده شود تا هر دو تابع محیط کلمه یکسانی داشته باشند. اما به محض اینکه یک متغیر توسط هر Closure استفاده میشود، به محیط کلمه میرسد که همه Closure ها در آن محدوده مشترک هستند. و همین تفاوت جزئی همان چیزی است که منجر به این نشت بد حافظه می شود.
نشت حافظه مثال 2: رفرنس های دایره ای
این قطعه کد را در نظر بگیرید:
function addClickHandler(element) {
element.click = function onClick(e) {
alert("Clicked the " + element.nodeName)
}
}
در اینجا، onClick
یک کلاسور دارد که ارجاع به عنصر را نگه میدارد (از طریق element.nodeName
). با اختصاص دادن onClick
به element.click
، مرجع دایره ای ایجاد می شود، یعنی element → onClick → element → onClick → element…
جالب توجه است، حتی اگر عنصر از DOM حذف شود، خود مرجع دایرهای بالا از جمعآوری عنصر و onClick
جلوگیری میکند و در نتیجه به نشت حافظه تبدیل میشود.
اجتناب از نشت حافظه: ملزومات
مدیریت حافظه جاوا اسکریپت (و به ویژه جمعآوری زبالههای آن) تا حد زیادی مبتنی بر مفهوم دسترسی به آبجکت است.
آبجکت های زیر در دسترس فرض می شوند و به عنوان "ریشه" شناخته می شوند:
- اشیاء ارجاع شده از هر نقطه در پشته تماس فعلی (یعنی همه متغیرها و پارامترهای محلی در توابعی که در حال حاضر فراخوانی می شوند و همه متغیرهای موجود در محدوده کلاسور)
- همه متغیرهای سراسری
آبجکت ها حداقل تا زمانی که از هر یک از ریشه ها از طریق یک مرجع یا زنجیره ای از مراجع قابل دسترسی باشند در حافظه نگهداری می شوند.
یک زباله جمع کن در مرورگر وجود دارد که حافظه اشغال شده توسط اشیاء غیرقابل دسترسی را پاک می کند. به عبارت دیگر، اشیا از حافظه حذف خواهند شد اگر و تنها در صورتی که GC معتقد باشد که آنها غیرقابل دسترسی هستند. متأسفانه، پایان دادن به آبجکت های «زامبی» منقرض شده که دیگر مورد استفاده قرار نمیگیرند، اما GC هنوز فکر میکند قابل دسترسی هستند، بسیار آسان است.
شماره 4 جاوا اسکریپت: سردرگمی درباره برابری
یکی از سهولت های جاوا اسکریپت این است که به طور خودکار هر مقداری را که در یک زمینه بولی به یک مقدار بولی ارجاع می شود، وادار می کند. اما مواردی وجود دارد که در آن می تواند به همان اندازه که راحت باشد گیج کننده باشد. به عنوان مثال، عبارات زیر برای بسیاری از توسعه دهندگان جاوا اسکریپت مشکل ساز هستند:
// All of these evaluate to 'true'!
console.log(false == '0');
console.log(null == undefined);
console.log(" \t\r\n" == 0);
console.log('' == 0);
// And these do too!
if ({}) // ...
if ([]) // ...
با توجه به دو مورد آخر، علیرغم خالی بودن (که ممکن است شما را به این باور برساند که آنها به غلط ارزیابی میشوند)، هر دو {}
و []
در واقع آبجکت هستند و هر آبجکت به مقدار بولی true در جاوا اسکریپت وادار میشود. ، مطابق با مشخصات ECMA-262.
همانطور که این مثالها نشان میدهند، قواعد نوع اجبار گاهی میتواند مانند نور روشن باشد. بر این اساس، مگر اینکه نوع اجبار صراحتاً مورد نظر باشد، معمولاً بهتر است از ===
و ==!
(به جای ==
و =!
) برای جلوگیری از هرگونه باگ های ناخواسته اجبار نوع استفاده کنید. (==
و =!
هنگام مقایسه دو چیز به طور خودکار تبدیل نوع را انجام می دهند، در حالی که ===
و !==
بدون تبدیل نوع یکسان را انجام می دهند.)
از آنجایی که ما در مورد نوع اجبار و مقایسه صحبت می کنیم، لازم به ذکر است که مقایسه NaN
با هر چیزی (حتی !NaN
) همیشه نادرست است. بنابراین نمی توانید از عملگرهای برابری (==
، ===
، =!
، ==!
) برای تعیین اینکه آیا یک مقدار NaN
است یا خیر استفاده کنید. در عوض، از تابع ()isNaN
گلوبال داخلی استفاده کنید:
console.log(NaN == NaN); // False
console.log(NaN === NaN); // False
console.log(isNaN(NaN)); // True
شماره 5 جاوا اسکریپت: دستکاری ناکارآمد DOM
جاوا اسکریپت دستکاری DOM را نسبتاً آسان می کند (یعنی افزودن، اصلاح و حذف عناصر)، اما کاری برای ارتقای کارآمد این کار انجام نمی دهد.
یک مثال معمول کدی است که یک سری عناصر DOM را یکی یکی اضافه می کند. افزودن یک عنصر DOM یک عملیات گران است و کدی که چندین عنصر DOM را به طور متوالی اضافه می کند ناکارآمد است و احتمالاً به خوبی کار نمی کند.
یکی از جایگزین های موثر زمانی که چندین عنصر DOM باید اضافه شود، استفاده از قطعات سند به جای آن است که کارایی و عملکرد را بهبود می بخشد.
مثلا:
const div = document.getElementById("my_div");
const fragment = document.createDocumentFragment();
const elems = document.querySelectorAll('a');
for (let e = 0; e < elems.length; e++) {
fragment.appendChild(elems[e]);
}
div.appendChild(fragment.cloneNode(true));
علاوه بر کارایی ذاتی بهبود یافته این رویکرد، ایجاد عناصر DOM متصل گران است، در حالی که ایجاد و اصلاح آنها در حین جدا شدن و سپس اتصال آنها عملکرد بسیار بهتری را به همراه دارد.
شماره 6 جاوا اسکریپت: استفاده نادرست از تعاریف تابع در داخل حلقه for
این کد را در نظر بگیرید:
var elements = document.getElementsByTagName('input');
var n = elements.length; // Assume we have 10 elements for this example
for (var i = 0; i < n; i++) {
elements[i].onclick = function() {
console.log("This is element #" + i);
};
}
بر اساس کد بالا، اگر 10 عنصر ورودی وجود داشت، با کلیک بر روی هر یک از آنها "This is element #10"
نمایش داده می شود! این به این دلیل است که تا زمانی که onclick
برای هر یک از عناصر فراخوانی شود، حلقه for
بالا تکمیل شده و مقدار i
در حال حاضر 10 خواهد بود (برای همه آنها).
در اینجا نحوه اصلاح این مشکل جاوا اسکریپت برای دستیابی به رفتار مورد نظر آمده است:
var elements = document.getElementsByTagName('input');
var n = elements.length; // Assume we have 10 elements for this example
var makeHandler = function(num) { // Outer function
return function() { // Inner function
console.log("This is element #" + num);
};
};
for (var i = 0; i < n; i++) {
elements[i].onclick = makeHandler(i+1);
}
در این نسخه اصلاحشده کد، هر بار که از حلقه عبور میکنیم، makeHandler
بلافاصله اجرا میشود، هر بار مقدار فعلی i+1
را دریافت کرده و آن را به یک متغیر num
با scope متصل میکند. تابع خارجی تابع داخلی را برمی گرداند (که از این متغیر num
نیز استفاده می کند) و onclick
عنصر روی آن تابع داخلی تنظیم می شود. این تضمین می کند که هر onclick
مقدار i
مناسب را دریافت کرده و از آن استفاده می کند (از طریق متغیر num scoped).
شماره 7 جاوا اسکریپت: عدم استفاده صحیح از وراثت نمونه اولیه
تعداد بسیار زیادی از توسعه دهندگان جاوا اسکریپت قادر به درک کامل و بنابراین به طور کامل از ویژگی های وراثت نمونه اولیه نیستند.
در اینجا یک مثال ساده است:
BaseObject = function(name) {
if (typeof name !== "undefined") {
this.name = name;
} else {
this.name = 'default'
}
};
این نسبتاً ساده به نظر می رسد. اگر name
ارائه می کنید، از آن استفاده کنید، در غیر این صورت نام را روی "default
" تنظیم کنید. برای مثال:
var firstObj = new BaseObject();
var secondObj = new BaseObject('unique');
console.log(firstObj.name); // -> Results in 'default'
console.log(secondObj.name); // -> Results in 'unique'
اما اگر بخواهیم این کار را انجام دهیم چه می شود:
delete secondObj.name;
سپس دریافت می کنیم:
console.log(secondObj.name); // -> Results in 'undefined'
اما آیا بهتر نیست که این به «default
» برگردد؟ اگر کد اصلی را برای استفاده از وراثت نمونه اولیه، به صورت زیر تغییر دهیم، به راحتی می توان این کار را انجام داد:
BaseObject = function (name) {
if(typeof name !== "undefined") {
this.name = name;
}
};
BaseObject.prototype.name = 'default';
با این نسخه، BaseObject
ویژگی نام را از شی نمونه اولیه خود به ارث می برد، جایی که (به طور پیش فرض) روی "default
" تنظیم شده است. بنابراین، اگر سازنده بدون نام فراخوانی شود، نام به طور پیش فرض default
خواهد بود. به طور مشابه، اگر ویژگی name از نمونه ای از BaseObject
حذف شود، زنجیره نمونه اولیه جستجو می شود و ویژگی name از شی نمونه اولیه که مقدار آن هنوز "default
" است بازیابی می شود. بنابراین اکنون دریافت می کنیم:
var thirdObj = new BaseObject('unique');
console.log(thirdObj.name); // -> Results in 'unique'
delete thirdObj.name;
console.log(thirdObj.name); // -> Results in 'default'
شماره 8 جاوا اسکریپت: ایجاد ارجاعات نادرست به متد های نمونه
بیایید یک شی ساده تعریف کنیم و یک نمونه از آن را به صورت زیر ایجاد کنیم:
var MyObjectFactory = function() {}
MyObjectFactory.prototype.whoAmI = function() {
console.log(this);
};
var obj = new MyObjectFactory();
اکنون، برای راحتی، بیایید یک مرجع به متد whoAmI
ایجاد کنیم، احتمالاً تا بتوانیم صرفاً با ()whoAmI
به آن دسترسی داشته باشیم و نه از ()obj.whoAmI
:
var whoAmI = obj.whoAmI;
و فقط برای اطمینان از اینکه ما یک مرجع به یک تابع ذخیره کرده ایم، بیایید مقدار متغیر whoAmI
جدید خود را چاپ کنیم:
console.log(whoAmI);
خروجی ها:
function () {
console.log(this);
}
اما وقتی که ()obj.whoAmI
را در مقابل مرجع راحتی ()whoAmI
فراخوانی می کنیم، به تفاوت نگاه کنید:
obj.whoAmI(); // Outputs "MyObjectFactory {...}" (as expected)
whoAmI(); // Outputs "window" (uh-oh!)
چه چیزی اشتباه پیش رفت؟ فراخوانی ()whoAmI
ما در فضای نام سراسری است، بنابراین روی window (یا در حالت strict ، تعریف نشده) تنظیم می شود، نه به نمونه obj MyObjectFactory
! به عبارت دیگر، مقدار این معمولاً به context فراخوانی بستگی دارد.
توابع پیکان ({} <=(params)
به جای {}function(params)
یک استاتیک را ارائه می دهند که بر اساس context فراخوانی نیست، مانند this
برای توابع معمولی. این یک راه حل به ما می دهد:
var MyFactoryWithStaticThis = function() {
this.whoAmI = () => { // Note the arrow notation here
console.log(this);
};
}
var objWithStaticThis = new MyFactoryWithStaticThis();
var whoAmIWithStaticThis = objWithStaticThis.whoAmI;
objWithStaticThis.whoAmI(); // Outputs "MyFactoryWithStaticThis" (as usual)
whoAmIWithStaticThis(); // Outputs "MyFactoryWithStaticThis" (arrow notation benefit)
ممکن است متوجه شده باشید که، حتی با وجود اینکه ما خروجی را مطابقت دادیم، این اشاره به کارخانه است، نه به نمونه. به جای تلاش برای رفع بیشتر این مشکل، ارزش آن را دارد که رویکردهایی را برای جاوا اسکریپت در نظر بگیریم که به هیچ وجه به این (یا حتی جدید) متکی نیستند.
شماره 9 جاوا اسکریپت: ارائه یک رشته به عنوان اولین آرگومان برای setTimeout
یا setInterval
برای شروع، اجازه دهید در اینجا چیزی را روشن کنیم: ارائه یک رشته به عنوان اولین آرگومان برای setTimeout
یا setInterva
l به خودی خود یک اشتباه نیست. این کد جاوا اسکریپت کاملاً قانونی است. مسئله در اینجا بیشتر مربوط به عملکرد و کارایی است. چیزی که اغلب نادیده گرفته می شود این است که اگر یک رشته را به عنوان اولین آرگومان به setTimeout
یا setInterval
ارسال کنید، به سازنده تابع ارسال می شود تا به یک تابع جدید تبدیل شود. این فرآیند می تواند کند و ناکارآمد باشد و به ندرت ضروری است.
جایگزینی برای ارسال یک رشته به عنوان اولین آرگومان برای این متدها این است که به جای آن یک تابع را ارسال کنید. بیایید به یک مثال نگاه کنیم.
بنابراین، در اینجا، استفاده نسبتاً معمولی از setInterval
و setTimeout
است که یک رشته را به عنوان اولین پارامتر ارسال می کند:
setInterval("logTime()", 1000);
setTimeout("logMessage('" + msgValue + "')", 1000);
انتخاب بهتر این است که یک تابع را به عنوان آرگومان اولیه منتقل کنید، به عنوان مثال:
setInterval(logTime, 1000); // Passing the logTime function to setInterval
setTimeout(function() { // Passing an anonymous function to setTimeout
logMessage(msgValue); // (msgValue is still accessible in this scope)
}, 1000);
شماره 10 جاوا اسکریپت: عدم استفاده از “Strict Mode”
همانطور که می دانید، «Strict Mode» (به عنوان مثال، «use strict
»؛ در ابتدای فایلهای منبع جاوا اسکریپت شما) راهی برای اجرای داوطلبانه تجزیه سختتر و مدیریت خطا در کد جاوا اسکریپت شما در زمان اجرا است. به عنوان راهی برای امن تر کردن کد شما.
در حالی که، مسلماً، عدم استفاده از حالت سختگیرانه واقعاً یک "اشتباه" نیست، استفاده از آن به طور فزاینده ای تشویق می شود و حذف آن به طور فزاینده ای به شکل بد در نظر گرفته می شود.
در اینجا برخی از مزایای کلیدی حالت سخت وجود دارد:
- اشکال زدایی را آسان تر می کند. خطاهای کدی که در غیر این صورت نادیده گرفته میشدند یا بیصدا شکست میخوردند، اکنون خطاهایی ایجاد میکنند یا استثناهایی ایجاد میکنند، و شما را زودتر از مشکلات جاوا اسکریپت در پایگاه کدتان آگاه میکنند و شما را سریعتر به منبع خود هدایت میکنند.
- از گلوبال شدن تصادفی جلوگیری می کند. بدون Strict Mode، اختصاص یک مقدار به یک متغیر اعلام نشده به طور خودکار یک متغیر سراسری با آن نام ایجاد می کند. این یکی از رایج ترین خطاهای جاوا اسکریپت است. در حالت سخت، تلاش برای انجام این کار با خطا مواجه می شود.
- اجبار
this
را از بین می برد. بدون Strict Mode، ارجاع به this مقدارnull
یاundefined
به طور خودکار به متغیرglobalThis
اجباری می شود. این می تواند بسیاری از اشکالات خسته کننده را ایجاد کند. در Strict Mode، ارجاع دادن به this مقدار null یا undefined یک خطا ایجاد می کند.
- نام ویژگیها یا مقادیر پارامترهای تکراری را مجاز نمیداند. حالت دقیق زمانی که یک ویژگی تکراری با نام را در یک شیء تشخیص میدهد (مثلاً var object = {foo: "bar", foo: "baz"};) یا یک آرگومان تکراری با نام برای یک تابع (مثلاً، function foo( val1، val2، val1){})، به این ترتیب اشکالی در کد شما پیدا میشود که در غیر این صورت ممکن است زمان قابل توجهی را برای پیگیری آن تلف کرده باشید.
- ()eval را ایمن تر می کند. تفاوت هایی در نحوه رفتار
()eval
در حالت سخت و در حالت غیر محدود وجود دارد. مهمتر از همه، در حالت سخت، متغیرها و توابع اعلام شده در داخل یک عبارت()eval
در محدوده حاوی ایجاد نمی شوند. (آنها در scope در حالت غیر محدود ایجاد می شوند، که همچنین می تواند منبع رایج مشکلات جاوا اسکریپت باشد.)
- خطای استفاده نامعتبر از
delete
را ایجاد می کند. عملگرdelete
(که برای حذف خصوصیات از اشیاء استفاده می شود) نمی تواند در ویژگی های غیرقابل تنظیم شی مورد استفاده قرار گیرد. وقتی تلاش برای حذف یک ویژگی غیرقابل تنظیم انجام شود، کد غیرمستقیم بیصدا شکست میخورد، در حالی که حالت سخت در چنین حالتی خطا ایجاد میکند.
نتیجه
همانطور که در مورد هر فناوری صدق می کند، هرچه بهتر بفهمید که چرا و چگونه جاوا اسکریپت کار می کند و کار نمی کند، کد شما قوی تر خواهد بود و بیشتر می توانید از قدرت واقعی زبان استفاده کنید. برعکس، عدم درک صحیح پارادایم ها و مفاهیم جاوا اسکریپت جایی است که بسیاری از مشکلات جاوا اسکریپت در آن نهفته است. آشنایی کامل با ظرافت ها و ظرافت های زبان موثرترین استراتژی برای بهبود مهارت و افزایش بهره وری شما است.
از جهنم یادگیری یا همان آموزش بیاید بیرون!