كود Buggy JavaScript: الأخطاء العشرة الأكثر شيوعًا التي يرتكبها مطورو JavaScript
نشرت: 2022-03-11اليوم ، تعد JavaScript في صميم جميع تطبيقات الويب الحديثة تقريبًا. شهدت السنوات العديدة الماضية على وجه الخصوص انتشار مجموعة واسعة من المكتبات والأطر القوية القائمة على JavaScript لتطوير تطبيقات الصفحة الواحدة (SPA) والرسومات والرسوم المتحركة وحتى منصات JavaScript من جانب الخادم. أصبحت JavaScript موجودة في كل مكان حقًا في عالم تطوير تطبيقات الويب ، وبالتالي فهي مهارة ذات أهمية متزايدة لإتقانها.
للوهلة الأولى ، قد يبدو JavaScript بسيطًا جدًا. وبالفعل ، فإن إنشاء وظائف JavaScript الأساسية في صفحة ويب يعد مهمة مباشرة إلى حد ما لأي مطور برامج متمرس ، حتى لو كان مستخدمًا جديدًا لـ JavaScript. ومع ذلك ، فإن اللغة أكثر دقة وقوة وتعقيدًا بشكل ملحوظ مما قد يعتقده المرء في البداية. في الواقع ، تؤدي العديد من خواص JavaScript الدقيقة إلى عدد من المشكلات الشائعة التي تمنعها من العمل - 10 منها نناقشها هنا - والتي من المهم أن تكون على دراية بها وتتجنبها في سعي المرء لأن يصبح مطور JavaScript رئيسيًا.
الخطأ الشائع الأول: إشارات غير صحيحة إلى this
سمعت ذات مرة ممثلًا كوميديًا يقول:
أنا لست هنا حقًا ، لأن ما يوجد هنا ، إلى جانب ذلك ، بدون حرف "t"؟
تميز هذه النكتة من نواح كثيرة نوع الارتباك الذي يوجد غالبًا للمطورين فيما يتعلق this
الكلمة الأساسية في JavaScript. أعني ، هل this
حقًا ، أم أنه شيء آخر تمامًا؟ أم أنها غير محددة؟
نظرًا لأن تقنيات ترميز JavaScript وأنماط التصميم أصبحت متطورة بشكل متزايد على مر السنين ، كانت هناك زيادة مقابلة في انتشار نطاقات المرجع الذاتي داخل عمليات الاسترجاعات والإغلاق ، والتي تعد مصدرًا شائعًا إلى حد ما لـ "هذا / هذا الارتباك".
ضع في اعتبارك مثال مقتطف الشفرة هذا:
Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // what is "this"? }, 0); };
ينتج عن تنفيذ الكود أعلاه الخطأ التالي:
Uncaught TypeError: undefined is not a function
لماذا ا؟
الأمر كله يتعلق بالسياق. سبب حصولك على الخطأ أعلاه هو أنك عندما تستدعي setTimeout()
، فأنت في الواقع تستدعي window.setTimeout()
. نتيجة لذلك ، يتم تعريف الوظيفة المجهولة التي يتم تمريرها إلى setTimeout()
في سياق كائن window
، الذي لا يحتوي على طريقة clearBoard()
.
الحل التقليدي المتوافق مع المستعرض القديم هو ببساطة حفظ المرجع الخاص بك إلى this
في متغير يمكن توارثه بعد ذلك عن طريق الإغلاق ؛ على سبيل المثال:
Game.prototype.restart = function () { this.clearLocalStorage(); var 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(); // ahhh, back in the context of the right 'this'! };
الخطأ الشائع الثاني: التفكير في وجود نطاق على مستوى الكتلة
كما تمت مناقشته في دليل التوظيف الخاص بجافا سكريبت ، فإن أحد المصادر الشائعة للارتباك بين مطوري جافا سكريبت (وبالتالي مصدر مشترك للأخطاء) يفترض أن جافا سكريبت تنشئ نطاقًا جديدًا لكل كتلة تعليمات برمجية. على الرغم من أن هذا صحيح في العديد من اللغات الأخرى ، إلا أنه غير صحيح في JavaScript. ضع في اعتبارك ، على سبيل المثال ، الكود التالي:
for (var i = 0; i < 10; i++) { /* ... */ } console.log(i); // what will this output?
إذا كنت تعتقد أن استدعاء console.log()
إما أن ينتج undefined
أو يتسبب في خطأ ، فقد خمنت بشكل غير صحيح. صدق أو لا تصدق ، سيخرج 10
. لماذا ا؟
في معظم اللغات الأخرى ، قد يؤدي الكود أعلاه إلى خطأ لأن "الحياة" (أي النطاق) للمتغير i
ستقتصر على الكتلة for
. على الرغم من ذلك ، في JavaScript ، ليس هذا هو الحال ويظل المتغير i
في النطاق حتى بعد اكتمال حلقة for
، مع الاحتفاظ بقيمته الأخيرة بعد الخروج من الحلقة. (يُعرف هذا السلوك ، بالمناسبة ، بالرفع المتغير).
ومع ذلك ، تجدر الإشارة إلى أن دعم النطاقات على مستوى الكتلة يشق طريقه إلى JavaScript من خلال الكلمة الرئيسية let
الجديدة. الكلمة الرئيسية let
متاحة بالفعل في JavaScript 1.7 ومن المقرر أن تصبح كلمة رئيسية JavaScript مدعومة رسميًا اعتبارًا من ECMAScript 6.
جديد على JavaScript؟ اقرأ عن النطاقات والنماذج الأولية والمزيد.
الخطأ الشائع الثالث: إحداث تسرب للذاكرة
تسريبات الذاكرة هي مشاكل جافا سكريبت لا مفر منها تقريبًا إذا لم تكن تعمد في الترميز لتجنبها. هناك العديد من الطرق لحدوثها ، لذلك سنقوم فقط بتسليط الضوء على اثنين من أكثر حالات حدوثها شيوعًا.
تسرب في الذاكرة مثال 1: الإشارات المتدلية إلى الكائنات البائدة
ضع في اعتبارك الكود التالي:
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
إذا قمت بتشغيل الكود أعلاه وراقبت استخدام الذاكرة ، فستجد أن لديك تسريبًا هائلاً للذاكرة ، مما يؤدي إلى تسريب ميغا بايت كاملة في الثانية! وحتى GC اليدوي لا يساعد. لذلك يبدو أننا longStr
طويلاً في كل مرة يتم استدعاء replaceThing
. لكن لماذا؟
دعنا نفحص الأشياء بمزيد من التفصيل:
يحتوي كل كائن theThing
على كائن طوله 1 ميغا longStr
. في كل ثانية ، عندما نطلق replaceThing
، فإنه يحتفظ بإشارة إلى الكائن theThing
في الشيء priorThing
. لكننا ما زلنا لا نعتقد أن هذا سيكون مشكلة ، لأنه في كل مرة خلال ، سيتم إلغاء الإشارة إلى priorThing
المشار إليه سابقًا (عند إعادة تعيين priorThing
عبر priorThing = theThing;
). علاوة على ذلك ، تتم الإشارة إليه فقط في الجزء الرئيسي من replaceThing
وفي الوظيفة unused
والتي في الواقع لم يتم استخدامها مطلقًا.
لذا مرة أخرى ، تركنا نتساءل لماذا يوجد تسرب للذاكرة هنا !؟
لفهم ما يحدث ، نحتاج إلى فهم أفضل لكيفية عمل الأشياء في JavaScript تحت الغطاء. الطريقة النموذجية التي يتم بها تنفيذ عمليات الإغلاق هي أن كل كائن دالة له رابط إلى كائن على غرار القاموس يمثل نطاقه المعجمي. إذا تم تعريف كلتا الوظيفتين داخل replaceThing
بالفعل ، priorThing
من المهم أن يحصل كلاهما على نفس الكائن ، حتى لو تم تعيين priorThing
مرارًا وتكرارًا ، لذلك تشترك كلتا الوظيفتين في نفس البيئة المعجمية. ولكن بمجرد استخدام أي متغير بواسطة أي إغلاق ، ينتهي به الأمر في البيئة المعجمية المشتركة بين جميع عمليات الإغلاق في هذا النطاق. وهذا الفارق البسيط هو ما يؤدي إلى هذا التسرب الشائك للذاكرة. (يتوفر المزيد من التفاصيل حول هذا هنا.)
مثال تسرب الذاكرة 2: مراجع دائرية
ضع في اعتبارك جزء التعليمات البرمجية هذا:
function addClickHandler(element) { element.click = function onClick(e) { alert("Clicked the " + element.nodeName) } }
هنا ، يحتوي onClick
على إغلاق يحافظ على مرجع element
(عبر element.nodeName
). من خلال تعيين onClick
to element.click
أيضًا ، يتم إنشاء المرجع الدائري ؛ على سبيل المثال: element
-> onClick
-> element
-> onClick
-> element
…
ومن المثير للاهتمام ، أنه حتى إذا تمت إزالة element
من DOM ، فإن المرجع الذاتي الدائري أعلاه سيمنع تجميع element
و onClick
، وبالتالي ، تسرب الذاكرة.
تجنب تسرب الذاكرة: ما تحتاج إلى معرفته
تعتمد إدارة ذاكرة JavaScript (وفي جمع البيانات المهملة ، في paticular) إلى حد كبير على فكرة قابلية الوصول إلى الكائن.
من المفترض أن تكون الكائنات التالية قابلة للوصول وتُعرف باسم "الجذور":
- الكائنات المشار إليها من أي مكان في مكدس الاستدعاء الحالي (أي ، جميع المتغيرات والمعلمات المحلية في الوظائف التي يتم استدعاؤها حاليًا ، وجميع المتغيرات في نطاق الإغلاق)
- جميع المتغيرات العامة
يتم الاحتفاظ بالكائنات في الذاكرة على الأقل طالما يمكن الوصول إليها من أي من الجذور من خلال مرجع ، أو سلسلة من المراجع.
يوجد Garbage Collector (GC) في المتصفح تقوم بتنظيف الذاكرة التي تشغلها الكائنات التي لا يمكن الوصول إليها ؛ على سبيل المثال ، ستتم إزالة الأشياء من الذاكرة إذا وفقط إذا اعتقد GC أنه يتعذر الوصول إليها. لسوء الحظ ، من السهل جدًا أن ينتهي الأمر بكائنات "الزومبي" البائدة التي لم تعد في الواقع قيد الاستخدام ولكن لا يزال GC يعتقد أنه "يمكن الوصول إليها".
الخطأ الشائع الرابع: الارتباك حول المساواة
تتمثل إحدى وسائل الراحة في JavaScript في أنها ستفرض تلقائيًا أي قيمة يتم الإشارة إليها في سياق منطقي إلى قيمة منطقية. ولكن هناك حالات يمكن أن يكون فيها هذا محيرًا بقدر ما هو ملائم. بعض ما يلي ، على سبيل المثال ، من المعروف أنه يعض العديد من مطوري JavaScript:
// 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 ([]) // ...
فيما يتعلق بالاثنين الأخيرين ، على الرغم من كونهما فارغين (مما قد يدفع المرء للاعتقاد بأنه سيتم التقييم على false
) ، فإن كلا من {}
و []
كائنات في الواقع وسيتم إجبار أي كائن على قيمة منطقية true
في جافا سكريبت ، بما يتوافق مع مواصفات ECMA-262.
كما توضح هذه الأمثلة ، يمكن أن تكون قواعد نوع الإكراه في بعض الأحيان واضحة مثل الطين. وفقًا لذلك ، ما لم يكن نوع الإكراه مرغوبًا بشكل صريح ، فمن الأفضل عادةً استخدام ===
و !==
(بدلاً من ==
و !=
) ، وذلك لتجنب أي آثار جانبية غير مقصودة للإكراه من النوع. ( ==
و !=
إجراء تحويل النوع تلقائيًا عند مقارنة شيئين ، بينما ===
و !==
يقومان بنفس المقارنة بدون تحويل النوع.)
وبشكل كامل كنقطة جانبية - ولكن نظرًا لأننا نتحدث عن نوع الإكراه والمقارنات - فمن الجدير بالذكر أن مقارنة NaN
بأي شيء (حتى NaN
!) سيعود دائمًا إلى false
. لذلك لا يمكنك استخدام عوامل المساواة ( ==
، ===
، !=
، !==
) لتحديد ما إذا كانت القيمة هي NaN
أم لا. بدلاً من ذلك ، استخدم دالة isNaN()
العالمية المضمنة:
console.log(NaN == NaN); // false console.log(NaN === NaN); // false console.log(isNaN(NaN)); // true
الخطأ الشائع الخامس: التلاعب غير الفعال في DOM
تجعل JavaScript من السهل نسبيًا معالجة DOM (أي إضافة وتعديل وإزالة العناصر) ، لكنها لا تفعل شيئًا للترويج لفعل ذلك بكفاءة.
المثال الشائع هو الكود الذي يضيف سلسلة من عناصر DOM واحدًا تلو الآخر. تعد إضافة عنصر DOM عملية مكلفة. الكود الذي يضيف عناصر DOM متعددة على التوالي غير فعال ومن المحتمل ألا يعمل بشكل جيد.

أحد البدائل الفعالة عند الحاجة إلى إضافة عناصر DOM متعددة هو استخدام أجزاء المستند بدلاً من ذلك ، وبالتالي تحسين الكفاءة والأداء.
علي سبيل المثال:
var div = document.getElementsByTagName("my_div"); var fragment = document.createDocumentFragment(); for (var e = 0; e < elems.length; e++) { // elems previously set to list of elements fragment.appendChild(elems[e]); } div.appendChild(fragment.cloneNode(true));
بالإضافة إلى الكفاءة المحسّنة بطبيعتها لهذا الأسلوب ، فإن إنشاء عناصر DOM المرفقة أمر مكلف ، في حين أن إنشائها وتعديلها أثناء فصلها ثم إرفاقها يؤدي إلى أداء أفضل بكثير.
الخطأ الشائع السادس: الاستخدام غير الصحيح لتعريفات الوظائف داخل حلقات 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
بمتغير عدد محدد النطاق. تقوم الوظيفة الخارجية بإرجاع الوظيفة الداخلية (والتي تستخدم أيضًا متغير num
المحدد النطاق) ويتم تعيين العنصر onclick
الخاص بالعنصر على تلك الوظيفة الداخلية. هذا يضمن أن كل onclick
تستقبل وتستخدم قيمة i
المناسبة (عبر متغير num
المحدد النطاق).
الخطأ الشائع رقم 7: الفشل في الاستفادة بشكل صحيح من وراثة النموذج الأولي
نسبة عالية بشكل مدهش من مطوري JavaScript يفشلون في الفهم الكامل ، وبالتالي الاستفادة الكاملة من ميزات وراثة النموذج الأولي.
هذا مثال بسيط. ضع في اعتبارك هذا الرمز:
BaseObject = function(name) { if(typeof name !== "undefined") { this.name = name; } else { this.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'
لكن ألن يكون من الأجمل العودة إلى "الافتراضي"؟ يمكن القيام بذلك بسهولة ، إذا قمنا بتعديل الكود الأصلي للاستفادة من وراثة النموذج الأولي ، على النحو التالي:
BaseObject = function (name) { if(typeof name !== "undefined") { this.name = name; } }; BaseObject.prototype.name = 'default';
باستخدام هذا الإصدار ، يرث BaseObject
خاصية name
من كائن prototype
الخاص به ، حيث يتم تعيينه (افتراضيًا) على 'default'
. وبالتالي ، إذا تم استدعاء المُنشئ بدون اسم ، فسيتم تعيين الاسم افتراضيًا على الوضع default
. وبالمثل ، إذا تمت إزالة خاصية name
من مثيل BaseObject
، فسيتم البحث بعد ذلك عن سلسلة النموذج الأولي وسيتم استرداد خاصية name
من كائن prototype
حيث تظل قيمتها '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 MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject();
الآن ، للتيسير ، دعنا ننشئ مرجعًا إلى طريقة whoAmI
، على الأرجح حتى نتمكن من الوصول إليها فقط من خلال whoAmI()
بدلاً من الكائن الأطول obj.whoAmI()
:
var whoAmI = obj.whoAmI;
وللتأكد فقط من أن كل شيء يبدو متماثلًا ، دعنا نطبع قيمة متغير whoAmI
الجديد:
console.log(whoAmI);
المخرجات:
function () { console.log(this === window ? "window" : "MyObj"); }
حسنا جيد. تبدو على ما يرام.
لكن الآن ، انظر إلى الاختلاف عندما obj.whoAmI()
مقابل مرجع الملاءمة whoAmI()
:
obj.whoAmI(); // outputs "MyObj" (as expected) whoAmI(); // outputs "window" (uh-oh!)
ماذا حصل؟
الهدف هنا هو أننا عندما قمنا var whoAmI = obj.whoAmI;
، تم تعريف المتغير الجديد whoAmI
في مساحة الاسم العالمية . نتيجة لذلك ، this
قيمته هي window
، وليس مثيل obj
لـ MyObject
!
وبالتالي ، إذا احتجنا حقًا إلى إنشاء مرجع لطريقة موجودة لكائن ما ، فنحن بحاجة إلى التأكد من القيام بذلك داخل مساحة اسم هذا الكائن ، للحفاظ على قيمة this
. تتمثل إحدى طرق القيام بذلك ، على سبيل المثال ، في ما يلي:
var MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject(); obj.w = obj.whoAmI; // still in the obj namespace obj.whoAmI(); // outputs "MyObj" (as expected) obj.w(); // outputs "MyObj" (as expected)
الخطأ الشائع 9: تقديم سلسلة كأول وسيطة لـ setTimeout
أو setInterval
بالنسبة للمبتدئين ، لنكن واضحين بشأن شيء ما هنا: تقديم سلسلة باعتبارها الوسيطة الأولى ل setTimeout
أو setInterval
ليس خطأ بحد ذاته. إنه كود JavaScript شرعي تمامًا. القضية هنا تتعلق بالأداء والكفاءة. ما نادرًا ما يتم شرحه هو أنه ، تحت غطاء المحرك ، إذا قمت بتمرير سلسلة كأول وسيطة إلى 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: عدم استخدام "الوضع المتشدد"
كما هو موضح في دليل التوظيف الخاص بجافا سكريبت ، فإن "الوضع الصارم" (على سبيل المثال ، بما في ذلك 'use strict';
في بداية ملفات مصدر جافا سكريبت) هو وسيلة لفرض تحليل أكثر صرامة ومعالجة الأخطاء على شفرة جافا سكريبت في وقت التشغيل ، أيضًا لجعله أكثر أمانًا.
بينما ، من المسلم به أن الفشل في استخدام الوضع الصارم ليس "خطأ" في حد ذاته ، يتم تشجيع استخدامه بشكل متزايد وأصبح إغفاله بشكل متزايد يعتبر شكلاً سيئًا.
فيما يلي بعض الفوائد الرئيسية للوضع المتشدد:
- يجعل التصحيح أسهل. أخطاء التعليمات البرمجية التي كان من الممكن تجاهلها أو كانت ستفشل بصمت ستولد الآن أخطاء أو تلقي استثناءات ، وتنبهك عاجلاً إلى مشاكل في التعليمات البرمجية وتوجهك بسرعة أكبر إلى مصدرها.
- يمنع الكرات الأرضية العرضية. بدون الوضع المتشدد ، يؤدي تعيين قيمة إلى متغير غير معرّف إلى إنشاء متغير عام بهذا الاسم تلقائيًا. يعد هذا أحد أكثر الأخطاء شيوعًا في JavaScript. في الوضع المتشدد ، تؤدي محاولة القيام بذلك إلى حدوث خطأ.
- يزيل
this
الإكراه . بدون الوضع المتشدد ، يتم إجبار الإشارة إلىthis
القيمة فارغة أو غير محددة تلقائيًا على القيمة العامة. يمكن أن يتسبب هذا في حدوث العديد من أنواع الحشرات المزيفة. في الوضع المتشدد ، يؤدي الرجوع إلى aa إلىthis
null أو undefined إلى حدوث خطأ. - لا يسمح بتكرار أسماء الخصائص أو قيم المعلمات. يعرض الوضع المتشدد خطأً عندما يكتشف خاصية مسماة مكررة في كائن (على سبيل المثال ،
var object = {foo: "bar", foo: "baz"};
) أو وسيطة مسماة مكررة لوظيفة (على سبيل المثال ،function foo(val1, val2, val1){}
) ، وبالتالي اكتشاف ما يعتبر خطأ في شفرتك بشكل شبه مؤكد والذي ربما كنت قد أهدرت الكثير من الوقت في تعقبه. - يجعل EVAL () أكثر أمانًا. توجد بعض الاختلافات في الطريقة التي يتصرف بها
eval()
في الوضع المتشدد وفي الوضع غير المقيد. الأهم من ذلك ، في الوضع المتشدد ، لا يتم إنشاء المتغيرات والوظائف المعلنة داخل عبارةeval()
في النطاق المحتوي ( يتم إنشاؤها في النطاق المتضمن في الوضع غير المقيد ، والذي يمكن أن يكون أيضًا مصدرًا شائعًا للمشكلات). - يلقي خطأ في الاستخدام غير صالح
delete
. لا يمكن استخدام عاملdelete
(المستخدم لإزالة الخصائص من الكائنات) في الخصائص غير القابلة للتكوين للكائن. ستفشل التعليمات البرمجية غير الصارمة بصمت عند إجراء محاولة لحذف خاصية غير قابلة للتكوين ، بينما سيؤدي الوضع المتشدد إلى ظهور خطأ في مثل هذه الحالة.
يتم إحتوائه
كما هو الحال مع أي تقنية ، كلما فهمت بشكل أفضل لماذا وكيف تعمل JavaScript ولا تعمل ولا تعمل ، زادت صلابة شفرتك وزادت قدرتك على الاستفادة بشكل فعال من القوة الحقيقية للغة. على العكس من ذلك ، فإن الافتقار إلى الفهم الصحيح لنماذج ومفاهيم JavaScript هو في الواقع مكان تكمن فيه العديد من مشكلات JavaScript.
يعد التعرف على الفروق الدقيقة في اللغة ودقتها هي الاستراتيجية الأكثر فاعلية لتحسين كفاءتك وزيادة إنتاجيتك. سيساعدك تجنب العديد من أخطاء JavaScript الشائعة عندما لا يعمل JavaScript.