في جافا سكريبت، يجب علينا استخدام المتغيرات المحلية بدلاً من المتغيرات العامة قدر الإمكان، الجميع يعرف هذه الجملة، ولكن من قالها أولاً؟ لماذا تفعل هذا؟ هل هناك أي أساس لهذا؟ إذا لم تقم بذلك، ما مقدار الخسارة التي ستسببها في الأداء؟ سوف تستكشف هذه المقالة إجابات هذه الأسئلة وستفهم بشكل أساسي العوامل المرتبطة بأداء القراءة والكتابة للمتغيرات.
【الأصل】أداء جافا سكريبت المتغير
【المؤلف】نيكولاس سي. زاكاس
[ترجمة] في جافا سكريبت، لماذا يجب علينا استخدام المتغيرات المحلية كلما أمكن ذلك؟
[مترجم] Mingda
ما يلي هو ترجمة للنص الأصلي:
فيما يتعلق بكيفية تحسين أداء JavaScript، فإن الاقتراح الأكثر شيوعًا هو استخدام المتغيرات المحلية بدلاً من المتغيرات العامة. هذه هي النصيحة التي ظلت عالقة في ذهني ولم أشكك فيها أبدًا طوال السنوات التسع التي قضيتها في العمل في تطوير الويب، وهي تعتمد على طريقة معالجة JavaScript لتحديد النطاق وحل المعرف (تحليل المعرف).
بداية، علينا أن نوضح أن الوظائف متجسدة ككائنات في JavaScript. إن عملية إنشاء دالة هي في الواقع عملية إنشاء كائن. يحتوي كل كائن دالة على خاصية داخلية تسمى [[Scope]]، والتي تحتوي على معلومات النطاق عند إنشاء الوظيفة. في الواقع، تتوافق سمة [[النطاق]] مع قائمة الكائنات (الكائنات المتغيرة)، ويمكن الوصول إلى الكائنات الموجودة في القائمة من داخل الوظيفة. على سبيل المثال، إذا أنشأنا دالة عامة A، فإن الخاصية الداخلية [[Scope]] لـ A تحتوي على كائن عام واحد فقط (Global Object)، وإذا أنشأنا دالة جديدة B في A، فإن سمة [[Scope] ] لـ B تحتوي على كائنين، كائن كائن التنشيط للوظيفة A موجود في المقدمة، والكائن العام (الكائن العمومي) موجود في الخلف.
عند تنفيذ وظيفة، يتم إنشاء كائن قابل للتنفيذ (كائن التنفيذ) تلقائيًا وربطه بسلسلة النطاق (سلسلة النطاق). سيتم إنشاء سلسلة النطاق من خلال الخطوتين التاليتين لتحليل المعرف.
1. أولاً، انسخ الكائنات الموجودة في الخصائص الداخلية لكائن الوظيفة [[النطاق]] إلى سلسلة النطاق بالترتيب.
2. ثانيًا، عند تنفيذ الوظيفة، سيتم إنشاء كائن كائن التنشيط الجديد. يحتوي هذا الكائن على تعريفات هذا، والمعلمات (الوسائط)، والمتغيرات المحلية (بما في ذلك المعلمات المسماة) التي سيتم وضعها موضع التنفيذ الجزء الأمامي من سلسلة المجال.
أثناء تنفيذ كود JavaScript، عند مواجهة معرف، سيتم البحث عنه في سلسلة النطاق لسياق التنفيذ (سياق التنفيذ) بناءً على اسم المعرف. بدءًا من الكائن الأول في سلسلة النطاق (كائن التنشيط الخاص بالوظيفة)، إذا لم يتم العثور عليه، فابحث عن الكائن التالي في سلسلة النطاق، وهكذا حتى يتم العثور على تعريف المعرف. إذا لم يتم العثور على الكائن الأخير في النطاق، وهو الكائن العام، بعد البحث، فسيتم طرح خطأ، مما يدفع المستخدم إلى أن المتغير غير محدد. هذه هي عملية نموذج تنفيذ الوظيفة ودقة المعرف (تحليل المعرف) الموضحة في معيار ECMA-262. وتبين أن معظم محركات JavaScript يتم تنفيذها بالفعل بهذه الطريقة. تجدر الإشارة إلى أن ECMA-262 لا يفرض استخدام هذا الهيكل، ولكنه يصف فقط هذا الجزء من الوظيفة.
بعد فهم عملية تحليل المعرف (تحليل المعرف)، يمكننا أن نفهم لماذا يتم حل المتغيرات المحلية بشكل أسرع من المتغيرات في النطاقات الأخرى، ويرجع ذلك أساسًا إلى اختصار عملية البحث إلى حد كبير. ولكن كم سيكون أسرع؟ للإجابة على هذا السؤال، قمت بمحاكاة سلسلة من الاختبارات لاختبار أداء المتغيرات في أعماق نطاق مختلفة.
الاختبار الأول هو كتابة أبسط قيمة لمتغير (يتم استخدام القيمة الحرفية 1 هنا)، وتظهر النتيجة في الشكل أدناه، وهو أمر مثير للاهتمام للغاية:
ليس من الصعب أن نرى من النتائج أنه عندما تتطلب عملية تحليل المعرف بحثًا عميقًا، سيكون هناك فقدان في الأداء، وستزداد درجة فقدان الأداء مع زيادة عمق المعرف. ليس من المستغرب أن يكون أداء Internet Explorer هو الأسوأ (ولكن لكي نكون منصفين، كانت هناك بعض التحسينات في IE 8). تجدر الإشارة إلى أن هناك بعض الاستثناءات هنا، حيث يتمتع Google Chrome وأحدث إصدار منتصف الليل من WebKit بوقت وصول مستقر للغاية إلى المتغيرات ولا يزيد مع زيادة عمق النطاق. بالطبع، يجب أن يُعزى هذا إلى الجيل التالي من محركات JavaScript التي يستخدمونها، V8 وSquirrelFish. تقوم هذه المحركات بإجراء تحسينات عند تنفيذ التعليمات البرمجية، ومن الواضح أن هذه التحسينات تجعل الوصول إلى المتغيرات أسرع من أي وقت مضى. كان أداء Opera جيدًا أيضًا، حيث كان أسرع بكثير من IE وFirefox والإصدار الحالي من Safari، ولكنه أبطأ من المتصفحات المعتمدة على V8 وSquirrelfish. أداء Firefox 3.1 Beta 2 غير متوقع بعض الشيء. كفاءة تنفيذ المتغيرات المحلية عالية جدًا، ولكن مع زيادة عدد طبقات النطاق، تنخفض الكفاءة بشكل كبير. تجدر الإشارة إلى أنني أستخدم الإعدادات الافتراضية هنا، مما يعني أن وظيفة التتبع لم يتم تشغيلها في Firefox.
تم الحصول على النتائج المذكورة أعلاه من خلال إجراء عمليات الكتابة على المتغيرات. في الواقع، كنت أشعر بالفضول لمعرفة ما إذا كان الوضع سيكون مختلفًا عند قراءة المتغيرات، لذلك قمت بإجراء الاختبار التالي. لقد وجد أن سرعة القراءة أسرع قليلاً من سرعة الكتابة، لكن اتجاه تغيرات الأداء ثابت.
كما هو الحال في الاختبار السابق، كان Internet Explorer وFirefox لا يزالان الأبطأ، وأظهر Opera أداءً ملفتًا للنظر للغاية، وبالمثل، أظهر Chrome والإصدار الأحدث من Webkit Midnight Edition اتجاهات الأداء التي لا علاقة لها بعمق النطاق أيضًا يستحق الاهتمام به. نعم، لا يزال وقت الوصول المتغير في Firefox 3.1 Beta 2 يتمتع بقفزة غريبة مع العمق.
أثناء الاختبار، اكتشفت ظاهرة مثيرة للاهتمام، وهي أن Chrome سيواجه خسائر إضافية في الأداء عند الوصول إلى المتغيرات العامة. لا علاقة لوقت الوصول إلى المتغيرات العامة بمستوى النطاق، ولكنه سيكون أطول بنسبة 50% من وقت الوصول إلى المتغيرات المحلية من نفس المستوى.
ما هي الاستنارات التي يمكن أن يجلبها لنا هذان الاختباران؟ الأول هو التحقق من وجهة النظر القديمة، وهي استخدام المتغيرات المحلية قدر الإمكان. في جميع المتصفحات، يكون الوصول إلى المتغيرات المحلية أسرع من الوصول إلى المتغيرات عبر النطاقات، بما في ذلك المتغيرات العامة. يجب أن تكون النقاط التالية هي الخبرة المكتسبة من خلال هذا الاختبار:
* التحقق بعناية من جميع المتغيرات المستخدمة في الوظيفة. إذا كان هناك متغير غير محدد في النطاق الحالي ويتم استخدامه أكثر من مرة، فيجب علينا حفظ هذا المتغير في ملف المتغير المحلي، استخدم هذا المتغير المحلي لإجراء عمليات القراءة والكتابة. يمكن أن يساعدنا هذا في تقليل عمق البحث في المتغيرات خارج النطاق إلى 1. وهذا مهم بشكل خاص للمتغيرات العامة، لأن المتغيرات العامة يتم البحث عنها دائمًا في الموضع الأخير من سلسلة النطاق.
* تجنب استخدام العبارة with . لأنه سيعدل سلسلة النطاق لسياق التنفيذ (سياق التنفيذ) ويضيف كائنًا (كائن متغير) في المقدمة. هذا يعني أنه أثناء تنفيذ with، يتم نقل المتغيرات المحلية الفعلية إلى الموضع الثاني في سلسلة النطاق، مما سيؤدي إلى فقدان الأداء.
* إذا كنت متأكدًا من أن جزءًا من التعليمات البرمجية سيؤدي بالتأكيد إلى استثناء، فتجنب استخدام محاولة الالتقاط، لأن فرع الالتقاط تتم معالجته في نفس سلسلة النطاق كما هو الحال مع. ومع ذلك، لا يوجد أي فقدان للأداء في كود فرع المحاولة، لذلك لا يزال من المستحسن استخدام حاول الالتقاط لاكتشاف الأخطاء غير المتوقعة.
إذا كنت تريد المزيد من المناقشة حول هذا الموضوع، فقد ألقيت محاضرة قصيرة في Mountain View JavaScript Meetup الشهر الماضي. يمكنك تنزيل الشرائح على SlideShare، أو مشاهدة الفيديو الكامل للحفل، والذي يبدأ عند مرور 11 دقيقة تقريبًا على محادثتي.
ملاحظات المترجم:
إذا كان لديك أي شكوك أثناء قراءة هذا المقال، أقترح عليك قراءة المقالين التاليين:
* "نموذج تنفيذ نموذج كائن JavaScript" بقلم ريتشي
* "ECMA-262 الإصدار الثالث"، انظر بشكل أساسي إلى الفصل 10، وهو سياق التنفيذ، حيث تم شرح المصطلحات المذكورة في هذه المقالة بالتفصيل هناك.
في النهاية، ذكر نيكولاس أن موقع Mountain View JavaScript Meetup هو في الواقع موقع ويب خاص بالمنظمة لمختلف الأنشطة الواقعية، لذا فإن العيش في كاليفورنيا يعد بمثابة نعمة كبيرة الأنشطة للمشاركة فيها. الكالينجيون.