Sebelumnya, kita mengeksplorasi prinsip kerja JavaScript dari mekanisme parsing mesin JavaScript. Sekarang kita menggunakan contoh yang lebih jelas untuk menggambarkan urutan eksekusi kode JavaScript pada halaman. Jika mekanisme kerja mesin JavaScript relatif mendalam karena termasuk dalam perilaku yang mendasarinya, maka urutan eksekusi kode JavaScript lebih jelas, karena secara intuitif kita dapat merasakan urutan eksekusi kode JavaScript tersebut relatif kompleks, jadi bahasa JavaScript juga perlu dibuat profilnya sebelum mendalaminya.
1.1 Jalankan kode JavaScript sesuai urutan aliran dokumen HTML
Pertama-tama, pembaca harus mengetahui bahwa proses parsing dokumen HTML di browser adalah sebagai berikut: browser secara bertahap mengurai struktur halaman dan informasi dari atas ke bawah sesuai dengan alur dokumen. Kode JavaScript sebagai skrip yang disematkan juga harus diperhitungkan sebagai komponen dokumen HTML, sehingga urutan eksekusi kode JavaScript pada saat pemuatan juga ditentukan berdasarkan urutan kemunculan tag skrip <script>. Misalnya, telusuri halaman dokumentasi di bawah dan Anda akan melihat bahwa kode diurai langkah demi langkah dari atas ke bawah.
Copy kode kodenya sebagai berikut:
<skrip>
alert("Skrip teratas");
</skrip>
<html><kepala>
<skrip>
alert("skrip kepala");
</skrip>
<judul></judul>
</kepala>
<tubuh>
<skrip>
alert("skrip halaman");
</skrip>
</tubuh></html>
<skrip>
alert("skrip paling bawah");
</skrip>
Jika skrip file JavaScript eksternal diimpor melalui atribut src dari tag skrip <script>, maka skrip tersebut juga akan dieksekusi sesuai urutan kemunculan pernyataannya, dan proses eksekusi merupakan bagian dari pemuatan dokumen. Eksekusi tidak akan tertunda karena ini adalah file JavaScript eksternal. Misalnya, pindahkan skrip di area kepala dan badan dokumen di atas ke file JavaScript eksternal, lalu impor melalui atribut src. Melanjutkan pratinjau halaman dokumen, Anda akan melihat urutan eksekusi yang sama.
Copy kode kodenya sebagai berikut:
<skrip>
alert("Skrip teratas");
</skrip>
<html>
<kepala>
<skrip src="//www.VeVB.COm/head.js"></skrip>
<judul></judul>
</kepala>
<tubuh>
<skrip src="//www.VeVB.COm/body.js"></skrip>
</tubuh>
</html>
<skrip>
alert("skrip paling bawah");
</skrip>
1.2 Hubungan antara prakompilasi dan perintah eksekusi
Dalam Javascript, fungsi adalah jenis Javascript pertama. Saat kita menulis suatu fungsi, kita sebenarnya hanya membuat entitas bertipe fungsi.
Seperti yang bisa kita tuliskan dalam bentuk ini:
Copy kode kodenya sebagai berikut:
fungsiHalo()
{
peringatan("Halo");
}
Halo();
varHalo = fungsi()
{
peringatan("Halo");
}
Halo();
Faktanya, semuanya sama. Namun ketika kita memodifikasi fungsinya, kita akan menemukan masalah yang sangat aneh.
Copy kode kodenya sebagai berikut:
<scripttype="teks/javascript">
fungsiHalo() {
peringatan("Halo");
}
Halo();
fungsiHalo() {
peringatan("Halo Dunia");
}
Halo();
</skrip>
Kita akan melihat hasilnya seperti ini: Hello World dikeluarkan dua kali berturut-turut.
Daripada Hello dan Hello World yang kita bayangkan.
Ini karena Javascript tidak sepenuhnya diinterpretasikan dan dieksekusi secara berurutan. Sebaliknya, Javascript "dikompilasi sebelumnya" sebelum interpretasi. Selama proses prakompilasi, fungsi yang ditentukan akan dieksekusi terlebih dahulu dan semua variabel var akan dibuat, nilai defaultnya tidak ditentukan untuk ditingkatkan efisiensi eksekusi program.
Dengan kata lain, potongan kode di atas sebenarnya telah dikompilasi sebelumnya oleh mesin JS ke dalam bentuk ini:
Copy kode kodenya sebagai berikut:
<scripttype="teks/javascript">
varHalo = fungsi() {
peringatan("Halo");
}
Halo = fungsi() {
peringatan("Halo Dunia");
}
Halo();
Halo();
</skrip>
Kita dapat melihat dengan jelas dari kode di atas bahwa fungsi juga merupakan data dan variabel. Kita juga dapat menetapkan (menetapkan kembali) nilai ke "fungsi".
Tentu saja, untuk mencegah situasi ini, kita juga dapat melakukan hal berikut:
Copy kode kodenya sebagai berikut:
<scripttype="teks/javascript">
fungsiHalo() {
peringatan("Halo");
}
Halo();
</skrip>
<scripttype="teks/javascript">
fungsiHalo() {
peringatan("Halo Dunia");
}
Halo();
</skrip>
Dengan cara ini, program ini dibagi menjadi dua bagian, dan mesin JS tidak akan menyatukannya.
Saat mesin JavaScript mem-parsing skrip, ia memproses semua variabel dan fungsi yang dideklarasikan selama prakompilasi.
Lakukan hal berikut:
1. Sebelum eksekusi, operasi yang mirip dengan "prakompilasi" akan dilakukan: pertama, objek aktif di lingkungan eksekusi saat ini akan dibuat, dan variabel yang dideklarasikan dengan var akan ditetapkan sebagai atribut objek aktif, tetapi saat ini , penetapan variabel-variabel ini akan menjadi tidak terdefinisi, dan fungsi-fungsi yang didefinisikan dengan fungsi juga ditambahkan sebagai properti objek aktif, dan nilainya persis dengan definisi fungsi tersebut.
2. Selama fase interpretasi dan eksekusi, ketika suatu variabel perlu diurai, variabel tersebut akan dicari terlebih dahulu dari objek aktif dari lingkungan eksekusi saat ini. Jika tidak ditemukan dan pemilik lingkungan eksekusi memiliki atribut prototipe, itu akan dicari dari rantai prototipe, jika tidak maka akan dicari berdasarkan rantai cakupan. Saat menghadapi pernyataan seperti var a = ..., variabel terkait akan diberi nilai (catatan: penetapan variabel selesai pada tahap interpretasi dan eksekusi. Jika variabel digunakan sebelum ini, nilainya akan menjadi tidak terdefinisi). Oleh karena itu, tampaknya tidak ada kesalahan yang dilaporkan ketika penerjemah JavaScript menjalankan skrip berikut:
Copy kode kodenya sebagai berikut:
peringatan(a); // nilai kembalian tidak ditentukan
var a =1;
peringatan(a); // mengembalikan nilai 1
Karena deklarasi variabel diproses pada waktu prakompilasi, deklarasi tersebut dapat dilihat oleh semua kode selama eksekusi. Namun, Anda juga akan melihat bahwa saat menjalankan kode di atas, nilai yang diminta adalah tidak terdefinisi, bukan 1. Hal ini karena proses inisialisasi variabel terjadi selama eksekusi, bukan pra-kompilasi. Selama eksekusi, penerjemah JavaScript mem-parsing kode secara berurutan. Jika suatu variabel tidak diberi nilai pada baris kode sebelumnya, penerjemah JavaScript akan menggunakan nilai default tidak terdefinisi. Karena variabel a diberi nilai pada baris kedua, baris kode ketiga akan meminta nilai variabel a adalah 1, bukan tidak terdefinisi.
Demikian pula, dalam contoh berikut, memanggil fungsi sebelum fungsi dideklarasikan dan dapat diurai dengan benar adalah sah, sehingga nilai yang dikembalikan adalah 1.
Copy kode kodenya sebagai berikut:
f(); // Memanggil fungsi, mengembalikan nilai 1
fungsi f(){
peringatan(1);
}
Namun, jika fungsinya didefinisikan sebagai berikut, penerjemah JavaScript akan memunculkan kesalahan sintaksis.
Copy kode kodenya sebagai berikut:
f(); // Memanggil fungsi dan mengembalikan kesalahan sintaksis
var f = fungsi(){
peringatan(1);
}
Ini karena fungsi yang didefinisikan dalam contoh di atas hanya ditetapkan ke variabel f sebagai nilai. Oleh karena itu, selama periode pra-kompilasi, penerjemah JavaScript hanya dapat memproses deklarasi variabel f, dan nilai variabel f hanya dapat memproses deklarasi variabel f. ditekan selama periode eksekusi. Jika tugas dilakukan secara berurutan, kesalahan sintaksis akan terjadi secara alami, yang menyebabkan objek f tidak dapat ditemukan.
Sampai jumpa beberapa contoh:
Copy kode kodenya sebagai berikut:
<skrip tipe="teks/javascript">
/*Selama proses prakompilasi, func adalah atribut pada objek aktif di lingkungan jendela, dan nilainya adalah fungsi, yang mencakup nilai yang tidak ditentukan*/
peringatan(fungsi); //fungsi fungsi(){peringatan("halo!")}
var func = "ini adalah variabel"
fungsi fungsi(){
waspada("halo!")
}
/*Selama eksekusi, var ditemui dan ditugaskan kembali ke "ini adalah variabel"*/
peringatan(fungsi); //ini adalah variabel
</skrip>
Copy kode kodenya sebagai berikut:
<skrip tipe="teks/javascript">
var nama = "feng"; fungsi fungsi()
{
/*Pertama, tetapkan nama menjadi tidak terdefinisi di lingkungan func, lalu cari atribut nama objek aktif di lingkungan func selama eksekusi, saat ini, nilainya telah dikompilasi sebelumnya menjadi tidak terdefinisi, sehingga keluarannya tidak terdefinisi, bukan feng */
peringatan(nama); //nama var tidak terdefinisi = "JSF";
peringatan(nama); //JSF
}
fungsi();
peringatan(nama);
//feng
</skrip>
Meskipun deklarasi variabel dan fungsi dapat berada di mana saja dalam dokumen, praktik yang baik adalah mendeklarasikan variabel dan fungsi global sebelum semua kode JavaScript, serta menginisialisasi dan menetapkan variabel. Dalam suatu fungsi, variabel dideklarasikan terlebih dahulu dan kemudian direferensikan.
1.3 Jalankan kode JavaScript dalam blok
Yang disebut blok kode adalah segmen kode yang dipisahkan oleh tag <script>. Misalnya, dua tag <script> di bawah mewakili dua blok kode JavaScript.
Copy kode kodenya sebagai berikut:
<skrip>
// Blok kode JavaScript 1
var a =1;
</skrip>
<skrip>
// blok kode JavaScript 2
fungsi f(){
peringatan(1);
}
</skrip>
Ketika penerjemah JavaScript mengeksekusi sebuah skrip, ia mengeksekusinya dalam bentuk blok. Dalam istilah awam, jika browser menemukan tag <script> saat mengurai aliran dokumen HTML, penerjemah JavaScript akan menunggu hingga blok kode dimuat, pertama-tama kompilasi blok kode terlebih dahulu, lalu jalankan. Setelah eksekusi, browser terus mengurai aliran dokumen HTML di bawah, dan penerjemah JavaScript siap memproses blok kode berikutnya.
Karena JavaScript dieksekusi dalam blok, jika Anda memanggil variabel atau fungsi yang dideklarasikan di blok berikutnya dalam blok JavaScript, kesalahan sintaksis akan muncul. Misalnya, ketika penerjemah JavaScript mengeksekusi kode berikut, ia akan memunculkan kesalahan sintaksis, yang menunjukkan bahwa variabel a tidak terdefinisi dan objek f tidak dapat ditemukan.
Copy kode kodenya sebagai berikut:
<skrip>
// Blok kode JavaScript 1
peringatan(a);
F();
</skrip>
<skrip>
// blok kode JavaScript 2
var a =1;
fungsi f(){
peringatan(1);
}
</skrip>
Meskipun JavaScript dijalankan dalam blok, blok yang berbeda berada dalam cakupan global yang sama, yang berarti bahwa variabel dan fungsi antar blok dapat dibagikan.
1.4 Gunakan mekanisme acara untuk mengubah urutan eksekusi JavaScript
Karena JavaScript memproses kode dalam beberapa bagian dan mengikuti urutan penguraian aliran dokumen HTML, Anda akan melihat kesalahan sintaksis seperti itu pada contoh di atas. Namun ketika aliran dokumen dimuat, kesalahan seperti itu tidak akan terjadi jika diakses lagi. Misalnya, jika kode yang mengakses variabel dan fungsi di blok kode kedua ditempatkan di fungsi peristiwa inisialisasi halaman, tidak akan ada kesalahan sintaksis.
Copy kode kodenya sebagai berikut:
<skrip>
// Blok kode JavaScript 1
window.onload = function(){ // Fungsi penanganan kejadian inisialisasi halaman
peringatan(a);
F();
}
</skrip>
<skrip>
// blok kode JavaScript 2
var a =1;
fungsi f(){
peringatan(1);
}
</skrip>
Demi alasan keamanan, kami biasanya hanya mengizinkan eksekusi kode JavaScript setelah halaman diinisialisasi. Hal ini dapat menghindari dampak kecepatan jaringan pada eksekusi JavaScript, dan juga menghindari pembatasan eksekusi JavaScript yang disebabkan oleh aliran dokumen HTML.
Melihat
Jika ada beberapa event handler windows.onload dalam satu halaman, hanya yang terakhir yang valid. Untuk mengatasi masalah ini, Anda dapat meletakkan semua skrip atau fungsi pemanggil di event handler onload yang sama, misalnya:
Copy kode kodenya sebagai berikut:
jendela.onload = fungsi(){
f1();
f2();
f3();
}
Dan dengan cara ini, urutan eksekusi fungsi dapat diubah hanya dengan menyesuaikan urutan pemanggilan fungsi di event handler onload.
Selain event inisialisasi halaman, kita juga dapat mengubah urutan eksekusi kode JavaScript melalui berbagai event interaktif, seperti event mouse, event keyboard, pemicu jam, dll. Untuk penjelasan lebih detail, silakan merujuk ke Bab 14.
1.5 Urutan eksekusi skrip keluaran JavaScript
Dalam pengembangan JavaScript, metode write() pada objek dokumen sering digunakan untuk menampilkan skrip JavaScript. Jadi bagaimana skrip keluaran dinamis ini dijalankan? Misalnya:
Copy kode kodenya sebagai berikut:
document.write('<script type="text/javascript">');
dokumen.write('f();');
dokumen.write('fungsi f(){');
dokumen.write('alert(1);');
dokumen.tulis('}');
dokumen.write('</script>');
Menjalankan kode di atas, kita akan menemukan bahwa: metode document.write() pertama-tama menulis string skrip keluaran ke lokasi dokumen tempat skrip berada, Setelah menguraikan konten dokumen tempat document.write() berada, browser terus mengurai konten keluaran document.write (), dan kemudian mengurai dokumen HTML berikutnya secara berurutan. Dengan kata lain, string kode yang dikeluarkan oleh skrip JavaScript akan dieksekusi segera setelah dikeluarkan.
Harap perhatikan bahwa keluaran string skrip JavaScript yang menggunakan metode document.write() harus ditempatkan di tag <script> yang dikeluarkan pada saat yang sama. Jika tidak, penerjemah JavaScript tidak akan dapat mengenali kode JavaScript resmi ini dan akan melakukannya ditampilkan sebagai string biasa di dokumen halaman. Misalnya, kode berikut akan menampilkan kode JavaScript alih-alih mengeksekusinya.
Copy kode kodenya sebagai berikut:
dokumen.write('f();');
dokumen.write('fungsi f(){');
dokumen.tulis('peringatan(1);');
dokumen.tulis(');');
Namun, ada risiko tertentu dalam mengeluarkan dan mengeksekusi skrip melalui metode document.write(), karena mesin JavaScript yang berbeda mengeksekusinya dalam urutan yang berbeda, dan bug mungkin terjadi di browser yang berbeda selama penguraian.
Ø Masalah 1: Variabel atau fungsi yang dideklarasikan dalam file JavaScript eksternal yang diimpor melalui metode document.write() tidak dapat ditemukan. Misalnya, lihat contoh kode di bawah ini.
Copy kode kodenya sebagai berikut:
document.write('<script type="text/javascript" src="//www.VeVB.COm/test.js">
</skrip>');
document.write('<script type="text/javascript">');
document.write('alert(n);'); // YAITU memberitahukan bahwa variabel n tidak dapat ditemukan
dokumen.write('</script>');
alert(n+1); // Semua browser akan menanyakan bahwa variabel n tidak dapat ditemukan
Kode file JavaScript eksternal (test.js) adalah sebagai berikut:
Copy kode kodenya sebagai berikut:
var n = 1;
Saat diuji di browser yang berbeda, Anda akan menemukan kesalahan sintaksis dan variabel n tidak dapat ditemukan. Dengan kata lain, jika Anda mengakses dalam blok kode JavaScript variabel yang terkandung dalam file JavaScript eksternal yang diimpor dalam keluaran skrip dengan metode document.write() di blok kode ini, kesalahan sintaksis akan ditampilkan. Pada saat yang sama, jika di browser IE, tidak hanya di skrip, tetapi juga di skrip keluaran, ini akan meminta bahwa variabel keluaran yang diimpor ke file JavaScript eksternal tidak dapat ditemukan (ekspresinya agak panjang dan berbelit-belit, bagi pembaca yang belum paham bisa mencoba menjalankan kode diatas agar bisa dipahami).
Ø Pertanyaan 2: Mesin JavaScript yang berbeda memiliki perintah eksekusi yang sedikit berbeda untuk skrip impor eksternal keluaran. Misalnya, lihat contoh kode di bawah ini.
Copy kode kodenya sebagai berikut:
<skrip tipe="teks/javascript">
document.write('<script type="text/javascript" src="http://shaozhuqing.com/test1.js">
</skrip>');
document.write('<script type="text/javascript">');
dokumen.tulis('peringatan(2);')
dokumen.write('alert(n+2);');
dokumen.write('</script>');
</skrip>
<skrip tipe="teks/javascript">
peringatan(n+3);
</skrip>
Kode untuk file JavaScript eksternal (test1.js) ditunjukkan di bawah.
Copy kode kodenya sebagai berikut:
var n = 1;
peringatan(n);
Urutan eksekusi di browser IE ditunjukkan pada Gambar 1-6.
Gambar 1-6 Urutan eksekusi dan kesalahan sintaksis yang diminta oleh browser IE 7
Urutan eksekusi di browser yang mematuhi standar DOM berbeda dengan di browser IE, dan tidak ada kesalahan sintaksis. Gambar 1-7 menunjukkan urutan eksekusi di browser Firefox 3.0.
Gambar 1-7 Urutan eksekusi browser Firefox 3 dan kesalahan sintaksis yang diminta
Selesaikan perintah eksekusi yang berbeda dari browser yang berbeda dan kemungkinan bug. Kita dapat menempatkan semua file eksternal yang diimpor menggunakan skrip keluaran dalam blok kode independen, sehingga masalah ini dapat dihindari sesuai dengan urutan eksekusi blok kode JavaScript yang diperkenalkan di atas. Misalnya untuk contoh di atas, Anda bisa mendesainnya seperti ini:
Copy kode kodenya sebagai berikut:
<skrip tipe="teks/javascript">
document.write('<script type="text/javascript" src="//www.VeVB.COm/test1.js"></script>');
</skrip>
<skrip tipe="teks/javascript">
document.write('<script type="text/javascript">');
dokumen.write('alert(2);') ; // Tip 2
dokumen.write('alert(n+2);'); // Tip 3
dokumen.write('</script>');
peringatan(n+3); // Tip 4
</skrip>
<skrip tipe="teks/javascript">
peringatan(n+4); // Tip 5
</skrip>
Dengan cara ini, kode di atas dapat dieksekusi secara berurutan di browser yang berbeda, dan urutan keluarannya adalah 1, 2, 3, 4, dan 5. Alasan masalahnya adalah: kontradiksi antara skrip keluaran yang diimpor dan blok kode JavaScript saat ini. Jika dikeluarkan secara terpisah, tidak akan ada konflik.