كيف يعمل JS: أشجار الجملة المجردة ، التحليل وتحسينه

[نصح القراءة] الأجزاء الـ 19 الأخرى من الدورة
الجزء 1: نظرة عامة على المحرك وآليات وقت التشغيل ومكدس المكالمة
الجزء 2: حول V8 الداخلية وتحسين التعليمات البرمجية
الجزء الثالث: إدارة الذاكرة وأربعة أنواع من تسرب الذاكرة والتعامل معها
الجزء 4: حلقة الأحداث ، عدم التزامن ، وخمس طرق لتحسين التعليمات البرمجية الخاصة بك مع عدم التزامن / انتظار
الجزء 5: WebSocket و HTTP / 2 + SSE. ماذا تختار؟
الجزء 6: ميزات ومجال WebAssembly
الجزء 7: عمال الويب وخمسة سيناريوهات الاستخدام
الجزء الثامن: عمال الخدمة
الجزء 9: دفع الإخطارات على شبكة الإنترنت
الجزء 10: تتبع التغييرات في DOM باستخدام MutationObserver
الجزء 11: محركات تقديم صفحات الويب ونصائح لتحسين أدائها
الجزء 12: نظام الشبكة الفرعي للمتصفحات ، مما يحسن من أدائها وأمانها
الجزء 12: نظام الشبكة الفرعي للمتصفحات ، مما يحسن من أدائها وأمانها
الجزء 13: الرسوم المتحركة مع CSS وجافا سكريبت
الجزء 14: كيف يعمل JS: أشجار الجملة المجردة ، التحليل والتحسين
الجزء 15: كيف يعمل JS: الطبقات والميراث ، transpilation في بابل و TypeScript
الجزء 16: كيف يعمل JS: التخزين
الجزء 17: كيف يعمل JS: تقنية Shadow DOM ومكونات الويب
الجزء 18: كيف يعمل JS: آليات الاتصال WebRTC و P2P
الجزء 19: كيف يعمل JS: العناصر المخصصة

نعلم جميعًا أن شفرة جافا سكريبت لمشاريع الويب يمكن أن تنمو إلى حجم كبير. وكلما كان الرمز أكبر ، كلما قام المتصفح بتحميله لفترة أطول. لكن المشكلة هنا ليست فقط في وقت نقل البيانات عبر الشبكة. بعد تحميل البرنامج ، لا يزال يلزم تحليله وتجميعه في رمز ثانوي وتنفيذه في النهاية. نلفت انتباهكم اليوم إلى ترجمة الجزء 14 من سلسلة النظام البيئي لجافا سكريبت. وبالتحديد ، سنتحدث عن تحليل JS-code ، وكيف يتم بناء أشجار بناء مجردة ، وكيف يمكن للمبرمج التأثير على هذه العمليات ، وتحقيق زيادة في سرعة تطبيقاتهم.

الصورة

كيف هي لغات البرمجة


قبل الحديث عن أشجار النحو المجردة ، دعنا نتناول كيفية عمل لغات البرمجة. بغض النظر عن اللغة التي تستخدمها ، يجب عليك دائمًا استخدام برامج معينة تأخذ شفرة المصدر وتحويلها إلى شيء يحتوي على أوامر محددة للأجهزة. يعمل إما المترجمون الشفويون أو المترجمون كمثل هذه البرامج. لا يهم ما إذا كنت تكتب بلغة مفسرة (JavaScript أو Python أو Ruby) أو مترجمة (C # أو Java أو Rust) ، فإن شفرتك ، وهي عبارة عن نص عادي ، ستمر دائمًا بمرحلة التحليل ، أي تحويل النص العادي إلى بنية بيانات تسمى شجرة البناء المجردة (AST).

لا تقدم أشجار الجملة المجردة تمثيلًا منظمًا لشفرة المصدر فحسب ، بل تلعب أيضًا دورًا حاسمًا في التحليل الدلالي ، حيث يتحقق المترجم من صحة بنيات البرامج والاستخدام الصحيح لعناصرها. بعد تشكيل AST وإجراء الفحوصات ، يتم استخدام هذا الهيكل لتوليد كود ثانوي أو كود الآلة.

باستخدام أشجار النحو المجردة


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

افترض أنك تريد تطوير أداة تجد بنيات تحدث بشكل متكرر في التعليمات البرمجية الخاصة بك. سوف تساعد تقارير هذه الأداة في إعادة الهيكلة وستقلل من تكرار التعليمات البرمجية. يمكن القيام بذلك باستخدام مقارنة السلسلة المعتادة ، ولكن هذا النهج سيكون بدائيًا جدًا ، وستكون قدراته محدودة. في الواقع ، إذا كنت تريد إنشاء أداة مماثلة ، فلن تحتاج إلى كتابة المحلل اللغوي الخاص بك لـ JavaScript. هناك العديد من التطبيقات مفتوحة المصدر لهذه البرامج والتي تتوافق تمامًا مع مواصفات ECMAScript. على سبيل المثال - Esprima و Acorn. هناك أيضًا أدوات يمكن أن تساعد في العمل مع ما يولده المحللون ، أي العمل مع أشجار النحو المجردة.

بالإضافة إلى ذلك ، تستخدم أشجار النحو المجردة على نطاق واسع في تطوير النواقل. لنفترض أنك قررت تطوير ناقل يعمل على تحويل كود Python إلى رمز JavaScript. يمكن أن يعتمد مشروع مماثل على فكرة استخدام ناقل للإنشاء لإنشاء شجرة بناء جملة مجردة استنادًا إلى رمز Python ، والذي بدوره يتم تحويله إلى رمز JavaScript. ربما ستتساءل هنا كيف يمكن ذلك. الشيء هو أن أشجار النحو المجردة ليست سوى طريقة بديلة لتمثيل التعليمات البرمجية في بعض لغات البرمجة. قبل تحويل الرمز إلى AST ، يبدو النص العادي ، عند كتابته والذي يتبع قواعد معينة تشكل اللغة. بعد التحليل ، يتحول هذا الرمز إلى هيكل شجرة يحتوي على نفس المعلومات مثل التعليمات البرمجية المصدر للبرنامج. ونتيجة لذلك ، من الممكن تنفيذ ليس فقط الانتقال من التعليمات البرمجية المصدر إلى AST ، ولكن أيضًا التحويل العكسي ، وتحويل شجرة البنية المجردة إلى تمثيل نصي لرمز البرنامج.

تحليل جافا سكريبت


دعونا نتحدث عن كيفية بناء أشجار بناء مجردة. كمثال ، ضع في اعتبارك وظيفة JavaScript بسيطة:

function foo(x) {    if (x > 10) {        var a = 2;        return a * x;    }    return x + 10; } 

سيقوم المحلل بإنشاء شجرة بناء مجردة ، والتي يتم تمثيلها بشكل تخطيطي في الشكل التالي.


شجرة تركيبية مجردة

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

ربما سيكون لديك هنا سؤال حول سبب حاجة المبرمج لمعرفة كيفية عمل محلل JS. في النهاية ، يعد تحليل التعليمات البرمجية وتنفيذها مهمة مستعرض. بطريقة ما ، أنت على حق. يوضح الشكل أدناه الوقت المطلوب لبعض مشاريع الويب المعروفة لتنفيذ خطوات مختلفة في عملية تنفيذ كود JS.

ألق نظرة عن قرب على هذا الرسم ، ربما سترى شيئًا مثيرًا للاهتمام هناك.


الوقت المستغرق في تنفيذ كود JS

ترى؟ إذا لم يكن كذلك ، انظر مرة أخرى. في الواقع ، نحن نتحدث عن حقيقة أن المتصفحات ، في المتوسط ​​، تقضي 15-20 ٪ من الوقت في تحليل شفرة JS. وهذه ليست بعض البيانات الشرطية. فيما يلي معلومات إحصائية عن عمل مشاريع الويب الحقيقية التي تستخدم جافا سكريبت بطريقة أو بأخرى. ربما لا يبدو الرقم 15٪ كبيرًا جدًا بالنسبة لك ، ولكن صدقوني ، هذا كثير. يقوم تطبيق نموذجي من صفحة واحدة بتحميل 0.4 ميجا بايت تقريبًا من شفرة جافا سكريبت ، ويحتاج المتصفح إلى حوالي 370 مللي ثانية لتحليل هذا الرمز. مرة أخرى ، يمكنك القول أنه لا يوجد شيء يدعو للقلق. ونعم ، هذا وحده ليس كثيرًا. ومع ذلك ، لا تنس أن هذا هو الوقت الذي يستغرقه تحليل الشفرة وتحويلها إلى AST. هذا لا يشمل الوقت الذي يستغرقه تنفيذ التعليمات البرمجية ، أو الوقت الذي يستغرقه لحل المهام الأخرى المصاحبة لتحميل الصفحة ، على سبيل المثال ، مهام معالجة HTML و CSS وعرض الصفحة . علاوة على ذلك ، نحن نتحدث فقط عن متصفحات سطح المكتب. في حالة الأنظمة المحمولة لا يزال أسوأ. على وجه الخصوص ، يمكن أن يكون وقت التحليل لنفس الرمز على الأجهزة المحمولة أطول من 2-5 مرات على سطح المكتب. نلقي نظرة على الشكل التالي.


وقت التحليل 1 ميغابايت JS-code على مختلف الأجهزة

هذا هو الوقت المطلوب لتحليل 1 ميغابايت من كود JS على مختلف الأجهزة المحمولة وأجهزة سطح المكتب.

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


تحليل الموقع باستخدام أدوات المطورين في المتصفح

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

تحليل أمثل ومحركات شبيبة


تقوم محركات JS بالكثير من الأشياء المفيدة لتجنب العمل غير الضروري وتحسين عمليات معالجة التعليمات البرمجية. فيما يلي بعض الأمثلة.

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

عادة ما يتم تجميع شفرة جافا سكريبت في رمز البايت في كل مرة تتم فيها زيارة الصفحة. ومع ذلك ، يتم فقدان هذا الرمز الفرعي بعد انتقال المستخدم إلى صفحة أخرى. هذا يرجع إلى حقيقة أن التعليمات البرمجية المترجمة تعتمد بشكل كبير على حالة وسياق النظام في وقت الترجمة. لتحسين الوضع ، قدم Chrome 42 دعمًا للتخزين المؤقت للبايت كود. بفضل هذا الابتكار ، يتم تخزين الشفرة المترجمة محليًا ، ونتيجة لذلك ، عندما يعود المستخدم إلى الصفحة التي تمت زيارتها بالفعل ، ليست هناك حاجة لتنزيل النصوص البرمجية وتحليلها وتجميعها لإعدادها للعمل. وهذا يوفر Chrome حوالي 40٪ من وقت التحليل والتحليل. بالإضافة إلى ذلك ، في حالة الأجهزة المحمولة ، يؤدي ذلك إلى توفير طاقة البطارية.

يمكن لمحرك Carakan ، الذي تم استخدامه في متصفح Opera وتم استبداله بـ V8 لفترة طويلة ، إعادة استخدام نتائج التجميع للنصوص المجهزة بالفعل. لم يكن مطلوبًا أن تكون هذه البرامج النصية متصلة بنفس الصفحة أو حتى يتم تحميلها من نفس المجال. إن تقنية التخزين المؤقت هذه ، في الواقع ، فعالة للغاية وتسمح لك بالتخلي تمامًا عن خطوة التجميع. تعتمد على سيناريوهات سلوك المستخدم النموذجية ، على كيفية عمل الناس مع موارد الويب. على وجه التحديد ، عندما يتبع المستخدم سلسلة معينة من الإجراءات ، أثناء العمل مع تطبيق ويب ، يتم تحميل نفس الرمز.

لا يقوم مترجم SpiderMonkey المستخدم من قبل FireFox بتخزين كل شيء على التوالي. يدعم نظام المراقبة الذي يحسب عدد المكالمات إلى نص برمجي معين. بناءً على هذه المؤشرات ، يتم تحديد أقسام الكود التي تحتاج إلى التحسين ، أي تلك التي تحتوي على الحد الأقصى للحمل.

بالطبع ، قد يقرر بعض مطوري المستعرضات أن منتجاتهم لا تحتاج إلى التخزين المؤقت على الإطلاق. لذلك ، يقول Masei Stachovyak ، وهو مطور رائد لمتصفح Safari ، أن Safari لا يشارك في التخزين المؤقت الذي تم تجميعه بواسطة البايت كود. تم النظر في إمكانية التخزين المؤقت ، ولكن لم يتم تنفيذه بعد ، حيث يستغرق إنشاء التعليمات البرمجية أقل من 2٪ من إجمالي وقت تنفيذ البرنامج.

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

تقليل وقت إعداد تطبيق الويب


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

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

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

في الواقع ، المفهوم ليس جديدا. حتى المتصفحات القديمة مثل IE9 تدعم أساليب التحسين هذه ، على الرغم من أن الأنظمة الحديثة قد تقدمت كثيرًا بالطبع.

دعونا نفحص مثالاً يوضح عمل هذه الآليات. لنفترض أن لدينا كود JS التالي:

 function foo() {   function bar(x) {       return x + 10;   }   function baz(x, y) {       return x + y;   }   console.log(baz(100, 200)); } 

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

  • إعلان وظيفة bar تأخذ وسيطة واحدة ( x ). هذه الوظيفة لها أمر عودة واحد ، فهي ترجع نتيجة إضافة x و 10.
  • إعلان دالة baz تأخذ وسيطتين ( x و y ). لديها أيضا أمر عودة واحد ، ترجع نتيجة إضافة x و y .
  • إجراء مكالمة مع الدالة baz باستخدام وسيطتين - 100 و 200.
  • إجراء مكالمة مع الدالة console.log باستخدام وسيطة واحدة ، وهي القيمة التي يتم إرجاعها بواسطة الوظيفة التي تم استدعاؤها سابقًا.

إليك كيف تبدو.


نتيجة تحليل نموذج التعليمات البرمجية دون تطبيق التحسين

لنتحدث عما يحدث هنا. يرى المحلل تعريف وظيفة bar ، وإعلان وظيفة baz ، واستدعاء وظيفة baz ، واستدعاء وظيفة console.log . من الواضح ، عند تحليل هذا الجزء من التعليمات البرمجية ، سيواجه المحلل اللغوي مهمة لن يؤثر تنفيذها على نتائج هذا البرنامج. حول تحليل bar الوظائف. لماذا تحليل هذه الوظيفة غير عملي؟ الشيء هو أن وظيفة bar ، على الأقل في جزء الرمز المعروض ، لم يتم استدعاؤها أبدًا. قد يبدو هذا المثال البسيط بعيد المنال ، ولكن العديد من التطبيقات الحقيقية لها عدد كبير من الوظائف التي لا يتم استدعاؤها مطلقًا.

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

ونتيجة لذلك ، في المثال السابق ، سيشكل المحلل الحقيقي هيكلًا يشبه المخطط التالي.


نتيجة تحليل كود المثال مع التحسين

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

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

هنا ، على سبيل المثال ، نمط شائع جدًا لتطبيق الوحدات النمطية في JavaScript:

 var myModule = (function() {   //      //    })(); 

يتعرف معظم موزعي JS الحديثين على هذا النمط ؛ فبالنسبة لهم هو إشارة إلى أن الكود الموجود داخل الوحدة يحتاج إلى تحليل كامل.

ولكن ماذا لو استخدم المحللون دائمًا التحليل البطيء؟ هذه ، للأسف ، ليست فكرة جيدة. والحقيقة هي أنه مع هذا النهج ، إذا كان هناك حاجة إلى تنفيذ بعض التعليمات البرمجية في أقرب وقت ممكن ، فسوف نواجه تباطؤًا في النظام. سيقوم المحلل بتمرير واحد للتحليل الكسول ، وبعد ذلك سيبدأ على الفور في تحليل كامل لما يجب القيام به في أقرب وقت ممكن. سيؤدي هذا إلى تباطؤ بنسبة 50٪ تقريبًا مقارنة بالنهج عندما يبدأ المحلل اللغوي على الفور في تحليل الرمز الأكثر أهمية بشكل كامل.

تحسين الشفرة ، مع مراعاة ميزات تحليلها


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

لنفترض أن لدينا وظيفة foo :

 function foo(x) {   return x * 10; } 

نظرًا لعدم وجود إشارة صريحة في جزء التعليمات البرمجية هذا إلى أنه من المقرر تنفيذ هذه الوظيفة على الفور ، فإن المتصفح لن يؤدي إلا إلى تحليله البطيء. ومع ذلك ، نحن واثقون من أننا سنحتاج إلى هذه الوظيفة قريبًا جدًا ، حتى نتمكن من اللجوء إلى الحيلة التالية.

أولاً ، احفظ الوظيفة في متغير:

 var foo = function foo(x) {   return x * 10; }; 

يرجى ملاحظة أننا تركنا اسم الوظيفة الأولي بين الكلمة الرئيسية function وقوس الفتح. لا يمكن القول أن هذا ضروري للغاية ، ولكن يوصى بالقيام بذلك بالضبط ، لأنه إذا تم طرح استثناء أثناء تشغيل الوظيفة ، يمكنك رؤية اسم الوظيفة في بيانات تتبع المكدس ، وليس <anonymous> .

بعد التغيير أعلاه ، سيستمر المحلل اللغوي في استخدام التحليل البطيء. من أجل تغيير هذا ، يكفي تفصيل صغير واحد. يجب وضع الوظيفة بين قوسين:

 var foo = (function foo(x) {   return x * 10; }); 

الآن ، عندما يعثر المحلل اللغوي على قوس فتح أمام الكلمة الأساسية function ، سيبدأ على الفور في تحليل هذه الوظيفة.

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

بالتأكيد ، لن يرغب المبرمجون في تحمل كل هذا العمل الإضافي. إلى جانب ذلك ، والذي لا يقل أهمية عن كل ما قيل بالفعل ، سيكون من الصعب قراءة وفهم الشفرة التي تمت معالجتها بهذه الطريقة. في هذه الحالة ، تكون حزم البرامج الخاصة مثل Optimize.js جاهزة لمساعدتنا. هدفهم الرئيسي هو تحسين وقت التمهيد الأولي لشفرة مصدر JS. يقومون بتحليل الكود الثابت وتعديله بحيث يتم وضع الوظائف التي يجب تنفيذها في أقرب وقت ممكن بين قوسين ، مما يؤدي إلى حقيقة أن المتصفح يوزعها على الفور ويجهزها للتنفيذ.

لذا ، لنفترض أننا نبرمج ، دون التفكير حقًا في أي شيء ، ولدينا جزء التعليمات البرمجية التالي:

 (function() {   console.log('Hello, World!'); })(); 

يبدو طبيعيًا تمامًا ، ويعمل كما هو متوقع ، ويتم تنفيذه بسرعة ، حيث يجد المحلل اللغوي قوس الفتح أمام الكلمة الأساسية function . جيد حتى الآن. , , , :

 !function(){console.log('Hello, World!')}(); 

, , . , - .

, , . , , , . , , , . , , . Optimize.js. Optimize.js, :

 !(function(){console.log('Hello, World!')})(); 

, . , . , , , — .


, JS- — , . ? , , , , . , , , , JS- , . , , , -, . - . , , . , , , , . , JS- , , V8 , , . .


, -:

  • . .
  • , .
  • , , , JS-. , , .
  • DeviceTiming , .
  • Optimize.js , , .

الملخص


, , SessionStack , , -, . , . — . , — , -, , , .

أعزائي القراء! - JavaScript-?

Source: https://habr.com/ru/post/ar415269/


All Articles