الملحق

توريث النموذج الأولي

باستثناء 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) لمحاولة تصحيح مثيلات الأجزاء المنقوطة التي تم حذفها. إذا واجه محلل 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

حسب المرجع، حسب القيمة

يمكن أن يحتوي أي متغيّر، بما في ذلك خصائص الكائن ومعلَمات الدالة والعناصر في صفيف أو مجموعة أو خريطة، على قيمة أولية أو على قيمة مرجعية.

عند تعيين قيمة أولية من متغير إلى آخر، ينشئ محرك جافا سكريبت نسخة من هذه القيمة ويعينها للمتغير.

عند تعيين كائن (مثيلات الفئة والمصفوفات والدوال) لمتغير، فبدلاً من إنشاء نسخة جديدة من هذا الكائن، يحتوي المتغير على مرجع لموقف الكائن المخزن في الذاكرة. لهذا السبب، يؤدي تغيير كائن مُشار إليه عن متغير إلى تغيير الكائن المشار إليه، وليس مجرد قيمة يحتوي عليها هذا المتغير. على سبيل المثال، إذا قمت بتهيئة متغير جديد باستخدام متغير يحتوي على مرجع كائن، فقم باستخدام المتغير الجديد لإضافة خاصية إلى هذا الكائن، تتم إضافة الخاصية وقيمتها إلى الكائن الأصلي:

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 وتضبطها في سياق الدالة.

حلقة الحدث وقائمة انتظار معاودة الاتصال

يعني هذا التنفيذ التسلسلي أنّ المهام غير المتزامنة التي تتضمّن دوال معاودة الاتصال، مثل استرجاع البيانات من الخادم أو الاستجابة لتفاعل المستخدم أو انتظار الموقّتات التي يتم ضبطها باستخدام setTimeout أو setInterval، قد تحظر سلسلة التعليمات الرئيسية حتى تكتمل هذه المهمة، أو تقاطع سياق التنفيذ الحالي بشكل غير متوقع في اللحظة التي تتم فيها إضافة سياق تنفيذ دالة الاستدعاء إلى الحزمة. لمعالجة هذه المشكلة، تُدير لغة JavaScript المهام غير المتزامنة باستخدام "نموذج تزامن" يستند إلى الحدث والذي يتألف من "حلقة الأحداث" و"قائمة انتظار معاودة الاتصال" (يُشار إليها أحيانًا باسم "قائمة انتظار الرسائل").

عند تنفيذ مهمة غير متزامنة في سلسلة التعليمات الرئيسية، يتم وضع سياق تنفيذ دالة معاودة الاتصال في قائمة انتظار رد الاتصال، وليس أعلى حزمة الاستدعاءات. حلقة الأحداث هي نمط يُطلق عليه أحيانًا مفاعل، ومن ثم يستعلم باستمرار عن حالة حزم المكالمات وقائمة انتظار معاودة الاتصال. إذا كانت هناك مهام في قائمة انتظار معاودة الاتصال وحددت حلقة الأحداث أن تكديس الاستدعاءات فارغ، يتم دفع المهام من قائمة انتظار معاودة الاتصال إلى المكدس واحدًا تلو الآخر ليتم تنفيذها.