Apa hasil dari program berikut?
Copy kode kodenya sebagai berikut:
var foo = 1;
bilah fungsi() {
jika (!foo) {
var foo = 10;
}
waspada(foo);
}
batang();
Hasilnya adalah 10;
Bagaimana dengan yang ini?
Copy kode kodenya sebagai berikut:
var a = 1;
fungsi b() {
sebuah = 10;
kembali;
fungsi a() {}
}
B();
peringatan(a);
Hasilnya adalah 1.
Apakah itu membuatmu takut? Apa yang telah terjadi? Ini mungkin aneh, berbahaya, dan membingungkan, namun sebenarnya ini juga merupakan fitur bahasa JavaScript yang sangat berguna dan mengesankan. Saya tidak tahu apakah ada nama standar untuk perilaku ini, tapi saya suka istilah ini: "Mengangkat". Artikel ini akan memberikan penjelasan pendahuluan tentang mekanisme ini, tetapi pertama-tama mari kita memahami beberapa hal yang diperlukan tentang ruang lingkup JavaScript.
Cakupan Javascript
Untuk pemula Javascript, salah satu bidang yang paling membingungkan adalah cakupan; sebenarnya, ini bukan hanya pemula. Saya telah bertemu dengan beberapa programmer JavaScript berpengalaman, tetapi mereka tidak memahami ruang lingkup secara mendalam. Alasan mengapa cakupan JavaScript membingungkan adalah karena sintaks program itu sendiri terlihat seperti bahasa keluarga C, seperti program C berikut:
Copy kode kodenya sebagai berikut:
#termasuk <stdio.h>
ke dalam utama() {
ke dalam x = 1;
printf("%d, ",x); // 1
jika (1) {
ke dalam x = 2;
printf("%d, ",x); // 2
}
printf("%d/n",x); // 1
}
Hasil outputnya adalah 1 2 1. Hal ini karena bahasa keluarga C mempunyai cakupan blok. Ketika kontrol program memasuki suatu blok, seperti blok if, variabel yang hanya mempengaruhi blok tersebut dapat dideklarasikan tanpa mempengaruhi efek di luar blok. domain. Namun dalam Javascript, ini tidak berhasil. Lihatlah kode di bawah ini:
Copy kode kodenya sebagai berikut:
var x = 1;
konsol.log(x); // 1
jika (benar) {
var x = 2;
konsol.log(x); // 2
}
konsol.log(x); // 2
Hasilnya adalah 1 2 2. Karena javascript adalah cakupan fungsi. Ini adalah perbedaan terbesar dari rumpun bahasa C. If dalam program ini tidak membuat cakupan baru.
Bagi banyak programmer C, C++, dan Java, hal ini bukanlah hal yang mereka harapkan dan sambut. Untungnya, karena fleksibilitas fungsi JavaScript, ada cara untuk mengatasi hal ini. Jika Anda harus membuat cakupan sementara, lakukan sesuatu seperti ini:
Copy kode kodenya sebagai berikut:
fungsi foo() {
var x = 1;
jika (x) {
(fungsi () {
var x = 2;
// beberapa kode lainnya
}());
}
// x masih 1.
}
Cara ini fleksibel dan dapat digunakan dimanapun Anda ingin membuat scope sementara. Tidak hanya di dalam blok. Namun, saya sangat menyarankan Anda meluangkan waktu untuk memahami pelingkupan JavaScript. Ini sangat berguna dan salah satu fitur JavaScript favorit saya. Jika Anda memahami ruang lingkup, maka pengangkatan variabel akan lebih masuk akal bagi Anda.
Deklarasi variabel, penamaan, dan promosi
Dalam JavaScript, ada 4 cara dasar variabel memasuki cakupan:
•1 Bahasa bawaan: semua cakupan memiliki this dan argumen (Catatan Penerjemah: Setelah pengujian, argumen tidak terlihat dalam cakupan global)
•2 Parameter formal: Parameter formal suatu fungsi akan menjadi bagian dari ruang lingkup badan fungsi;
•3 Deklarasi fungsi: seperti bentuk ini: function foo(){};
•4 Deklarasi variabel: seperti ini: var foo;
Deklarasi fungsi dan deklarasi variabel selalu diam-diam "diangkat" ke bagian atas badan metode oleh penerjemah. Artinya, kode seperti berikut:
Copy kode kodenya sebagai berikut:
fungsi foo() {
batang();
var x = 1;
}
sebenarnya akan diartikan sebagai:
Copy kode kodenya sebagai berikut:
fungsi foo() {
varx;
batang();
x = 1;
}
Terlepas dari apakah blok tempat variabel didefinisikan dapat dieksekusi. Dua fungsi berikut ini sebenarnya sama:
Copy kode kodenya sebagai berikut:
fungsi foo() {
jika (salah) {
var x = 1;
}
kembali;
var y = 1;
}
fungsi foo() {
var x, y;
jika (salah) {
x = 1;
}
kembali;
kamu = 1;
}
Perhatikan bahwa penetapan variabel tidak diangkat, hanya deklarasi. Namun, deklarasi fungsinya sedikit berbeda, dan isi fungsi juga dipromosikan. Namun perlu diketahui bahwa ada dua cara untuk mendeklarasikan suatu fungsi:
Copy kode kodenya sebagai berikut:
tes fungsi() {
foo(); // TypeError "foo bukan fungsi"
bar(); // "ini akan berjalan!"
var foo = function () {// variabel menunjuk ke ekspresi fungsi
alert("ini tidak akan berjalan!");
}
function bar() { // Deklarasi fungsi fungsi bernama bar
alert("ini akan berjalan!");
}
}
tes();
Dalam contoh ini, hanya deklarasi fungsional yang diangkat bersama dengan isi fungsi. Deklarasi foo akan diangkat, tetapi isi fungsi yang ditunjuknya hanya akan ditetapkan selama eksekusi.
Penjelasan di atas mencakup beberapa dasar-dasar peningkatan, dan sepertinya tidak terlalu membingungkan. Namun, dalam beberapa skenario khusus, masih terdapat tingkat kerumitan tertentu.
Urutan penguraian variabel
Hal terpenting yang perlu diingat adalah urutan resolusi variabel. Ingat 4 cara penamaan memasuki cakupan yang saya berikan sebelumnya? Urutan penguraian variabel adalah urutan saya mencantumkannya.
Copy kode kodenya sebagai berikut:
<skrip>
fungsi a(){
}
var a;
alert(a);//Mencetak isi fungsi a
</skrip>
<skrip>
var a;
fungsi a(){
}
alert(a);//Mencetak isi fungsi a
</skrip>
//Tetapi perhatikan perbedaan antara dua metode penulisan berikut:
<skrip>
var a=1;
fungsi a(){
}
peringatan(a);//Cetak 1
</skrip>
<skrip>
fungsi a(){
}
var a=1;
peringatan(a);//Cetak 1
</skrip>
Ada 3 pengecualian di sini:
1 Argumen nama bawaan berperilaku aneh. Tampaknya argumen tersebut harus dideklarasikan setelah parameter formal fungsi, tetapi sebelum deklarasi fungsi. Artinya, jika ada argumen dalam parameter formal, argumen tersebut akan diprioritaskan daripada argumen bawaan. Ini adalah fitur yang sangat buruk, jadi hindari penggunaan argumen dalam parameter formal;
2 Mendefinisikan variabel ini di mana saja akan menyebabkan kesalahan sintaksis, yang merupakan fitur bagus;
3. Jika beberapa parameter formal memiliki nama yang sama, parameter terakhir memiliki prioritas, meskipun nilainya tidak ditentukan selama operasi sebenarnya;
bernama fungsi
Anda dapat memberi nama pada suatu fungsi. Jika demikian, ini bukan deklarasi fungsi, dan nama fungsi yang ditentukan (jika ada, seperti spam di bawah, catatan penerjemah) dalam definisi isi fungsi tidak akan dipromosikan, tetapi diabaikan. Berikut ini beberapa kode untuk membantu Anda memahami:
Copy kode kodenya sebagai berikut:
foo(); // TypeError "foo bukan fungsi"
bar(); // sah
baz(); // TypeError "baz bukan fungsi"
spam(); //ReferenceError "spam tidak ditentukan"
var foo = function() {}; // foo menunjuk ke fungsi anonim
bilah fungsi() {}; // deklarasi fungsi
var baz = function spam() {}; // Fungsi yang diberi nama, hanya baz yang dipromosikan, spam tidak akan dipromosikan.
foo(); // sah
bar(); // sah
baz(); // sah
spam(); //ReferenceError "spam tidak ditentukan"
Cara menulis kode
Sekarang setelah Anda memahami pelingkupan dan pengangkatan variabel, apa artinya ini bagi pengkodean JavaScript? Yang paling penting adalah selalu mendefinisikan variabel Anda dengan var. Dan saya sangat menyarankan bahwa untuk sebuah nama, harus selalu hanya ada satu deklarasi var dalam suatu cakupan. Jika Anda melakukan ini, Anda tidak akan mengalami masalah cakupan dan pengangkatan variabel.
Apa yang Anda maksud dengan spesifikasi bahasa?
Saya menemukan dokumentasi referensi ECMAScript selalu berguna. Inilah yang saya temukan tentang cakupan dan pengangkatan variabel:
Jika suatu variabel dideklarasikan dalam kelas isi fungsi, maka itu adalah cakupan fungsi. Jika tidak, cakupannya bersifat global (sebagai properti global). Variabel akan dibuat ketika eksekusi memasuki ruang lingkup. Blok tidak akan mendefinisikan cakupan baru, hanya deklarasi fungsi dan prosedur (penerjemah menganggapnya sebagai eksekusi kode global) yang akan membuat cakupan baru. Variabel diinisialisasi ke tidak terdefinisi saat dibuat. Jika terdapat operasi penugasan dalam pernyataan deklarasi variabel, operasi penugasan hanya akan terjadi saat dijalankan, bukan saat dibuat.
Saya harap artikel ini dapat memberikan pencerahan bagi para programmer yang bingung tentang JavaScript. Saya juga mencoba yang terbaik untuk menghindari kebingungan lebih lanjut. Jika saya mengatakan sesuatu yang salah atau melewatkan sesuatu, harap beri tahu saya.
Suplemen penerjemah
Seorang teman mengingatkan saya tentang masalah promosi fungsi bernama dalam lingkup global di bawah IE:
Beginilah cara saya mengujinya ketika saya menerjemahkan artikel:
Copy kode kodenya sebagai berikut:
<skrip>
fungsit(){
spam();
var baz = fungsi spam() {peringatan('ini spam')};
}
T();
</skrip>
Cara penulisan ini, yaitu promosi fungsi bernama dalam lingkup non-global, memiliki kinerja yang sama di bawah ie dan ff.
Copy kode kodenya sebagai berikut:
<skrip>
spam();
var baz = fungsi spam() {peringatan('ini spam')};
</skrip>
Kemudian spam dapat dieksekusi di bawah ie, tetapi tidak di bawah ff. Hal ini menunjukkan bahwa browser yang berbeda menangani detail ini secara berbeda.
Pertanyaan ini juga membuat saya memikirkan dua pertanyaan lainnya, 1: Untuk variabel yang memiliki cakupan global, ada perbedaan antara var dan non-var. Tanpa var, variabel tersebut tidak akan dipromosikan. Misalnya, dari dua program berikut, program kedua akan melaporkan kesalahan:
Copy kode kodenya sebagai berikut:
<skrip>
peringatan(a);
var a=1;
</skrip>
Copy kode kodenya sebagai berikut:
<skrip>
peringatan(a);
sebuah=1;
</skrip>
2: Variabel lokal yang dibuat di eval tidak akan dipromosikan (tidak ada cara untuk melakukannya).
Copy kode kodenya sebagai berikut:
<skrip>
var a = 1;
fungsi t(){
peringatan(a);
eval('var a = 2');
peringatan(a);
}
T();
peringatan(a);
</skrip>