Dari definisi ke eksekusi, mesin JS melakukan banyak inisialisasi bekerja di lapisan implementasi. Oleh karena itu, sebelum mempelajari mekanisme kerja mesin JS, kita perlu memperkenalkan beberapa konsep terkait: tumpukan lingkungan eksekusi, objek global, lingkungan eksekusi, objek variabel, objek aktif, ruang lingkup dan rantai ruang lingkup, dll. Konsep -konsep ini adalah komponen inti dari karya mesin JS. Tujuan artikel ini bukan untuk menjelaskan setiap konsep kepada Anda secara terpisah, tetapi untuk menganalisisnya melalui demo sederhana, menjelaskan detail mesin JS dari definisi ke eksekusi, dan peran konsep -konsep ini dimainkan di dalamnya.
var x = 1; // Tentukan variabel global xfunction a (y) {var x = 2; // Tentukan fungsi variabel x lokal b (z) {// tentukan fungsi internal b konsol.log (x+y+z); } return b; // Kembalikan referensi ke fungsi b} var c = a (1); // Jalankan A, kembalikan BC (1); // Jalankan fungsi bDemo ini adalah penutupan, dan hasil eksekusi adalah 4 di bawah ini kami akan menganalisis mekanisme kerja mesin JS dalam tiga tahap: inisialisasi global, fungsi eksekusi A, dan fungsi eksekusi B :
1. Inisialisasi global
Ketika mesin JS memasuki sepotong kode yang dapat dieksekusi, ia perlu menyelesaikan tiga tugas inisialisasi berikut:
Pertama, buat objek global (objek global). Hanya ada satu salinan global dari objek ini, propertinya dapat diakses di mana saja, dan keberadaannya akan menyertai seluruh siklus hidup aplikasi. Saat membuat objek global, objek JS yang biasa digunakan seperti matematika, string, tanggal, dokumen digunakan sebagai propertinya. Karena objek global ini tidak dapat diakses secara langsung dengan nama, ada jendela properti lain, dan menunjuk jendela ke dirinya sendiri, sehingga objek global dapat diakses melalui jendela. Struktur umum menggunakan kode semu untuk mensimulasikan objek global adalah sebagai berikut:
// Buat Objek Global Var GlobalObject = {Math: {}, String: {}, Date: {}, Dokumen: {}, // Operasi Dom ... Jendela: Ini // Biarkan atribut jendela menunjuk ke dirinya sendiri}Kemudian, mesin JS perlu membangun tumpukan konteks eksekusi. Pada saat yang sama, ia juga perlu menciptakan EC Konteks Eksekusi Global dan mendorong EC EKSEKUSI Global ini ke dalam tumpukan lingkungan eksekusi. Fungsi tumpukan lingkungan eksekusi adalah untuk memastikan bahwa program dapat dieksekusi dalam urutan yang benar. Dalam JavaScript, setiap fungsi memiliki lingkungan eksekusi sendiri. Saat menjalankan fungsi, lingkungan eksekusi fungsi akan didorong ke atas tumpukan lingkungan eksekusi dan mendapatkan hak eksekusi. Ketika fungsi ini dijalankan, lingkungan eksekusi dihapus dari atas tumpukan dan hak eksekusi dikembalikan ke lingkungan eksekusi sebelumnya. Kami menggunakan pseudocode untuk mensimulasikan hubungan antara tumpukan lingkungan eksekusi dan EC:
var ecStack = []; // Tentukan tumpukan lingkungan eksekusi, mirip dengan array var ec = {}; // Buat ruang eksekusi, // Spesifikasi ECMA-262 tidak dengan jelas mendefinisikan struktur data EC, Anda dapat memahaminya sebagai bagian dari ruang yang dialokasikan dalam memori ecstack.push (EC); // Masukkan fungsi dan dorong lingkungan eksekusi ecstack.pop (EC); // Setelah fungsi kembali, hapus lingkungan eksekusiAkhirnya, mesin JS juga membuat objek variabel global (objek varibale) yang terkait dengan EC dan poin VO ke objek global. VO tidak hanya berisi sifat asli dari objek global, tetapi juga mencakup variabel X dan fungsi yang ditentukan secara global. Pada saat yang sama, ketika mendefinisikan fungsi A, ruang lingkup atribut internal juga ditambahkan ke ruang lingkup A dan poin ke VO. Ketika setiap fungsi didefinisikan, atribut lingkup yang terkait dengannya akan dibuat, dan ruang lingkup selalu menunjuk ke lingkungan di mana fungsi didefinisikan. Struktur EcStack saat ini adalah sebagai berikut:
EcStack = [// Eksekusi Lingkungan Stack EC (g) = {// Lingkungan Eksekusi Global VO (g): {// Tentukan objek variabel global ... // berisi atribut asli objek global x = 1; // Tentukan variabel x a = fungsi () {...}; // Tentukan fungsi aa [[scope]] = this; // tentukan ruang lingkup nilai a dan tetapkan untuk vo sendiri}}];2. Eksekusi fungsi a
Saat eksekusi memasuki (1), mesin JS perlu melakukan hal berikut:
Pertama, mesin JS akan menciptakan lingkungan eksekusi EC fungsi A, dan kemudian EC akan mendorongnya ke bagian atas tumpukan lingkungan eksekusi dan mendapatkan hak eksekusi. Pada saat ini, ada dua lingkungan eksekusi di tumpukan lingkungan eksekusi, yaitu lingkungan eksekusi global dan fungsi lingkungan eksekusi. Lingkungan eksekusi A berada di puncak tumpukan, dan lingkungan eksekusi global berada di bagian bawah tumpukan. Kemudian, ciptakan rantai lingkup fungsi A. Dalam JavaScript, setiap lingkungan eksekusi memiliki rantai ruang lingkupnya sendiri untuk resolusi pengidentifikasi. Ketika lingkungan eksekusi dibuat, rantai ruang lingkupnya diinisialisasi sebagai objek yang terkandung dalam ruang lingkup fungsi yang saat ini berjalan.
Selanjutnya, mesin JS akan membuat objek aktif (objek aktivasi) AO dari fungsi saat ini. Objek aktif di sini memainkan peran objek variabel, tetapi disebut secara berbeda dalam fungsi (Anda dapat berpikir bahwa objek variabel adalah konsep umum, dan objek aktif adalah cabangnya). AO berisi parameter formal fungsi, objek argumen, objek ini, serta definisi variabel lokal dan fungsi internal, dan kemudian AO akan didorong ke bagian atas rantai ruang lingkup. Perlu dicatat bahwa ketika mendefinisikan fungsi B, mesin JS juga akan menambahkan atribut ruang lingkup ke B dan titik lingkup titik ke lingkungan di mana fungsi B didefinisikan. Lingkungan di mana fungsi B didefinisikan adalah objek aktif A AO AO, dan AO terletak di ujung depan daftar tertaut. Karena Daftar Tertaut memiliki karakteristik koneksi akhir, ruang lingkup fungsi B menunjuk ke seluruh rantai ruang lingkup A. Mari kita lihat struktur EcStack saat ini:
EcStack = [// Eksekusi Lingkungan Stack EC (a) = {// Lingkungan Eksekusi A [Lingkup]: vo (g), // vo adalah objek variabel global ao (a): {// buat objek aktif y: 1, x: 2, // tentukan variabel lokal x b: function () {...}, // definisi fungsi bb [scope local] [) {{{}, // definisi bb (scope] [) {{{}, // define BB [scope] [) {{{{{{{{), //This refers to AO itself, and AO is at the top of scopeChain, so B[[scope]] points to the entire scope chain arguments:[],//The arguments we access in the function are arguments in AO this:window //This in the function points to the caller window object}, scopeChain:<AO(A),A[[scope]]> //The linked list is initialized as A[[scope]], and Kemudian AO ditambahkan ke bagian atas rantai lingkup. Pada saat ini, rantai lingkup A: ao (a)-> vo (g)}, ec (g) = {// Lingkungan Eksekusi Global VO (g): {// Buat objek variabel global ... // berisi atribut asli dari objek global x = 1; // Tentukan variabel x a = function () {...}; // Tentukan fungsi aa [[scope]] = this; // Tentukan ruang lingkup a, a [scope]] == vo (g)}}];3. Eksekusi fungsi b
Setelah fungsi A dieksekusi, referensi ke B dikembalikan dan ditugaskan ke variabel C. Eksekusi C (1) setara dengan pelaksanaan B (1). Mesin JS perlu menyelesaikan tugas -tugas berikut:
Pertama, seperti di atas, buat lingkungan eksekusi EC fungsi B, dan kemudian dorong EC ke bagian atas tumpukan lingkungan eksekusi dan dapatkan hak eksekusi. Pada saat ini, ada dua lingkungan eksekusi di tumpukan lingkungan eksekusi, yaitu lingkungan eksekusi global dan lingkungan eksekusi fungsi B. Lingkungan eksekusi B berada di puncak tumpukan, dan lingkungan eksekusi global berada di bagian bawah tumpukan. (Catatan: Ketika fungsi A pengembalian, lingkungan eksekusi A akan dihapus dari tumpukan, hanya menyisakan lingkungan eksekusi global) Kemudian, buat rantai ruang lingkup fungsi B dan menginisialisasi ke dalam objek yang terkandung dalam ruang lingkup fungsi B, yaitu rantai ruang lingkup A. Akhirnya, buat objek aktif AO dari fungsi B, dan gunakan parameter z, objek argumen dan objek ini ini. Saat ini, EcStack akan menjadi seperti ini:
EcStack = [// Eksekusi Lingkungan Stack EC (B) = {// Buat lingkungan eksekusi B dan berada di bagian atas rantai lingkup [lingkup]: ao (a), // arahkan ke rantai ruang lingkup fungsi a, ao (a)-> vo (g) var ao (b) = {// Buat objek fungsi aktif b z: 1, 1, 1, [g) var ao (b) = { / / Scopechain: <ao (b), b [[scope]]> // Daftar tertaut diinisialisasi ke B [[SCOPE]], dan kemudian AO (b) ditambahkan ke header daftar yang ditautkan. Pada saat ini, rantai lingkup B: ao (b)-> ao (a) -vo (g)}, ec (a), // lingkungan eksekusi A telah dihapus dari bagian atas tumpukan, EC (g) = {// Lingkungan Eksekusi Global VO: {// Definisikan objek variabel global ... // berisi atribut asli dari objek global X = 1; // Tentukan variabel x a = fungsi () {...}; // Tentukan fungsi aa [[scope]] = this; // Tentukan ruang lingkup a, a [[scope]] == vo (g)}}];Ketika fungsi B mengeksekusi "x+y+z", perlu untuk menguraikan tiga pengidentifikasi x, y, dan z satu per satu. Proses parsing mematuhi aturan pencarian variabel: Pertama temukan apakah atribut ada di objek aktif Anda. Jika ada, berhentilah mencari dan kembali; Jika tidak ada, terus cari dari atas di sepanjang rantai lingkupnya, sampai ditemukan. Jika variabel tidak ditemukan pada seluruh rantai lingkup, kembalikan "tidak ditentukan". Dari analisis di atas, kita dapat melihat bahwa rantai lingkup fungsi B adalah sebagai berikut:
Ao (b)-> ao (a)-> vo (g)
Oleh karena itu, variabel X akan ditemukan di AO (A), dan tidak akan mencari x di VO (g), variabel Y akan ditemukan dalam AO (A), dan variabel z akan ditemukan di AO sendiri (b). Jadi hasil eksekusi: 2+1+1 = 4.
Ringkasan Sederhana
Setelah memahami mekanisme kerja mesin JS, kami tidak bisa hanya tetap pada tingkat pemahaman konsep, tetapi menggunakannya sebagai alat dasar untuk mengoptimalkan dan meningkatkan kode kami dalam pekerjaan aktual, meningkatkan efisiensi eksekusi, dan menghasilkan nilai aktual. Ambil mekanisme pencarian variabel sebagai contoh. Jika kode Anda sangat bersarang dan setiap kali Anda merujuk ke variabel global, mesin JS akan mencari seluruh rantai lingkup. Misalnya, ada masalah ini dengan objek jendela dan dokumen di bagian bawah rantai lingkup. Oleh karena itu, kita dapat melakukan banyak pekerjaan optimasi kinerja di sekitar masalah ini, dan tentu saja ada aspek optimisasi lainnya. Saya tidak akan membahas detailnya di sini. Artikel ini hanya dianggap sebagai tip-off!
oleh @一竞 2015
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.