Saya menonton latihan ES2015 baru -baru ini, ada pepatah di dalamnya yang mengatakan
Tidak ada ruang lingkup level blok di JavaScript
Anda mungkin tidak memahami masalah ini, mari kita lihat contoh terlebih dahulu
var a = [] untuk (var i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] ();Saya pikir banyak orang berpikir hasil dari pertanyaan ini adalah 6, tetapi sayangnya, jawabannya adalah 10. Cobalah sesuatu yang lain. A [7] (), A [8] (), dan A [8] () semuanya memiliki 10 !!
Karena JS sering membungkus variabel primitif menjadi objek yang sesuai saat memproses, misalnya, untuk var str = "hello world"; str.slice (1).
Proses nyata JS mungkin adalah var str = "Hello World"; New String (str) .slice (1). Proses seperti itu dapat menyebabkan masalah bagi kita untuk memahami masalahnya.
Untuk menjelaskan masalah ini di sini, saya termasuk jenis angka dalam tipe primitif, saya secara eksplisit menyatakannya sebagai jenis angka. Karena proses penugasan dari tipe dasar adalah untuk menampilkan kembali memori dan memodifikasi arah variabel, kami juga menggunakan proses objek nomor baru untuk mensimulasikan proses ini. Kode yang dimodifikasi adalah sebagai berikut:
var a = [] var i = angka baru (0); untuk (; i <10; i = angka baru (i+1)) {a [i] = function () {console.log (i.toString ()); }} a [6] (); // 10a [7] (); // 10a [8] (); // 10a [9] (); // 10a [9] (); // 10Mari kita lihat alamat memori relatif dari variabel -variabel ini dalam kombinasi dengan program.
(function () {var id = 0; function generateId () {return id ++;}; object.prototype.id = function () {var newId = generateId (); this.id = function () {return newID;}; return newiD;};}) (); var a = [] var i = bilangan newId (0); console;};}) (); var a = [] var i = bilangan baru (0); console; nomor baru (i+1), i.id ()) {a [i] = function () {console.log (i.id ()); console.log (i.tostring ()); }} a [6] (); // 10 10a [7] (); // 10 10a [8] (); // 10 10a [9] (); // 10 10console.log (i.id ()) // 10Di sini kami memang telah mensimulasikan seluruh efek "penugasan" dari I kami, dan alamat relatif saya berubah dari 0 menjadi 10 (pada akhirnya, perlu ditambahkan sekali sebelum dapat melompat keluar dari loop untuk).
Saat melihat alamat relatif I, kami menemukan masalah: ketika fungsi yang sesuai dengan [x] (x: 0 ~ 9) dieksekusi, alamat relatif yang saya referensikan adalah 10. Mengapa?
Di sini kita akan melibatkan masalah lingkup level blok. Di sini kami mengutip bagian dari Ruan Yifeng dalam pengantar ES6:
ES5 hanya memiliki ruang lingkup global dan ruang lingkup fungsi, tetapi tidak ada ruang lingkup level blok.
ES5 adalah versi JS yang paling banyak digunakan. Kalimat ini mengatakan bahwa dalam JavaScript, tidak ada lingkup blok. Hanya ada ruang lingkup global dan cakupan level blok.
Bagaimana cara memahami? Misalnya
untuk (var i = 0; i <10; i ++) {console.log (i);} console.log (i); // 10console.log (window.i); // 10Secara intuitif, kami berpikir bahwa for loop adalah blok kode dan harus menjadi bagian dari ruang lingkup level blok. Namun, tidak hanya output 0 hingga 9 secara normal, tetapi juga dapat menghasilkan 10 secara eksternal di loop untuk. Pada saat yang sama, kami menemukan bahwa meskipun kami mendefinisikan saya pada loop untuk, tampaknya saya tergantung pada objek jendela global (jika itu adalah lingkungan eksekusi nodeJs, itu akan tergantung pada objek global)
Oleh karena itu, blok seperti untuk loop di JavaScript tidak akan memiliki efek ruang lingkup level blok. Mendefinisikan variabel dalam blok kode seperti untuk loop tidak berbeda dari variabel yang mendefinisikan langsung dalam ruang lingkup saat ini.
Tapi kita bisa mengisolasi ruang lingkup melalui fungsi:
(function () {for (var i = 0; i <10; i ++) {console.log (i);} console.log (i);}) () console.log (i); /// i tidak didefinisikanPada saat yang sama, jika console.log (window.i); dieksekusi, hasil yang tidak ditentukan akan diperoleh. Di sini kami menggunakan fungsi eksekusi langsung untuk membentuk ruang lingkup. Ini memainkan peran yang mirip dengan blok kode. Setelah lingkup fungsi ini, variabel yang saya tidak dapat lagi diakses. Namun, saya dapat diakses kapan saja dalam lingkup fungsi.
Kembali ke pertanyaan sebelumnya, dan kemudian kita akan memahaminya dalam kombinasi dengan hanya ruang lingkup global dan ruang lingkup level blok dalam JavaScript. Dalam loop untuk, yang saya tentukan harus ditentukan dalam ruang lingkup saat ini, yaitu, lingkup jendela. Di badan loop, kami menetapkan fungsi ke [i]. Ketika kami menjalankan fungsi ini, situasinya adalah sebagai berikut:
Saya tidak ada dalam fungsi, jadi kami mengikuti rantai ruang lingkup untuk menemukan i. Output I kami saat ini adalah saya. Karena saya melompat keluar dari +1 terakhir dari loop, saya menjadi 10, jadi hasil output selalu 10. Tapi yang sebenarnya kita butuhkan bukanlah yang terakhir, tetapi saya dalam proses perantara. Jika kita ingin menyelesaikan masalah ini, kita perlu mengesampingkan variabel I (karena yang terakhir saya pasti menjadi 10). Kita perlu membiarkan fungsi yang sesuai dengan [0] merujuk pada nilai 0, dan membiarkan fungsi yang sesuai dengan [1] merujuk pada nilai 1. Seperti yang ditunjukkan pada gambar di bawah ini:
Kembali ke kode kami sebelumnya.
Panah pada gambar menunjukkan bahwa kita dapat mengakses I (0 ~ 9). Di sini, karena loop untuk tidak membentuk ruang lingkup level blok dengan sendirinya, kami mengakses yang saya tentukan oleh loop untuk ketika kita mengikuti rantai ruang lingkup.
Di sini kami membungkus kode kami dengan fungsi eksekusi langsung, yang dapat membentuk ruang lingkup, dan kami memberikan nilai I untuk itu. Berikut:
var a = [] var i = angka baru (0); console.log (i.id ()); // 0for (; i <10; i = angka baru (i+1), i.id ()) {(fungsi (i) {a [i] = fungsi () {console.log (i.id ()); console.log (i.tostring (); // 6 6a [7] (); // 7 7a [8] (); // 8 8a [9] (); // 9 9console.log (i.id ()); // 10}Karena fungsi eksekusi langsung ini mengacu pada nilai numerik 0 ~ 9, ketika kami menjalankan fungsi A [i], pertama -tama kita akan menemukan ruang lingkup fungsi eksekusi langsung di sepanjang rantai lingkup. Fungsi eksekusi langsung mempertahankan referensi numerik dari 0 ~ 9, dan kami dapat dengan benar menghasilkan nilai i dalam fungsi a [i]. Melalui hasil eksekusi, kita dapat melihat bahwa tidak hanya hasil eksekusi yang benar, tetapi alamat memori relatif dari nilai yang kami referensi juga benar. Kemudian kami mengubah objek angka yang awalnya dinyatakan secara eksplisit untuk pengujian. Sebagai berikut:
var a = []; untuk (var i = 0; i <10; i ++) {(function (i) {a [i] = function () {console.log (i);}}) (i);}Akhirnya, mari kita lihat sintaks ES6 yang direkomendasikan menggunakan Let alih -alih VAR dan menyusun dan menghasilkan kode ES5 melalui Baable:
// kode ES6 var a = [] untuk (biarkan i = 0; i <10; i ++) {a [i] = function () {console.log (i); }} a [6] (); // Babel dikumpulkan dan dihasilkan kode ES5 "Gunakan ketat"; var a = []; var _loop = fungsi _loop (i) {a [i] = fungsi () {console.log (i); };}; untuk (var i = 0; i <10; i ++) {_loop (i);} a [6] ();Mari kita lihat apakah solusi kami sangat mirip dengan solusi ES6. Di sini fungsi eksekusi langsung kami setara dengan eksekusi fungsi _loop dan _loop (i) dalam kode ES5 yang dihasilkan.