اكتساب الخصائص من النموذج الأولي
باستثناء null
وundefined
، يحتوي كل نوع بيانات أولي على
نموذج أولي، وهو عنصر لفّ للعنصر المقابل يقدّم طُرقًا للعمل
مع القيم. عند استدعاء طريقة أو البحث عن خاصية في عنصر أساسي، تُغلِّف JavaScript العنصر الأساسي من وراء الكواليس وتستدعي الطريقة أو تُجري عملية البحث عن الخاصية في عنصر الغلاف بدلاً من ذلك.
على سبيل المثال، لا تتضمّن السلسلة الثابتة أي طرق خاصة بها، ولكن يمكنك استدعاء الأسلوب
.toUpperCase()
عليها باستخدام العنصر المُغلفString
المقابل:
"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL
يُعرف هذا باسم الاكتساب النموذجي، أي اكتساب السمات والطُرق من الدالة الإنشائية المقابلة للقيمة.
Number.prototype
> Number { 0 }
> constructor: function Number()
> toExponential: function toExponential()
> toFixed: function toFixed()
> toLocaleString: function toLocaleString()
> toPrecision: function toPrecision()
> toString: function toString()
> valueOf: function valueOf()
> <prototype>: Object { … }
يمكنك إنشاء عناصر أساسية باستخدام أدوات الإنشاء هذه، بدلاً من تحديد
قيمها فقط. على سبيل المثال، يؤدي استخدام الدالة الإنشائية String
إلى إنشاء كائن
سلسلة، وليس سلسلة حرفية: كائن لا يحتوي فقط على قيمة السلسلة
، بل على جميع السمات والطُرق المُكتسَبة من الدالة الإنشائية.
const myString = new String( "I'm a string." );
myString;
> String { "I'm a string." }
typeof myString;
> "object"
myString.valueOf();
> "I'm a string."
في معظم الأحيان، تتصرف العناصر الناتجة وفقًا للقيم التي استخدمناها لتحديدها. على سبيل المثال، على الرغم من أنّ تحديد قيمة عددية باستخدام new Number
يؤدي إلى إنشاء عنصر يحتوي على جميع الطرق والخصائص الخاصة بالنموذج الأصلي Number
، يمكنك استخدام عوامل حسابية على
هذه العناصر تمامًا كما تفعل مع القيم الرقمية الثابتة:
const numberOne = new Number(1);
const numberTwo = new Number(2);
numberOne;
> Number { 1 }
typeof numberOne;
> "object"
numberTwo;
> Number { 2 }
typeof numberTwo;
> "object"
numberOne + numberTwo;
> 3
نادرًا ما ستحتاج إلى استخدام أدوات الإنشاء هذه، لأنّ ميزة التوريث من النموذج المضمّنة في JavaScript تعني أنّها لا توفّر أي فائدة عملية. يمكن أن يؤدي إنشاء العناصر الأساسية باستخدام الدوالّ الإنشائية أيضًا إلى نتائج غير متوقّعة، لأنّ النتيجة هي عنصر، وليس تعبيرًا حرفيًا بسيطًا:
let stringLiteral = "String literal."
typeof stringLiteral;
> "string"
let stringObject = new String( "String object." );
stringObject
> "object"
ويمكن أن يؤدي ذلك إلى تعقيد استخدام عوامل المقارنة الصارمة:
const myStringLiteral = "My string";
const myStringObject = new String( "My string" );
myStringLiteral === "My string";
> true
myStringObject === "My string";
> false
إدراج الفاصلة المنقوطة التلقائي (ASI)
أثناء تحليل نص برمجي، يمكن لمترجمي JavaScript استخدام ميزة تُعرف باسم إدراج الفاصلة المنقوطة التلقائي (ASI) لمحاولة تصحيح حالات علامات cotización المنقوطة المُهمَلة. إذا صادف منظِّم JavaScript رمزًا مميزًا غير مسموح به، يحاول إضافة فاصلة منقوطة قبل هذا الرمز المميّز لإصلاح الخطأ المحتمل في البنية، ما دام أحد الشروط التالية أو أكثر صحيحًا:
- ويتم فصل هذا الرمز المميّز عن الرمز المميّز السابق بفاصل سطر.
- الرمز المميّز هو
}
. - الرمز المميّز السابق هو
)
، وستكون الفاصلة المنقوطة المُدرَجة هي الفاصلة المنقوطة الختامية لعبارةdo
…while
.
لمزيد من المعلومات، يُرجى الرجوع إلى قواعد ASI.
على سبيل المثال، لن يؤدي حذف الفواصل المنقوطة بعد العبارات التالية إلى حدوث خطأ في النحو بسبب معالجة ASI:
const myVariable = 2
myVariable + 3
> 5
ومع ذلك، لا يمكن لـ ASI احتساب عبارات متعددة في السطر نفسه. إذا كتبت أكثر من عبارة واحدة في السطر نفسه، احرص على فصلها باستخدام نقطات المنتصف:
const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier
const myVariable = 2; myVariable + 3;
> 5
إنّ ASI هي محاولة لتصحيح الأخطاء، وليس نوعًا من المرونة النحوية المضمّنة في JavaScript. احرص على استخدام الفواصل المنقوطة حيثما ينطبق ذلك كي لا تعتمد علىها لإنشاء رمز صحيح.
الوضع المتشدد
لقد تطورت المعايير التي تحكم طريقة كتابة JavaScript إلى أبعد حد مقارنةً بأي شيء تم أخذه في الاعتبار أثناء التصميم المبكر للغة. يجب أن يتجنّب كل تغيير جديد في السلوك المتوقّع لسمة JavaScript حدوث أخطاء في المواقع الإلكترونية القديمة.
تعالج ES5 بعض المشاكل المستمرة في دلالات JavaScript بدون
إيقاف عمليات التنفيذ الحالية من خلال تقديم "الوضع الصارم"، وهي طريقة للاستفادة
من مجموعة أكثر تقييدًا من قواعد اللغة لأي نص برمجي كامل أو
دالة فردية. لتفعيل الوضع الصارم، استخدِم السلسلة الثابتة
"use strict"
متبوعة بنقطتَي فاصلة في السطر الأول من نص برمجي أو دالة:
"use strict";
function myFunction() {
"use strict";
}
يمنع "الوضع الصارم" بعض الإجراءات "غير الآمنة" أو الميزات المتوقّفة نهائيًا، ويعرض
أخطاء صريحة بدلاً من الأخطاء الشائعة "الصامتة"، ويحظر استخدام
البنى النحوية التي قد تتعارض مع ميزات اللغة المستقبلية. على سبيل المثال، أدّت قرارات التصميم المبكرة المتعلّقة بنطاق المتغيّر
إلى زيادة احتمالية أن "يلوث" المطوّرون النطاق العام عن طريق الخطأ عند
تعريف متغيّر، بغض النظر عن السياق الذي يحتوي عليه، من خلال حذف العبارة الافتتاحية
var
:
(function() {
mySloppyGlobal = true;
}());
mySloppyGlobal;
> true
لا يمكن لوقت تشغيل JavaScript الحديث تصحيح هذا السلوك بدون المخاطرة ب تعطيل أي موقع إلكتروني يعتمد عليه، سواء عن طريق الخطأ أو عن قصد. بدلاً من ذلك، تمنع لغة JavaScript الحديثة حدوث ذلك من خلال السماح للمطوّرين بتفعيل ميزة "وضع صارم" للعمل الجديد، وتفعيل هذا الوضع تلقائيًا فقط في سياق ميزات اللغة الجديدة التي لن تؤدي إلى إيقاف عمليات التنفيذ القديمة:
(function() {
"use strict";
mySloppyGlobal = true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal
يجب كتابة "use strict"
كأحد
القيم الثابتة للسلسلة.
لن يعمل تعبير نموذجي
(use strict
). يجب أيضًا تضمين "use strict"
قبل أي رمز
قابل للتنفيذ في السياق المقصود. وإلا، يتجاهل المترجم ذلك.
(function() {
"use strict";
let myVariable = "String.";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal
(function() {
let myVariable = "String.";
"use strict";
console.log( myVariable );
sloppyGlobal = true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope
حسب المرجع، حسب القيمة
يمكن أن يحتوي أي متغيّر، بما في ذلك سمات عنصر، مَعلمات الدالة، والعناصر في صفيف أو مجموعة أو خريطة، على قيمة أساسية أو قيمة مرجعية.
عند تعيين قيمة أساسية من متغيّر إلى آخر، ينشئ محرّك JavaScript نسخة من تلك القيمة ويحدّدها للمتغيّر.
عند تعيين عنصر (مثيلات فئة ومصفوفات ودوال) إلى متغيّر، بدلاً من إنشاء نسخة جديدة من هذا العنصر، يحتوي المتغيّر على إشارة إلى موضع العنصر المخزّن في الذاكرة. ولهذا السبب، يؤدي تغيير كائن يشير إليه متغيّر إلى تغيير الكائن المُشار إليه، وليس فقط القيمة التي يحتوي عليها هذا المتغيّر. على سبيل المثال، إذا تمّت بدء عملية حسابية لمتغيّر جديد باستخدام متغيّر يحتوي على إشارة إلى عنصر، ثمّ تمّ استخدام المتغيّر الجديد لإضافة خاصية إلى هذا العنصر، تتمّ إضافة الخاصية وقيمتها إلى العنصر الأصلي:
const myObject = {};
const myObjectReference = myObject;
myObjectReference.myProperty = true;
myObject;
> Object { myProperty: true }
وهذا مهم ليس فقط لتغيير العناصر، ولكن أيضًا لإجراء مقارنة
صارمة، لأنّ المساواة الصارمة بين العناصر تتطلّب من كلا المتغيّرين أن يشيرَا إلى العنصر نفسه لتقييمه على أنّه true
. ولا يمكنها الإشارة إلى
كائنات مختلفة، حتى إذا كانت هذه الكائنات متطابقة من الناحية الهيكلية:
const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};
myObject === myNewObject;
> false
myObject === myReferencedObject;
> true
تخصيص الذاكرة
تستخدم JavaScript إدارة الذاكرة التلقائية، ما يعني أنّه ليس من الضروري تخصيص الذاكرة أو إلغاء تخصيصها بشكل صريح أثناء عملية التطوير. على الرغم من أنّ تفاصيل طرق محرّكات JavaScript لإدارة الذاكرة تتجاوز نطاق هذه الوحدة، فإنّ فهم كيفية تخصيص الذاكرة يقدّم سياقًا مفعّلاً للعمل مع القيم المرجعية.
هناك "منطقتان" في الذاكرة: "الحزمة" و "الحِزمة العشوائية". تخزِّن الحزمة البيانات الثابتة، وهي القيم الأساسية والإشارات إلى الكائنات، لأنّه يمكن تخصيص المساحة الثابتة المطلوبة لتخزين هذه البيانات قبل تنفيذ النص البرمجي. تخزِّن الحِزمة الكائنات التي تحتاج إلى مساحة مخصّصة ديناميكيًا لأنّ حجمها يمكن أن يتغيّر أثناء التنفيذ. يتم تفريغ الذاكرة من خلال عملية تدعى "جمع المهملات"، وهي تزيل من الذاكرة العناصر التي لا تحتوي على أي إحالات.
سلسلة التعليمات الرئيسية
JavaScript هي لغة برمجة أحادية السلسلة بشكل أساسي مع نموذج تنفيذ "متزامن"، ما يعني أنّه يمكنها تنفيذ مهمة واحدة فقط في كل مرة. يُعرف سياق التنفيذ التسلسلي هذا باسم سلسلة التعليمات الرئيسية.
تتم مشاركة سلسلة المهام الرئيسية مع مهام المتصفّح الأخرى، مثل تحليل HTML، وعرض أجزاء من الصفحة وإعادة عرضها، وتشغيل الرسوم المتحرّكة في CSS، ومعالجة تفاعلات المستخدمين التي تتراوح بين التفاعلات البسيطة (مثل تمييز النص) والتفاعلات المعقّدة (مثل التفاعل مع عناصر النموذج). عثر مورّدو المتصفّحات على طُرق لتحسين المهام التي يؤديها الخيط الرئيسي، ولكنّه لا يزال بإمكان النصوص البرمجية الأكثر تعقيدًا استخدام الكثير من موارد الخيط الرئيسي والتأثير في ملفه الشخصي أداء الصفحة.
يمكن تنفيذ بعض المهام في خيوط معالجة في الخلفية تُعرف باسم Web Workers، مع بعض القيود:
- لا يمكن لخيوط العمل المعالجة إلا ملفات JavaScript المستقلة.
- لا يمكنهم الوصول إلى نافذة المتصفح وواجهة المستخدم أو يمكنهم الوصول إليهما بشكل محدود للغاية.
- تكون إمكانية التواصل مع سلسلة المحادثات الرئيسية محدودة.
تجعلها هذه القيود مثالية للمهام المركزة التي تتطلّب الكثير من الموارد والتي قد تشغل السلسلة الرئيسية بخلاف ذلك.
حزمة التنفيذ
إنّ بنية البيانات المستخدَمة لإدارة "سياقات التنفيذ"، أي التعليمات البرمجية التي يتم تنفيذها بشكل نشط، هي قائمة تُسمى تسلسل استدعاء الدوال البرمجية (يُشار إليها غالبًا باسم "التسلسل"). عند تنفيذ نص برمجي لأول مرة، ينشئ مفسِّر JavaScript "سياق تنفيذ شامل" ويدفعه إلى تسلسل استدعاء الدوال البرمجية، مع تنفيذ الجمل داخل هذا السياق الشامل واحدة تلو الأخرى، من الأعلى إلى الأسفل. عندما يصادف المُفسِّر استدعاء دالة أثناء تنفيذ السياق الشامل، يدفع "سياق تنفيذ الدالة" لهذا الاستدعاء إلى أعلى الحزمة، ويوقف السياق الشامل للتنفيذ مؤقتًا، وينفِّذ سياق تنفيذ الدالة.
في كل مرة يتم فيها استدعاء دالة، يتم دفع سياق تنفيذ الدالة لهذا الاستدعاء إلى أعلى الحزمة، فوق سياق التنفيذ الحالي مباشرةً. تعمل حزمة المكالمات على أساس "آخر عنصر يدخل هو أول عنصر يخرج"، ما يعني أنّه يتم تنفيذ دعوة الدالة الأخيرة، وهي أعلى عنصر في الحزمة، وتستمر إلى أن يتم حلّها. عند اكتمال هذه الدالة، يزيل المُفسِّر هذه الدالة من تسلسل استدعاء الدوال، ويصبح سياق التنفيذ الذي يحتوي على طلب استدعاء الدالة هو العنصر الأعلى في التسلسل مرة أخرى ويستأنف التنفيذ.
تلتقط سياقات التنفيذ هذه أيّ قيم ضرورية لتنفيذها. وتعمل هذه الدوال
أيضًا على إنشاء المتغيّرات والدوالّ المتاحة في نطاق الدالة
استنادًا إلى السياق الرئيسي لها، وتحديد قيمة العبارة
this
الرئيسية وضبطها في سياق الدالة.
حلقة الأحداث وقائمة الانتظار لطلبات الاستدعاء
ويعني هذا التنفيذ التسلسلي أنّ المهام غير المتزامنة التي تتضمّن وظائف callback
، مثل جلب البيانات من خادم أو الاستجابة لتفاعل المستخدم أو
الانتظار إلى أن يتم ضبط الموقّتات باستخدام setTimeout
أو setInterval
، ستؤدي إما إلى حظر
سلسلة التعليمات الرئيسية إلى أن تكتمل هذه المهمة، أو إلى إيقاف
سياق التنفيذ الحالي بشكل غير متوقّع في اللحظة التي تتم فيها إضافة سياق تنفيذ دالة callback
إلى الحزمة. لحلّ هذه المشكلة، تدير JavaScript المهام غير المتزامنة
باستخدام "نموذج التزامن" المستنِد إلى الأحداث والمكوّن من "حلقة الأحداث" و
"قائمة الانتظار للطلبات المُعاد تنفيذها" (يُشار إليها أحيانًا باسم "قائمة الرسائل").
عند تنفيذ مهمة غير متزامنة في سلسلة المهام الرئيسية، يتم وضع سياق تنفيذ دالّة callback في قائمة انتظار callback، وليس في أعلى ملف برمجي معالجة طلبات برمجية. حلقة الأحداث هي نمط يُسمى أحيانًا المفاعل، والذي يراجع باستمرار حالة تسلسل استدعاء الدوال البرمجية وقائمة الانتظار لطلبات الاستدعاء. إذا كانت هناك مهام في قائمة انتظار عمليات الردّ وتبيّن لحلقة الأحداث أنّ تسلسل استدعاء الدوالّ فارغ، يتم دفع المهام من قائمة انتظار عمليات الردّ إلى التسلسل واحدًا تلو الآخر لكي تتم تنفيذها.