جافا سكريبت سؤال وجواب

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



السؤال رقم 1. نموذج الميراث


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

▍ الإجابة


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

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

var Grandfather = function () {}; //  Grandfather Grandfather.prototype.color = 'green'; var Father = function () {}; //  Father Father.prototype = new Grandfather (); //  - ,        var Son = function () {}; //  Son Son.prototype = new Father (); // Son   Father var u = new Grandfather (); //   Grandfather var f = new Father (); //   Father var s = new Son (); //   Son //     console.log ([u.color, f.color, s.color]); // ["green", "green", "green"] //  Grandfather        Grandfather.prototype.color = 'blue'; console.log ([u.color, f.color, s.color]); // ["blue", "blue", "blue"] //  Father      -   ,     Father.prototype.color = 'green'; //      : // Grandfather.prototype.color = 'green'; console.log ([u.color, f.color, s.color]); // ["blue", "green", "green"] // ,        Grandfather     color,      Grandfather.prototype.color = 'blue'; console.log ([u.color, f.color, s.color]); // ["blue", "green", "green"] //  Son,  ,       Grandfather,    s.color = 'black'; //        console.log ([u.color, f.color, s.color]); // ["blue", "green", "black"] var SonsSon = function () {}; //  SonsSon -   Son SonsSon.prototype = new Son (); //  var ss = new SonsSon (); //   SonsSon //         console.log ([u.color, f.color, s.color, ss.color]); // ["blue", "green", "black", "green"] 

السؤال رقم 2. إنشاء كائنات


إذا قمت بإنشاء مثيلات جديدة للكائنات باستخدام الكلمة الأساسية new ، فكيف يمكنك حماية نفسك من الأخطاء؟ هكذا أعمل عادة:

  1. أنا دائما بناء وظائف منشئ بحيث تبدأ بحرف كبير.
  2. أتحقق من صحة العملية باستخدام بناء this instanceof Function_Name (أحاول عدم استخدام بنية نوع this instanceof arguments.callee ).
  3. يشبه هذا الأسلوب النهج السابق ، ولكن تتم المقارنة مع window ، لأنني لا أرغب في ترميز أسماء الكيانات بشكل صارم ولست بحاجة إلى كتابة شفرة لبيئات أخرى غير المتصفح.

ما هو النموذج الأكثر ملاءمة لإنشاء الكائنات؟

▍ الإجابة


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

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

 var Obj = function () {   "use strict";   this.pew = 100; }; //  let o = new Obj(); o.pew++; console.log(o.pew); //101 // .   Obj (); // TypeError: this is undefined 

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

 //    . var Obj = function () {    if (! (this instanceof Obj)) {        return new Obj ();    }    this.pew = 100; }; 

إذا كنت تحتاج إلى مُنشئ ، حتى لإنشاء كائن صغير ، فمن الأفضل القيام بذلك:

 var Obj = function () {    "use strict";    this.pew = 100; }; 

هنا ، كما هو موضح أعلاه ، نظرًا لحقيقة أن المُنشئ يعمل في وضع صارم ، سيحدث خطأ عند الاتصال به بدون new .

السؤال رقم 3. اعتراض نقرات الماوس


كيف ، باستخدام جافا سكريبت ، لمعرفة أي زر الماوس يتم النقر؟

▍ الإجابة


يؤدي النقر فوق أزرار الماوس إلى إنشاء أحداث الماوس والماوس. في هذه الحالة ، يتم إنشاء حدث click فقط بواسطة زر الماوس الأيسر. في معالج الأحداث ، تحتاج إلى التحقق من الكود الموجود في خاصية event.button لمعرفة أي زر يتم الضغط عليه (0 - يسار ، 1 - وسط ، 2 - يمين). ومع ذلك ، في IE كل شيء يبدو مختلفا بعض الشيء. النظر في مثال:

 var button = document.getElementById ('button'),              // 0 1 2    buttonMap = ['Left', 'Middle', 'Right'],    handler = function (event) {        event = event || window.event;        alert (buttonMap [event.button] + 'id:' + event.button);    }; if (button.addEventListener) {     button.addEventListener ('mousedown', handler, false); } else {     // IE 0 1 2 3 4     buttonMap = ['???', 'Left', 'Right', '???', 'Middle'];     button.attachEvent ('onmousedown', handler); } 

تأخذ مكتبة jQuery هذه الميزة IE في الاعتبار ، لذلك عند استخدامها في أي مستعرض ، فقط تحقق من قيمة event.which بدلاً من messing مع event.button :

 $('button').mousedown(function (event) {   alert(['Left', 'Middle', 'Right'][event.which]); }); 

السؤال رقم 4. اعتراض ضربات المفاتيح على مفاتيح لوحة المفاتيح


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

▍ الإجابة


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

يمكن القيام بذلك بطرق مختلفة . على سبيل المثال ، مثل هذا:

 window.addEventListener("keydown", function(e) {   //  -   if([37, 38, 39, 40].indexOf(e.keyCode) > -1) {       e.preventDefault();   } }, false); 

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

 $('input').bind ('mousedown', function (event) {   event.preventDefault();   //    return false; }); 

عند النقر فوق حقل الإدخال ، تحدث الأحداث التالية - بالتسلسل الذي تظهر به هنا:

  1. mousedown
  2. focus (قبل ذلك ، سيؤدي كائن آخر يفقد التركيز إلى حدث blur )
  3. mouseup
  4. click

إذا حاولت منع العنصر من الحصول على تركيز المدخلات ، فلن يساعدنا استخدام معالجات الأحداث لهذا بدءًا من معالج أحداث focus .

السؤال رقم 5. وقف الرسوم المتحركة GIF ومفتاح ESC


كيفية التعامل مع مشكلة إيقاف الرسوم المتحركة GIF عند الضغط على مفتاح ESC؟

▍ الإجابة


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

السؤال رقم 6. أقواس في IIFE


كيف يتم استخدام القوسين المستخدمين عند الإعلان عن تعبير دالة الاستدعاء فورًا (IIFE)؟

▍ الإجابة


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

 function () { //  }() 

هذا يرجع إلى حقيقة أن الوظيفة لا تحتوي على اسم (يجب عليك تحديد أسمائها في إعلانات الوظيفة).

دعنا نحاول إعادة كتابة هذا الرمز ، مع إعطاء الوظيفة اسمًا:

 function foo() { //  }() 

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

غالبًا ما يتم تصميم IIFE على النحو التالي:

 (function () {   //  })() 

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

 !function () { //  }(); +function () { //  }(); [function() { //  }()]; var a = function () { //  }(); 

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

السؤال رقم 7. تمرير الكود استجابة للطلبات


يرسل الخادم ، أثناء عملية تفاعل AJAX مع العميل ، في نص الاستجابة ، إلى العميل سلسلة alert ('Boom !!!'); . يقبل العميل الاستجابة وينفذ هذا الرمز باستخدام دالة eval() . ما يسمى؟ بعد كل شيء ، ما هو موجود في استجابة الخادم ليس JSON ، وليس XML ، وليس HTML. ماذا يمكنك أن تقول عن تنفيذ العميل على الكود الذي يأتي من الخادم في شكل نص الاستجابة لطلب معين؟

▍ الإجابة


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

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

السؤال رقم 8. أداء العمليات الثقيلة في الموضوع الرئيسي


كيف يمكن تنظيم تنفيذ بعض الأوامر كثيفة الاستخدام للموارد في جافا سكريبت وليس "تعليق" البرنامج النصي بأكمله؟

▍ الإجابة


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

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

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

فيما يلي نموذج التعليمات البرمجية من الكتاب أعلاه:

 function timedProcessArray(items, process, callback) {   var todo = items.concat();   //    setTimeout(function () {       var start = +new Date();       do {           process(todo.shift());       } while (todo.length > 0 && (+new Date() - start < 50));       if (todo.length > 0){           setTimeout(arguments.callee, 25);       } else {           callback(items);       }   }, 25); } function saveDocument(id) {   var tasks = [openDocument, writeText, closeDocument, updateUI];   timedProcessArray(tasks, [id], function(){       alert("Save completed!");   }); } 

تقوم timedProcessArray() بحظر الخيط الرئيسي لمدة 25 مللي ثانية ، وتنفذ سلسلة من الإجراءات ، ثم تطلقها لمدة 25 مللي ثانية ، وبعد ذلك تتكرر هذه العملية.

السؤال رقم 9. حول تغيير حجم نافذة المتصفح


هل يمكنني بطريقة ما معرفة أن المستخدم قد انتهى من تغيير حجم نافذة المتصفح؟

▍ الإجابة


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

فيما يلي مسودة رمز لحل هذه المشكلة.

 var time = 0,   timerId,   TIME_ADMISSION = 100; // 0.1  function onresizeend () {   console.log('onresizeend'); }; function resizeWatcher () {   if (+new Date - time >= TIME_ADMISSION) {       onresizeend();       if (timerId) {           window.clearInterval(timerId);           timerId = null;       }   } }; $(window).resize(function () {   if (!timerId) {       timerId = window.setInterval(resizeWatcher, 25);   }   time = +new Date; }); 

السؤال رقم 10. فتح نوافذ وعلامات المتصفح الجديدة


كيف ، باستخدام طريقة window.open() ، لفتح نافذة متصفح جديدة ، وليس علامة تبويب جديدة؟

▍ الإجابة


تعتمد الطريقة التي تتصرف بها طريقة window.open() بالضبط على المستعرض. يفتح Opera دائمًا علامات تبويب جديدة (على الرغم من أنها تبدو وكأنها نوافذ) ، يفتح Safari دائمًا النوافذ (على الرغم من إمكانية تغيير هذا السلوك). يمكن التحكم في سلوك Chrome و Firefox و Internet Explorer.

لذلك ، إذا window.open() تمرير معلمة إضافية (موضع النافذة window.open() إلى طريقة window.open() ، سيتم فتح نافذة جديدة:

 window.open('http://www.google.com', '_blank', 'toolbar=0,location=0,menubar=0'); 

إذا تم تمرير رابط فقط إلى هذه الطريقة ، فسيتم فتح علامة تبويب متصفح جديدة:

 window.open('http://www.google.com'); 

غالبًا ما تحتاج إلى فتح علامة تبويب متصفح جديدة. قد تكون هناك مشاكل في هذا في متصفح Safari. افتراضيًا (يعتمد ذلك على الإعدادات) ، يفتح المستعرض ، عند window.open() ، نافذة جديدة. ولكن إذا قمت بالنقر فوق الارتباط ، أثناء الضغط على المفاتيح Ctrl + Shift/Meta + Shift ، سيتم فتح علامة تبويب جديدة (بغض النظر عن الإعدادات). في المثال التالي ، سنحاكي حدث click الذي يتم رفعه عند Ctrl + Shift/Meta + Shift على Ctrl + Shift/Meta + Shift :

 function safariOpenWindowInNewTab (href) {    var event = document.createEvent ('MouseEvents'),        mac = (navigator.userAgent.indexOf ('Macintosh')> = 0); //  Ctrl + Shift + LeftClick / Meta + Shift + LeftClick ()    //       event.initMouseEvent (        / * type * / "click",        / * canBubble * / true        / * cancelable * / true,        / * view * / window,        / * detail * / 0,        / * screenX, screenY, clientX, clientY * / 0, 0, 0, 0,        / * ctrlKey * /! mac,        / * altKey * / false,        / * shiftKey * / true        / * metaKey * / mac,        / * button * / 0,        / * relatedTarget * / null    ); //     ,   ,         $ ('<a/>', {'href': href, 'target': '_blank'}) [0] .dispatchEvent (event); } 

السؤال رقم 11. نسخ الكائنات العميقة


كيفية تنظيم فعال نسخ عميق من الأشياء؟

▍ الإجابة


إذا لم يتغير الكائن الذي تريد إنشاء نسخته (دعنا نسميها oldObject ) ، فسيكون ذلك أكثر فاعلية من خلال القيام بذلك من خلال النموذج الأولي (يتم ذلك بسرعة كبيرة):

 function object(o) {   function F() {}   F.prototype = o;   return new F(); } var newObject = object(oldObject); 

إذا كنت بحاجة إلى إجراء عملية استنساخ كائن بالفعل ، فستكون الطريقة الأسرع بشكل متكرر ، وتحسين هذه العملية ، وتصفح خصائصه. ربما تكون هذه هي أسرع خوارزمية لإنشاء نسخ عميقة من الكائنات:

 var cloner = {   _clone: function _clone(obj) {       if (obj instanceof Array) {           var out = [];           for (var i = 0, len = obj.length; i < len; i++) {               var value = obj[i];               out[i] = (value !== null && typeof value === "object") ? _clone(value) : value;           }       } else {           var out = {};           for (var key in obj) {               if (obj.hasOwnProperty(key)) {                   var value = obj[key];                   out[key] = (value !== null && typeof value === "object") ? _clone(value) : value;               }           }       }       return out;   }, clone: function(it) {       return this._clone({       it: it       }).it;   } }; var newObject = cloner.clone(oldObject); 

إذا كنت تستخدم jQuery ، فيمكنك اللجوء إلى التصميمات التالية:

 //   var newObject = jQuery.extend ({}, oldObject); //   var newObject = jQuery.extend (true, {}, oldObject); 

السؤال رقم 12. جافا سكريبت المدمرات


كيف تصنع شيئا مثل المدمر في جافا سكريبت؟ كيفية إدارة دورة حياة الأشياء؟

▍ الإجابة


في JavaScript ، سيتم حذف كائن من الذاكرة بعد اختفاء المرجع الأخير:

 var a = {z: 'z'}; var b = a; var c = a; delete az; delete a; //    a console.log (b, c); // ,  ,    

استخدام شيء مثل "destructor" في JavaScript يؤدي فقط إلى مسح محتويات الكائن ، ولكن ليس في حذفه من الذاكرة.

السؤال رقم 13. معالجة البيانات الثنائية


هل من الممكن معالجة البيانات الثنائية في JavaScript؟ وإذا كان الأمر كذلك ، كيف؟

▍ الإجابة


إذا كنت بحاجة إلى العمل مع البيانات الثنائية في تطبيق JavaScript ، فيمكنك محاولة استخدام مكتبة Binary Parser . لكن رمزها هو الجحيم. في ES6 + هناك اقتراح يتعلق بنوع StructType (هذا هو نفسه الموجود في C ++ بواسطة النوع المركب إلى struct المحددة). هناك حاجة إلى هذا النوع من البيانات لتبسيط العمل مع البيانات الثنائية. قد يبدو العمل معها شيئًا من هذا القبيل:

 const Point2D = new StructType({ x: uint32, y: uint32 }); const Color = new StructType({ r: uint8, g: uint8, b: uint8 }); const Pixel = new StructType({ point: Point2D, color: Color }); const Triangle = new ArrayType(Pixel, 3); let t = new Triangle([{ point: { x:  0, y: 0 }, color: { r: 255, g: 255, b: 255 } },                     { point: { x: 5, y: 5 }, color: { r: 128, g: 0,   b: 0 } },                     { point: { x: 10, y: 0 }, color: { r: 0,   g: 0, b: 128 } }]); 

السؤال رقم 14. تغيير المتغيرات في وظيفة واحدة من وظيفة أخرى


كيفية تغيير المتغيرات الموجودة في وظيفة واحدة من وظيفة أخرى؟

▍ الإجابة


هنا يمكنك تطبيق عدة طرق:

  1. يمكنك استخدام الرابط إلى سياق الوظيفة التي تهمنا (الدالة primer() في المثال التالي) في دالة smth() .
  2. يمكنك تمرير دالة تم إنشاؤها في سياق دالة primer() إلى دالة smth() .

     var primer = function () {    var a, b, c, d, e = {}; smth (function () {        a = 1;        b = 2;        c = 3;        d = 4;    }, e); alert ([a, b, c, d, e.pewpew]); }, smth = function (callback, e) {    callback ();    e.pewpew = "pewpew"; }; primer (); 
  3. في السابق (قبل Firefox 3.6) ، يمكنك الوصول إلى السياق باستخدام خاصية __parent__ ، ولكن بالفعل في Firefox 4 تمت إزالة هذه الميزة.

السؤال رقم 15. العمل مع وظائف


أخبرنا كيف يمكن استدعاء الوظائف في JavaScript.

▍ الإجابة


أفترض أنه لا توجد حاجة للتحدث عن كيفية استدعاء الوظائف والأساليب والبنائين أثناء العمل العادي معهم. دعونا نتحدث عن كيفية استخدام أساليب call() apply() .

باستخدام call () لتكوين مُنشئ كائن


 //   function extend (newObj, oldObj) {   function F () {};   F.prototype = oldObj.prototype;   newObj.prototype = new F ();   return newObj }; var Obj = function () {   this.obj_var = 100; }; Obj.prototype.obj_proto_var = 101; var NewObj = function () {   Obj.call (this); //   Obj     obj_var   this.new_obj_var = 102; }; extend (NewObj, Obj) NewObj.prototype.new_obj_proto_var = 103; new NewObj (); // {new_obj_proto_var: 103, new_obj_var: 102, obj_proto_var: 101, obj_var: 100} 

تحويل كائنات تشبه الصفيف إلى صفائف


تشبه الكائنات المصفوفة صفائف JavaScript ، ولكنها ليست كذلك. على وجه الخصوص ، يتم التعبير عن ذلك في حقيقة أن مثل هذه الأشياء لا تملك طرقًا للصفائف العادية. من بين هذه الكائنات ، على سبيل المثال ، يمكن ملاحظة كائن arguments الخاص بالوظائف التقليدية ونتائج أسلوب getElementsByTagName () .

 // document.getElementsByTagName ("div")  ,   ,    , ,  ,        document.getElementsByTagName ("div"). forEach (function (elem) {    // ... }); // TypeError: document.getElementsByTagName ("div"). forEach is not a function //    Array.prototype.slice.call(document.getElementsByTagName("div")).forEach (function (elem) {   // OK }); //        console.log(Array.prototype.slice.call ('pewpew')) // ["p", "e", "w", "p", "e", "w"] //  IE8    

خلق كائنات المجمع


تتيح لك هذه التقنية لاستخدام call() apply() إنشاء كائنات مجمعة. , - foo() , bar() .

:

 function bar () {console.log(arguments)} // foo (context, arg1, arg2, ...) function foo () {    var context = arguments [0];    var args = Array.prototype.slice.call (arguments, 1); //     bar    bar.apply (context, args); } 

Function.call.apply() :

 function foo() {   Function.call.apply(bar, arguments); } 

, Function.call.apply() , , foo() , bar .

№16.


, , ?


. , Firefox 3.6, __parent__ , .

№17.


, , , eval() ?


, . , :

 // 1:  eval() (function () {    "use strict";    var globalObject = (0, eval) ("this"); //  :)    return globalObject; } ()); // 2:    (function (global) {    // ... } (window)); // 3:      (function () {    return this; } ()); // 4:            ,       . //  -    "use strict"; (function (global) {    // global }) (this); 

№18.


, JavaScript, , ?


JavaScript - « ». . . , :

 $('#smth').click(function onSmthClick(event) {   if (smth) {       //         event.handlerFunction = onSmthClick;       event.handlerContext = this;       //         //  otherObjectSetSomeEvent   event.handlerFunction          otherObjectSetSomeEvent(event);   } else {       //  -    } }); 

— , . , 2 :

 $('#smth'). click (function handler1 (event) {   if (smth) {       //         leftObjectSetSomeEvent(event, function handler2 (e) {           //  -   e       });   } else {       //  -    } }); function leftObjectSetSomeEvent(event, callback) {   callback (event);   //    } 

№19.


JavaScript ? — , .


click «» DOM. (, , ).

 // jQuery $(window).bind ('click', function (e) {   console.log ('Clicked on', e.target); }); //       $('#pewpew').delegate ('*', 'click', function(e) {   console.log('Clicked on', e.target); }); //     $('#pewpew').delegate('.pewpew', 'click', function (e) {   console.log ('Clicked on element with .pewpew class name'); }); 

№20. XHR-


XHR- jQuery?


, - :

 function xhr(m, u, c, x) { with(new XMLHttpRequest) onreadystatechange = function (x) {   readyState ^ 4 || c(x.target) }, open(m, u), send() } 

- :

 function xhr(m, u, c, x) { with(new(this.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")) onreadystatechange = function (x) {   readyState ^ 4 || c(x) }, open(m, u), send() } 

:

 xhr('get', '//google.com/favicon.ico', function (xhr) { console.dir(xhr) }); 

№21. -


(reflow) (repaint) -?


  1. requestAnimationFrame() , , setInterval() setTimeout() . , , , , , . , JavaScript- CSS- SVG-. , , , , , , . , , .
  2. float- ( ).
  3. DOM. , , , DOM ( ).
  4. — . ( , , ). هنا مثال:

     //      element.style.left = "150px;"; // ... element.style.color = "green"; //  ,   ,    element.setAttribute ('style', 'color: green; left: 150px'); 
  5. ( ).
  6. — ( style.display = "none" ). , .

, -, . , , , , .

  1. .
  2. DOM- ( — ).
  3. Document.querySelectorAll() firstElementChild .
  4. , document.getElementsByTagName() ( , DOM, ).

№22. Node.js


Node.js, , ?


, ( PHP Apache). , , . Node.js — . , , cluster . (master) - (worker). , .

№23. runInNewContext() Node.js


runInNewContext() Node.js.


. , ( Node.js- Nodester). - , runInNewContext() . , «», . «» , , runInNewContext() .

ملخص


JavaScript . , - , .

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

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


All Articles