データバイナリ
コンピュータ内のすべてのコンテンツ (テキスト、数値、画像、オーディオ、ビデオ) は最終的にバイナリで表現され
、
JS
列などの非常に直感的なデータを直接処理できます。
画像
JS
またはHTML
によってブラウザに処理されます。ブラウザは画像のアドレスただし、サーバーでは、
utf-8
ではなくGBK
でエンコードされますBuffer
読み取って処理する役割を担う、 sharp
というライブラリがあります。Node
では、 TCP
介して長い接続が確立され、バイトが送信されます。データは渡される前にバイトに変換され、送信されるバイトのサイズを知る必要があります (クライアントはサイズに基づいて読み取るコンテンツの量を判断する必要があります)。バッファとバイナリが
わかります。フロントエンド開発では、通常、バイナリ相互の処理に関連することはほとんどありませんが、サーバーサイドでは、多くの機能を実装するために、そのバイナリデータを直接操作する必要がある
ため、開発者がより多くの機能を完了しやすくすることができます
, Node
Buffer
という名前のクラスを提供しており、これはグローバルです
前に述べたように、バイナリ データは Buffer に保存されますが、どのように保存されるのでしょうか。
8
ビットのバイナリ00000000
格納できます。これはちょうど 1 バイトです。なぜ 8 ビットなのでしょうか。
1 byte = 8 bit
のbyte
と呼ばれます。1 byte = 8 bit
、 1kb = 1024 byte
、 1M = 1024kb
、 1 G = 1024 M
int
型は4
バイト、 long
型は8
バイトです。TCP
For を送信します。バイトストリームの場合、書き込み時と読み取り時にバイト数を指定する必要があります。RGB
の値はそれぞれ255
であるため、本質的に、バッファーと文字列
Buffer
1 バイトでコンピューターに保存されます。バイトの配列に相当します。配列内の各項目のサイズは 1 バイトです。
文字列をバッファーに入れたい場合は、どのようなプロセスを実行すればよいでしょうか。
const message = 'Hello'
buffer
// new キーワードを使用してバッファ インスタンスを作成しますが、この作成メソッドの有効期限が切れています constbuffer = new Buffer(message) console.log(buffer); // <バッファ 48 65 6c 6c 6f> console.log(buffer.toString()); // こんにちは、
中国語の文字列のエンコードとデコードです。
buffer
のデフォルトのエンコードはutf-8
なので、次のコードでは、 Buffer
クラスは文字列のエンコードに utf-8 を使用します。文字列のデコードにも utf-8 を使用します。utf3
バイトのバイナリ エンコードconst message = 'Hello'に対応します。
// Buffer.from を使用して文字列をデコードします constbuffer = Buffer.from(message) console.log(buffer); // <バッファ e4 bd a0 e5 a5 bd e5 95 8a> // エンコードをデコードできる toString メソッドがバッファ インスタンスにあります console.log(buffer.toString()); // 'Hello'
、エンコードとデコードで異なる形式のエンコード結果が使用される場合はどうなりますか?
const message = 'Hello' const バッファ = Buffer.from(メッセージ, 'utf16le') console.log(buffer); // <バッファ 60 4f 7d 59 4a 55> console.log(buffer.toString()); // `O}YJU
バッファを作成する他の方法
ここでは、alloc を使用してバッファ インスタンスを直接作成
buffer
Buffer
alloc
各 1 ビットが変更されます。
バッファの桁数を指定できます。たとえば、ここに 8 が渡された場合、作成されるバッファには 8 つの要素が含まれ、各要素に対応する 2 進数は 0 になります。 const バッファ = Buffer.alloc(8) console.log(buffer); // <バッファ 00 00 00 00 00 00 00 00> // 値が 10 進数に割り当てられている場合、バッファーはそれを 16 進数に変換し、対応する場所に書き込むのに役立ちます。buffer[0] = 88 // jsでは0xで始まるものは16進数で表現されます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', { エンコーディング: 'utf-8' }, (err, data) => { console.log(data); // 笑})
const fs = require('fs') // エンコードは utf16le 文字エンコーディングを使用し、デコードは utf-8 形式を使用します。 fs.readFile('./a.txt', {エンコーディング: 'utf16le' }, (err) 、データ) => { console.log(data); // エラー }) // 上記のコードは次のコードと似ています const msg = 'Haha' const バッファ = Buffer.from(msg, 'utf-8') console.log(buffer.toString('utf16le')); //
2. 画像をコピーする目的を達成するために、画像ファイルは
画像エンコーディングをコピーします。
encoding
属性を指定しないでください。fs = require('fs') を
fs.readFile('./logo.png', (err, data) => { console.log(data); // 出力されるのは、画像ファイルに対応するバイナリ エンコーディングです。 // 画像エンコーディングを別のファイルに書き込むこともできます。これは、画像をコピーするのと同じです。 fs.writeFile(' ./bar) .png'、データ、エラー => { コンソール.ログ(エラー); }) })
sharp
ライブラリconst Sharp = require('sharp')を使用できます。
// logo.png 画像を 200x300 にトリミングし、bax.png ファイルにコピーします Sharp('./logo.png') .resize(200, 300) .toFile('./bax.png', (err, 情報) => { コンソール.ログ(エラー); }) // 最初に画像ファイルをバッファに変換してから、画像をファイルに書き込むこともできます。sharp('./logo.png') .resize(300, 300) .toBuffer() .then(データ => { fs.writeFile('./baa.png', data, err => { コンソール.ログ(エラー); }) })
バッファ作成プロセス
Buffer
まず8 * 1024
バイト、つまり8kb
のメモリが適用されます。イベントループとは何ですか?
イベントループとは何ですか?
JS
とブラウザーまたはNode
の間のブリッジとして理解しています。JS
コードとブラウザー API 呼び出し ( setTimeout
、 AJAX
、监听事件
など) の間のリンクです。 ) ブリッジはコールバック関数を通じて通信します。file system
、 networ
など) の間のブリッジです。プロセスとスレッド
プロセスとスレッドは、オペレーティング システムにおける 2 つの概念です。
process
): コンピューターが実行するプログラムthread
): オペレーティング システムが計算スケジュールを実行できる最小単位で、 CPU
直接操作できます。スレッドは非常に抽象的に聞こえますが、直感的に説明しましょう。
説明するために、いくつかの例を示します
。複数
のプロセス (音楽を聴きながら、コードを書きながら) を実行できます。 、情報の確認)を同時に行いますか?
CPU
の計算速度が非常に速く、複数のプロセスをすばやく切り替えることができるため、ブラウザと JavaScript
JavaScript
はシングルスレッドであるとよく言われますが、JS スレッドには独自のコンテナ プロセスが必要ですNode
ブラウザまたは Node ブラウザはプロセスですか? スレッドは 1 つだけですか?
tab
が停止してすべてのページが応答しなくなるのを防ぐためです。ただし、JavaScript コードの実行は、同時に
JS
のことしか実行できません。JavaScript の実行処理
関数は、関数呼び出しスタックにプッシュされるまで実行されないので、
const message = 'Hello World ' というコードの実行処理を分析してみましょう。
console.log(メッセージ); 関数 sum(num1, num2) { num1 + num2 を返す } 関数 foo() { const 結果 = sum(20, 30) console.log(結果); } foo()
main
関数で実行されるとみなすことができますmessage
を定義しlog
関数を実行するfoo
関数を呼び出します。ただし、foo 関数は実行中に呼び出される必要がsum
js
コード全体が実行され、main 関数がブラウザのイベント ループ
からポップアウトされますJS
コードの実行中に非同期操作があった場合はどうなるでしょうか。
setTimeout
関数呼び出しを途中に挿入するとその後、関数は setTimeout 関数に渡されます。 (これをtimer
機能と呼びます)、いつ実行されますか?
web api
呼び出し、適切なタイミングで、タイマー関数がイベント キューに追加され、setTimeout がコードの実行をブロックしないのはなぜですか
?これは、ブラウザーが非常に重要なことを維持しているためです。イベント ループ
ブラウザーは、コールバック関数を何らかの方法で setTimeout に保存するのに役立ちます。より一般的な方法は、コールバック関数を赤黒ツリーに保存し
、setTimeout がスケジュールされるまで待つこと
タイマー時間になると、タイマー コールバック関数が保存された場所から取り出され、
イベント ループがキューに何かがあり、現在の関数呼び出しスタックが空であることが検出されると、それがイベント キューに入れられます
同期コードが実行されると、キュー内のコールバック関数がデキューされ、実行のために関数呼び出しスタックに置かれます (
もちろん
、キュー内の前の関数がポップアウトされるまで、次の関数はスタックにプッシュされません)。たとえば、特定のプロセス中にユーザーがブラウザーでボタンをクリックすると、コールバック関数に対応するこのボタンのクリックを監視することがあります。キューにも追加されます。実行順序は、イベント キュー内の順序に基づきます。イベント キューにajax
リクエストを送信するコールバックの概要もあります
。実際、イベント ループは非常に単純なもので、特別な状況下で特定のコールバックを実行する必要がある場合に、コールバックが保存されることを意味します。事前にイベント キューに詰め込まれ、イベント ループがそれを取り出して関数呼び出しスタックに置きます。
macrotask queue
タスク
ただし、イベント ループはキューを 1 つだけ保持するわけではなく、キュー内のタスクの実行はすべてのスクリプトが実行される
ajax
setTimeout
、 setInterval
、 DOM
モニタリング、 UI Rendering
、およびその他のmicrotask queue
): Promise
のthen
コールバック、 Mutation Observer API
、 queueMicrotask()
など。では、イベント ループ内の 2 つのキューの優先順位は何でしょうか?
main script
のコードが最初に実行されます (トップレベルのスクリプト コードが記述されます)。<1>
テストポイント: main stcipt
、 setTimeout
、 Promise
、 then
、 queueMicrotask
setTimeout(() => { console.log('set1');4 新しい約束(解決 => { 解決する() }).then(解決 => { 新しい約束(解決 => { 解決する() }).then(() => { console.log('then4'); }) console.log('then2'); }) }) 新しい約束(解決 => { console.log('pr1'); 解決する() }).then(() => { console.log('then1'); }) setTimeout(() => { console.log('set2'); }) コンソール.ログ(2); queueMicrotask(() => { console.log('queueMicrotask'); }) 新しい約束(解決 => { 解決する() }).then(() => { console.log('then3'); }) // pr1 // 2 //その後1 //キューマイクロタスク //それで3 // セット1 //その後2 //それで4 // set2
setTimeout
関数呼び出しスタックにすぐにプッシュされ、実行後すぐにスタックからポップアウトされます。そのtimer
関数はマクロ タスク キューに入れられ、
Promise
クラスに渡された関数はすぐに実行されます。これはコールバック関数ではないため、 pr1
が出力され、 resolve
メソッドが実行されるため、Promise のステータスが即座にfulfilled
に変更され、 then
関数が実行されると、対応するコールバック関数が実行されます。
スタックがポップされると、関数が
スタックにプッシュされconsole.log
2
キュー
ここで関数がqueueMicrotask
にバインドされ、関数が配置されます
。
新しい Promise ステートメントが検出されましたが、すぐに Promise ステータスがフルフィルメントに変更されたため、コールバック
then 関数に対応するタスクもマイクロタスク キューに入れられました。
同期スクリプト コードが実行されたため、ループの開始時に、マイクロタスク キューおよびマクロタスクと競合するタスクがマイクロタスク キューに入れられます。注: マイクロタスクの優先順位は、マクロタスクを実行する前に毎回読み取る必要があります。空ではないので、
最初のマイクロタスクはthen1
印刷し、3 番目のマイクロタスクはthen3
印刷します。その後、実行が完了します。
マクロ
タスクはより複雑で、最初にset1
を出力し、その後、そのnew promise
バックがマイクロタスク キューに入れられることに注意してください。キューは空ではないため、より高い優先度のマイクロタスク キューを実行する必要があります。これは、同じ新しい Promise ステートメントであり、対応する then スワップがマイクロタスク キューに入れられる、then コールバックと同等です。新しい Promise ステートメントの後にconsole
関数があることに注意してください。この関数は、新しい Promise ステートメントの実行直後に実行されます。 then2
、マイクロタスク対立にはタスクがまだ残っているため、次のステップは print です。 then4
.これまでのところ、マイクロタスク キューは空であり、マクロタスク キューは引き続き実行できるため
、マクロタスクが実行された後、次のマクロタスクset2
が出力され、
コード全体の出力結果はpr1 -> 2 -> then1 -> queueMicrotask -> then3 -> set1 -> then2 -> then4 -> set2
面接の質問 <2>
テストポイント: main script
、 setTimeout
、 Promise
、 then
、 queueMicrotask
、 await
、 async
知識補足: async、await は構文シュガーですイベントループのPromise
を扱う場合、
new Promise((resolve,rejcet) => { 函数执行})
でラップされたコードとみなすことができますthen(res => {函数执行})
非同期関数 async1() {の then(res => {関数実行}) のコードとして
console.log('async1 start'); async2()を待つ console.log('async1 終了'); } 非同期関数 async2() { console.log('async2'); } console.log('スクリプト開始'); setTimeout(() => { console.log('setTimeout'); }、0) async1() 新しい約束(解決 => { console.log('promise1'); 解決する() }).then(() => { console.log('promise2'); }) console.log('スクリプト終了'); // スクリプトの開始 // async1 開始 // 非同期2 // 約束1 // スクリプト終了 // async1 終了 // 約束2 // setTimeout
は最初の関数定義であり、最初のconsole
ステートメントに遭遇するまで、実行のために関数呼び出しスタックにプッシュする必要はありません。スタックをプッシュした後、印刷script start
、スクリプトをポップアウトします。これは
setTimeout
timer
マクロ タスク キューに入れられ
、async1 関数が実行されます。まず、 async1 start
が出力され、次にawait
ステートメントの後のasync2
関数が実行されます。前述したように、 await キーワードの後の関数はnew Promise
としてみなされるため、この関数はすぐに実行され、 async2 が出力されますが、 await ステートメントの後のコードは then に置かれたのと同じです。 callback、つまりconsole.log('async1 end')
このコード行はマイクロタスク キューに入れられ
、コードは実行を続けます。そのため、 promise1
関数がすぐに出力されます。次に、コールバックがマイクロタスク キューに入れられ、
印刷用の最後のコンソール関数が実行されます。同期script end
が実行されます。
まず、
最初のマイクロタスクに対応する print ステートメントが実行されます。つまり、 async1 end
が出力され、その後、 promise2
が出力されます。このとき、マイクロタスク キューは空であり、マクロタスク キュー内のタスクが開始されます。
このとき、タイマー関数に対応する setTimeout も出力され、最終的な出力シーケンスは、 script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout
となります
。
script start -> async1 start -> async2 -> promise1 -> script end -> async1 end -> promise2 -> setTimeout