البيانات الثنائية
سيتم تمثيل كل المحتوى الموجود في الكمبيوتر: النصوص والأرقام والصور والصوت والفيديو في النهاية بواسطة نظام ثنائي.
يمكن JS
معالجة بيانات بديهية للغاية بشكل مباشر: مثل السلاسل، وعادةً ما نعرض هذه المحتويات للمستخدمين،
ولكن قد تعتقد أن JS
في الواقع، تتممعالجة
JS
HTML
إخبار المتصفح بعنوان الصورةومع ذلك،
utf-8
، ولكن باستخدام GBK
. يجب أن نقرأها ثم يتم تحويل البيانات الثنائية إلى نص مناظر من خلال GKB.sharp
في Node، وهي مسؤولة عن قراءة الصور أو Buffer
الصور الواردة ثم معالجتها،Node
، يتم إنشاء اتصال طويل من خلال TCP
ينقل بايت نحتاج إلى تحويل البيانات إلى بايت قبل تمريرها، ويجب معرفة حجم البايتات المرسلة (يحتاج العميل إلى الحكم على مقدار المحتوى الذي سيتم قراءته بناءً على الحجم).سنجد ذلك
في المخزن المؤقت والثنائي.
بالنسبة لتطوير الواجهة الأمامية، نادرًا ما يتعلق الأمر بالتعامل الثنائي مع بعضها البعض، ولكن بالنسبة لجانب الخادم، من أجل تنفيذ العديد من الوظائف، يجب علينا تشغيل بياناته الثنائية مباشرة،
لذلك، من أجل تسهيل إكمال المزيد من الوظائف للمطورين تزودنا Node
بفئة اسمها Buffer
، وهي عالمية كما
قلنا من قبل، يتم تخزين البيانات الثنائية في Buffer، فكيف يتم تخزينها؟
8
بتات ثنائية: 00000000
، وهو ما يعادل بايت واحد بالضبط. لماذا هو 8 بت؟
byte
1 byte = 8 bit
، 1kb = 1024 byte
، 1M = 1024kb
، 1 G = 1024 M
int
في العديد من لغات البرمجة هو 4
بايت، والنوع long
هو 8
بايت،TCP
RGB
هي 255
على التوالي، لذلك في جوهرها،Buffer وstring
Buffer
ببايت واحد في الكمبيوتر. يعادل مصفوفة من البايتات. كل عنصر في المصفوفة يبلغ حجمه بايت واحد.
إذا أردنا وضع سلسلة في المخزن المؤقت، فما هي العملية؟
buffer
.. // استخدم الكلمة الأساسية الجديدة لإنشاء مثيل مخزن مؤقت، ولكن انتهت صلاحية طريقة الإنشاء هذه const buffer = new Buffer(message) console.log(buffer); // <Buffer 48 65 6c 6c 6f> console.log(buffer.toString()); // مرحبًا
بتشفير وفك تشفير السلسلة الصينية.
buffer
المؤقت هو utf-8
، لذلك في الكود التالي، تستخدم فئة Buffer
ترميز utf-8 لتشفير السلسلة الخاصة بنا ، نستخدم أيضًا utf-8 لفك تشفير سلاسلنا3
رسالة const ذات ترميز ثنائي مكونة من 3 بايت = "مرحبًا". // استخدم Buffer.from لفك تشفير السلسلة const buffer = Buffer.from(message) console.log(buffer); // <Buffer e4 bd a0 e5 a5 bd e5 95 8a> // هناك طريقة toString في مثيل المخزن المؤقت يمكنها فك تشفير التشفير console.log(buffer.toString()); // 'Hello'
، ماذا سيحدث إذا استخدم التشفير وفك التشفير أشكالًا مختلفة من نتائج التشفير؟
const message = 'Hello' المخزن المؤقت const = Buffer.from(message, 'utf16le') console.log(buffer); // <Buffer 60 4f 7d 59 4a 55> console.log(buffer.toString()); // `O}YJU
طرق أخرى لإنشاء مخازن مؤقتة
هناك طرق عديدة لإنشاء buffer
Buffer
من alloc
تخصيصها يتم تعديل كل بت واحد.
// والذي يمكنه تحديد عدد الأرقام المخزنة لدينا على سبيل المثال، إذا تم تمرير 8 هنا، فإن المخزن المؤقت الذي تم إنشاؤه سيحتوي على 8 عناصر، والرقم الثنائي المقابل لكل عنصر هو 0. المخزن المؤقت الثابت = Buffer.alloc(8) console.log(buffer); // <المخزن المؤقت 00 00 00 00 00 00 00 00> // إذا تم تعيين القيمة لرقم عشري، فسيساعدنا المخزن المؤقت في تحويلها إلى رقم سداسي عشري ثم كتابتها في الموقع المقابل buffer[0] = 88 // في لغة js، يتم تمثيل أي شيء يبدأ بـ 0x كرقم سداسي عشري buffer[1] = 0x88 console.log(buffer); // <Buffer 58 88 00 00 00 00 00 00>
عمليات المخزن المؤقت والملف
1. إذا لم يحدد الملف النصي
buffer
المؤقت الأصلي مباشرة ، وهو نتيجة محتوى الملف بالرقم الثنائي المشفر utf-8
const fs = require('fs') fs.readFile('./a.txt', (err, data) => { console.log(data); // <المخزن المؤقت e5 93 88 e5 93 88> })
const fs = require('fs') // يشير الترميز إلى ترميز الأحرف المستخدم لفك التشفير، ويكون الترميز الافتراضي هو utf-8 fs.readFile('./a.txt', { encoding: 'utf-8' }, (err, data) => { console.log(data // Haha})
const fs = require('fs') // يستخدم التشفير ترميز الأحرف utf16le، ويستخدم فك التشفير تنسيق utf-8. يجب أن يكون فك التشفير غير صحيح fs.readFile('./a.txt', { encoding: 'utf16le' }, (err. البيانات) => { console.log(data) // خطأ }) // الكود أعلاه مشابه للكود التالي const msg = 'Haha' المخزن المؤقت const = Buffer.from(msg, 'utf-8') console.log(buffer.toString('utf16le')); //
2. ينسخ ملف الصورة
ترميز الصورة لتحقيق غرض نسخ الصورة،
encoding
، لأن ترميز الأحرف تتم قراءته فقط عند قراءة الصورة، وهو مفيد فقط عند جلب الملفات النصية. fs.readFile('./logo.png', (خطأ، بيانات) => { console.log(data); // ما تتم طباعته هو الترميز الثنائي المطابق لملف الصورة // يمكننا أيضًا كتابة ترميز الصورة إلى ملف آخر، وهو ما يعادل نسخ الصورة fs.writeFile(' ./bar .png'، بيانات، خطأ => { console.log(err); }) })
sharp
const Sharp = require('sharp') // قم بقص صورة logo.png إلى 200 × 300 وانسخها إلى الملف bax.png Sharp('./logo.png') تغيير الحجم (200، 300) .toFile('./bax.png', (خطأ، معلومات) => { console.log(err); }) // يمكنك أيضًا تحويل ملف الصورة إلى مخزن مؤقت أولاً، ثم كتابته في الملف، ويمكنك أيضًا نسخ الصورة Sharp('./logo.png') تغيير الحجم (300، 300) .toBuffer() .ثم(البيانات => { fs.writeFile('./baa.png', بيانات, يخطئ => { console.log(err); }) })
عملية إنشاء المخزن المؤقت
Buffer
، فإننا لن نقوم في كثير من الأحيان بطلب الذاكرة من نظام التشغيل بشكل افتراضي، سيتم تطبيقه أولاً على ذاكرة بحجم 8 * 1024
بايت، أي 8kb
ما هي حلقة الحدث؟
ما هي حلقة الحدث؟
JS
الذي نكتبه والمتصفح أو Node
.JS
الذي نكتبه واستدعاءات واجهة برمجة تطبيقات المتصفح ( setTimeout
، AJAX
،监听事件
، وما إلى ذلك). ) تتواصل الجسور من خلال وظائف رد الاتصال.file system
الجسور أيضًا من خلال وظائف رد networ
.العملية والخيط
العملية والخيط مفهومان في نظام التشغيل:
process
): البرنامج الذي قام الكمبيوتر بتشغيلthread
): أصغر وحدة يمكن لنظام التشغيل تشغيل جدول الحساب، بحيث يمكن CPU
أن تعمل مباشرةمجردًا للغاية، فلنشرحه بشكل بديهي:
الرسائل
نظام تشغيل
متعدد العمليات
، والتحقق من المعلومات) العمل في نفس الوقت؟
CPU
سريعة جدًا، ويمكنها التبديل بسرعة بين عمليات متعددة.المتصفحات وجافا سكريبت
غالبًا ما نقول أن JavaScript
عبارة عن خيط واحد، ولكن يجب أن يكون لسلسلة JS عملية حاوية خاصة بها Node
هل المتصفح أو متصفح Node عبارة عن عملية؟
tab
، ستبدأ عملية جديدة لمنع توقف صفحة واحدة والتسبب في عدم استجابة جميعومع ذلك، يتم تنفيذ تعليمات JavaScript في سلسلة منفصلة،
JS
يمكنه فعل شيء واحد فقط في نفسعملية تنفيذ JavaScript
حتى يتم دفعها إلى مكدس استدعاء الوظيفة، دعنا نحلل عملية تنفيذ الرمز
const message = 'Hello World'. console.log(message); مجموع الدالة (num1، num2) { عودة رقم 1 + رقم 2 } وظيفة فو () { النتيجة الثابتة = مجموع (20، 30) console.log(result); } foo()
main
مثل لغات البرمجة الأخرىmessage
المتغيرةlog
sum
وظيفة foo
js
بالكامل، ويتم إخراج الوظيفة الرئيسية منحلقة أحداث المتصفح
. ماذا لو كانت هناك عمليات غير متزامنة أثناء تنفيذ كود JS
؟
setTimeout
في المنتصفثم تم تمرير الوظيفة إلى وظيفة setTimeout (نسميها وظيفة timer
)، متى سيتم تنفيذها؟
web api
. وسيقوم المتصفح بتخزين وظيفة رد الاتصال مسبقًا، وفي الوقت المناسب، ستتم إضافة وظيفة المؤقت إلى قائمة انتظار الأحداث،لماذا لا يمنع setTimeout تنفيذ التعليمات البرمجية
هذا لأن المتصفح يحتفظ بشيء مهم جدًا -
سيساعدنا متصفح حلقة الأحداث في حفظ وظيفة رد الاتصال في setTimeout بطريقة ما. الطريقة الأكثر شيوعًا هي حفظها في شجرة حمراء وسوداء
والانتظار حتى تتم جدولة setTimeout عندما يصل وقت المؤقت، سيأخذ وظيفة رد اتصال المؤقت الخاصة بنا من المكان المحفوظ ويضعها في قائمة انتظار الأحداث
بمجرد أن تجد حلقة الحدث أن هناك شيئًا ما في قائمة الانتظار الخاصة بنا، وأن مكدس استدعاء الوظيفة الحالي فارغ بعد تنفيذ رمز المزامنة، سيتم حذف وظائف رد الاتصال في قائمة الانتظار لدينا ووضعها في مكدس استدعاء الوظائف للتنفيذ (لن يتم دفع الوظيفة التالية إلى المكدس حتى يتم إخراج الوظيفة السابقة في قائمة الانتظار
بالطبع
).، لا يجب أن يكون هناك حدث واحد فقط، على سبيل المثال، أثناء عملية معينة، ينقر المستخدم على زر في المتصفح، وقد يكون لدينا شاشة للنقر على هذا الزر، والتي تتوافق مع وظيفة رد الاتصال سيتم أيضًا إضافتها إلى قائمة الانتظار الخاصة بنا، ويعتمد أمر التنفيذ على الترتيب الموجود في قائمة انتظار الأحداث. يوجد أيضًا ملخص لعمليات الاسترجاعات التي نرسلها لطلبات ajax
إلى قائمة انتظار الأحداث
: في الواقع، حلقة الحدث هي شيء بسيط جدًا، وهذا يعني أنه عندما يلزم تنفيذ رد اتصال معين في موقف خاص، فسيتم حفظ رد الاتصال يتم حشوها مسبقًا في قائمة انتظار الأحداث، وتقوم حلقة الحدث بإخراجها ووضعها في مكدس استدعاءات الوظائف.
مهام الماكرو والمهام الصغيرة،
ومع ذلك، لا تحتفظ حلقة الحدث بقائمة انتظار واحدة فقط، في الواقع، هناك قائمتان، ويجب أن ينتظر تنفيذ المهام في قائمة الانتظار
macrotask queue
ajax
setTimeout
و setInterval
ومراقبة DOM
UI Rendering
microtask queue
): رد اتصال Promise
then
، Mutation Observer API
، queueMicrotask()
، وما إلى ذلك.إذن ما هي أولوية قائمتي الانتظار في حلقة الأحداث؟
main script
أولاً (يتم كتابة رمز البرنامج النصي ذي المستوى الأعلى)أي
نقاط الاختبار: main stcipt
، setTimeout
، Promise
، then
، queueMicrotask
setTimeout(() => { console.log('set1');4 وعد جديد (الحل => { حل() }).ثم (الحل => { وعد جديد (الحل => { حل() }).ثم(() => { console.log('then4'); }) console.log('then2'); }) }) وعد جديد (الحل => { console.log('pr1'); حل() }).ثم(() => { console.log('then1'); }) setTimeout(() => { console.log('set2'); }) console.log(2); queueMicrotask(() => { console.log('queueMicrotask'); }) وعد جديد (الحل => { حل() }).ثم(() => { console.log('then3'); }) // pr1 // 2 // ثم1 //queueMicrotask //ثم3 // مجموعة1 // ثم 2 //ثم4سيتم دفع
// set2
setTimeout
إلى مكدس استدعاءات الوظيفة على الفور، وسيتم إخراجه من المكدس مباشرة بعد التنفيذ. سيتم وضع وظيفة timer
الخاصة به في قائمة انتظار مهام الماكرو
وسيتم تنفيذ الوظيفة التي تم تمريرها إلى فئة Promise
على الفور. إنها ليست وظيفة رد اتصال، لذلك ستتم طباعة pr1
، ونظرًا لتنفيذ طريقة resolve
، ستتغير حالة الوعد على الفور إلى fulfilled
، بحيث ستتم وظيفة رد الاتصال then
لها عند تنفيذ الوظيفة. يتم وضعها في قائمة انتظار المهام الدقيقة وتتم
مواجهة وظيفة setTimeout مرة أخرى عند ظهور المكدس، وسيتم وضع وظيفة المؤقت الخاصة بها في قائمة انتظار مهام الماكرو
عندما تواجه عبارة console.log
بعد دفع الوظيفة إلى 2
تمت طباعته، ثم انبثقت
دالة مرتبطة queueMicrotask
هنا، وسيتم وضع الوظيفة بعد الدخول إلى قائمة انتظار المهام الدقيقة،
تمت مواجهة عبارة وعد جديدة، ولكنها غيرت على الفور حالة الوعد إلى الوفاء، لذلك رد الاتصال. تم أيضًا وضع الوظيفة المقابلة للوظيفة في قائمة انتظار المهام الدقيقة
منذ أن تم تنفيذ كود البرنامج النصي للمزامنة، والآن يتم وضع الحدث في بداية الحلقة، حيث يتم وضع المهام المتنافسة مع قائمة انتظار المهام الصغيرة والمهمة الكلية في قائمة انتظار المهام الصغيرة. مكدس استدعاءات الوظائف حسب الأولوية. ملاحظة: أولوية المهام الصغيرة أعلى من المهام الكلية. يجب عليك قراءتها في كل مرة قبل أن ترغب في تنفيذ مهمة ماكرو. تحقق مما إذا كانت قائمة انتظار المهام الصغيرة فارغة إنها ليست فارغة، فأنت بحاجة إلى تنفيذ مهمة قائمة انتظار المهام الدقيقة أولاً،
المهمة الدقيقة الأولى هي الطباعة then1
، والمهمة الدقيقة الثانية هي طباعة قائمة الانتظار الدقيقة، والمهمة الدقيقة الثالثة هي الطباعة بعد ذلك then3
. ابدأ في تنفيذ مهمة الماكرو.
ستكون مهمة الماكرو الأولى أكثر تعقيدًا، وستقوم أولاً بطباعة set1
، ثم تنفيذ بيان new promise
يغير الحالة على الفور، ثم سيتم وضع رد الاتصال في قائمة انتظار المهام الدقيقة قائمة الانتظار ليست فارغة، لذلك يجب تنفيذ قائمة انتظار المهام الصغيرة ذات الأولوية الأعلى، وهو ما يعادل رد الاتصال الذي يتم تنفيذه على الفور، وهو نفس بيان الوعد الجديد، ويتم وضع المبادلة المقابلة له في قائمة انتظار المهام الصغيرة. لاحظ أن هناك وظيفة console
بعد بيان الوعد الجديد، وسيتم تنفيذ هذه الوظيفة فورًا بعد تنفيذ بيان الوعد الجديد، أي الطباعة then2
then4
. حتى الآن، قائمة انتظار المهام الدقيقة فارغة، ويمكن الاستمرار في تنفيذ قائمة انتظار المهام الكبيرة
، لذلك ستتم طباعة set2
المهام الكبيرة التالية بعد تنفيذ المهام الكبيرة،
وستكون نتيجة طباعة الكود بأكمله هي: pr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2
أسئلة المقابلة set2 <2>
نقاط الاختبار: main script
، setTimeout
، Promise
، then
، queueMicrotask
، await
async
ملحق المعرفة غير المتزامن: غير متزامن، الانتظار عبارة عن سكر بناء الجملة بالنسبة لـ Promise
. عند التعامل مع مشكلات حلقة الأحداث،
new Promise((resolve,rejcet) => { 函数执行})
then(res => {函数执行})
فيدالة الوعد غير المتزامنة السابقة async1() { console.log('async1 start'); في انتظار غير المتزامن2 () console.log('async1 end'); } وظيفة غير متزامنة async2() { console.log('async2'); } console.log("بدء البرنامج النصي"); setTimeout(() => { console.log('setTimeout'); }, 0) غير متزامن1() وعد جديد (الحل => { console.log('promise1'); حل() }).ثم(() => { console.log('promise2'); }) console.log('نهاية البرنامج النصي'); // بداية البرنامج النصي // بداية غير متزامنة // غير متزامن2 // وعد1 // نهاية البرنامج النصي // نهاية غير متزامنة // وعد2 // setTimeout
هو تعريف دالة في البداية، ولا يحتاج إلى دفعه إلى مكدس استدعاء الوظيفة للتنفيذ حتى يواجه بيان console
الأول. بعد دفع المكدس، script start
للطباعة ثم أخرجه من ملف المكدس
لمواجهة وظيفة setTimeout
الأولى، والتي تتوافق مع سيتم وضع timer
في قائمة انتظار مهام الماكرو
وسيتم تنفيذ وظيفة async1 أولاً، ستتم طباعة async1 start
، ثم سيتم تنفيذ وظيفة async2
بعد بيان await
. لأنه كما ذكرنا من قبل، تعتبر الوظيفة بعد الكلمة الأساسية انتظار new Promise
سيتم تنفيذ هذه الوظيفة على الفور، لذلك ستتم طباعة async2، لكن الكود الموجود بعد عبارة الانتظار يعادل وضعه في ثم. رد الاتصال، أي console.log('async1 end')
يتم وضع سطر التعليمات البرمجية هذا في قائمة انتظار المهام الصغيرة
ويستمر تنفيذ التعليمات البرمجية ويواجه عبارة Promise جديدة، لذلك تتم طباعة الوظيفة في ملف promise1
على الفور ثم يتم وضع رد الاتصال في قائمة انتظار المهام الدقيقة لتنفيذ
وظيفة وحدة التحكم الأخيرة للطباعة، وتم تنفيذ script end
المزامنة، وستنتقل حلقة الحدث إلى مهمة الماكرو وقوائم انتظار المهام الصغيرة لتنفيذ
المهام سيتم تنفيذ بيان الطباعة المطابق للمهمة الصغيرة الأولى، مما يعني أنه سيتم طباعة async1 end
، ثم تتم طباعة promise2
، في هذا الوقت، تكون قائمة انتظار المهام الصغيرة فارغة، وتبدأ المهام في قائمة انتظار المهام الكبيرة
تنفيذ
setTimeout المطابق لوظيفة المؤقت في هذا الوقت، ويتم تنفيذ المهمة الكبيرة أيضًا، ويكون تسلسل الطباعة النهائي هو: script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout