Perbedaan antara sintaks deklarasi variabel dalam Javascript (termasuk variabel global)?


292

Apakah ada perbedaan antara mendeklarasikan variabel:

var a=0; //1

...cara ini:

a=0; //2

...atau:

window.a=0; //3

dalam lingkup global?


2
AFAIK var a = 0; tidak berfungsi di IE ketika mengakses variabel melalui file js eksternal lain yang dideklarasikan dalam file js lain
Aivan Monceller

Saya tidak tahu tentang window.a tetapi 2 cara lainnya sama dalam lingkup global.
programmer

1
@AivanMonceller sungguh? tolong tautkan.
Raynos

@ Raynos, saya mengalaminya di situs web saya sendiri. IE6 lebih spesifik. Saya tidak bisa membuat var enum saya muncul yang ada di file js eksternal dan saya
merujuknya

@ Ashwini Di lingkup global, jendela adalah objek global (di peramban). var a = 1; console.log (a); console.log (win
leebriggs

Jawaban:


557

Ya, ada beberapa perbedaan, meskipun secara praktis mereka biasanya tidak besar.

Ada cara keempat, dan pada ES2015 (ES6) ada dua lagi. Saya telah menambahkan cara keempat di akhir, tetapi memasukkan cara ES2015 setelah # 1 (Anda akan melihat alasannya), jadi kami memiliki:

var a = 0;     // 1
let a = 0;     // 1.1 (new with ES2015)
const a = 0;   // 1.2 (new with ES2015)
a = 0;         // 2
window.a = 0;  // 3
this.a = 0;    // 4

Pernyataan itu dijelaskan

# 1 var a = 0;

Ini menciptakan variabel global yang juga merupakan properti dari objek global , yang kami akses seperti windowpada browser (atau melalui thislingkup global, dalam kode non-ketat). Tidak seperti beberapa properti lain, properti tidak dapat dihapus melalui delete.

Dalam istilah spesifikasi, ini menciptakan pengidentifikasi yang mengikat pada objek Catatan Lingkungan untuk lingkungan global . Itu membuatnya menjadi properti dari objek global karena objek global adalah tempat pengikat ikatan untuk objek lingkungan global Catatan Lingkungan diadakan. Inilah sebabnya mengapa properti tidak dapat dihapus: Ini bukan hanya properti sederhana, ini adalah pengikat pengikat.

Pengikatan (variabel) didefinisikan sebelum baris pertama kode dijalankan (lihat "Ketika varterjadi" di bawah).

Perhatikan bahwa pada IE8 dan sebelumnya, properti dibuat pada windowtidak enumerable (tidak muncul di for..inlaporan). Di IE9, Chrome, Firefox, dan Opera, ini dapat dihitung.


# 1.1 let a = 0;

Ini menciptakan variabel global yang bukan properti dari objek global. Ini adalah hal baru pada ES2015.

Dalam istilah spesifikasi, ini menciptakan pengidentifikasi yang mengikat pada Catatan Lingkungan deklaratif untuk lingkungan global daripada objek Catatan Lingkungan. Lingkungan global adalah unik dalam memiliki split Lingkungan Record, satu untuk semua barang-barang lama yang terjadi di obyek global (yang objek Lingkungan Record) dan satu lagi untuk semua barang baru ( let, const, dan fungsi-fungsi yang dibuat oleh class) yang tidak pergi pada objek global.

Ikatan dibuat sebelum kode langkah-demi-langkah di blok terlampir dijalankan (dalam hal ini, sebelum kode global dijalankan), tetapi tidak dapat diakses dengan cara apa pun sampai eksekusi langkah-demi-langkah mencapai letpernyataan. Setelah eksekusi mencapai letpernyataan, variabel dapat diakses. (Lihat "Kapan letdan constterjadi" di bawah.)


# 1.2 const a = 0;

Menciptakan konstanta global, yang bukan properti dari objek global.

constpersis seperti letkecuali bahwa Anda harus memberikan inisialisasi ( = valuebagian), dan Anda tidak dapat mengubah nilai konstanta setelah itu dibuat. Di bawah selimut, persis seperti lettetapi dengan bendera pada pengenal yang mengikat mengatakan nilainya tidak dapat diubah. Menggunakan constmelakukan tiga hal untuk Anda:

  1. Membuatnya kesalahan parse-time jika Anda mencoba untuk menetapkan konstanta.
  2. Dokumen sifatnya tidak berubah untuk programmer lain.
  3. Biarkan mesin JavaScript mengoptimalkan berdasarkan hal itu tidak akan berubah.

# 2 a = 0;

Ini menciptakan properti pada objek global secara implisit . Karena ini properti biasa, Anda dapat menghapusnya. Saya sarankan tidak melakukan ini, tidak jelas bagi siapa pun yang membaca kode Anda nanti. Jika Anda menggunakan mode ketat ES5, melakukan ini (menugaskan ke variabel tidak ada) adalah kesalahan. Itu salah satu dari beberapa alasan untuk menggunakan mode ketat.

Dan yang menarik, lagi pada IE8 dan sebelumnya, properti yang dibuat tidak dapat dihitung (tidak muncul dalam for..inpernyataan). Itu aneh, terutama diberi # 3 di bawah ini.


# 3 window.a = 0;

Ini menciptakan properti pada objek global secara eksplisit, menggunakan windowglobal yang merujuk pada objek global (pada browser; beberapa lingkungan non-browser memiliki variabel global yang setara, seperti globalpada NodeJS). Karena ini properti biasa, Anda dapat menghapusnya.

Properti ini enumerable, pada IE8 dan sebelumnya, dan pada setiap browser lain yang pernah saya coba.


# 4 this.a = 0;

Persis seperti # 3, kecuali kita mereferensikan objek global melalui thisalih-alih global window. Ini tidak akan bekerja dalam mode ketat, karena dalam mode global kode ketat, thistidak memiliki referensi ke objek global (ia memiliki nilai undefinedsebagai gantinya).


Menghapus properti

Apa yang saya maksud dengan "menghapus" atau "menghapus" a? Tepatnya: Menghapus properti (seluruhnya) melalui deletekata kunci:

window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"

deletesepenuhnya menghapus properti dari objek. Anda tidak dapat melakukannya dengan properti yang ditambahkan windowsecara tidak langsung melalui var, deletediabaikan secara diam-diam atau melempar pengecualian (tergantung pada implementasi JavaScript dan apakah Anda dalam mode ketat).

Peringatan : IE8 lagi (dan mungkin sebelumnya, dan IE9-IE11 dalam mode "kompatibilitas" yang rusak): Itu tidak akan membiarkan Anda menghapus properti windowobjek, bahkan ketika Anda seharusnya diizinkan. Lebih buruk, itu melempar pengecualian ketika Anda mencoba ( coba percobaan ini di IE8 dan di browser lain). Jadi ketika menghapus dari windowobjek, Anda harus bersikap defensif:

try {
    delete window.prop;
}
catch (e) {
    window.prop = undefined;
}

Itu mencoba untuk menghapus properti, dan jika pengecualian dilemparkan itu melakukan hal terbaik berikutnya dan menyetel properti undefined.

Ini hanya berlaku untuk windowobjek, dan hanya (sejauh yang saya tahu) ke IE8 dan sebelumnya (atau IE9-IE11 dalam mode "kompatibilitas" yang rusak). Browser lain baik-baik saja dengan menghapus windowproperti, tunduk pada aturan di atas.


Kapan varterjadi

Variabel didefinisikan melalui varpernyataan yang dibuat sebelum setiap langkah-demi-langkah kode dalam konteks eksekusi dijalankan, dan properti ada juga sebelum itu varpernyataan.

Ini bisa membingungkan, jadi mari kita lihat:

display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar);          // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar);          // displays "b"

Contoh langsung:

Seperti yang Anda lihat, simbol foodidefinisikan sebelum baris pertama, tetapi simbol baritu tidak. Di mana var foo = "f";pernyataan itu, sebenarnya ada dua hal: mendefinisikan simbol, yang terjadi sebelum baris pertama kode dijalankan; dan melakukan penugasan untuk simbol itu, yang terjadi di mana garis berada dalam aliran langkah demi langkah. Ini dikenal sebagai " varmengangkat" karena var foobagian tersebut dipindahkan ("diangkat") ke bagian atas ruang lingkup, tetapi foo = "f"bagian tersebut dibiarkan di lokasi aslinya. (Lihat Miskin disalahpahamivar di blog kecil anemia saya.)


Kapan letdan constterjadi

letdan constberbeda vardalam beberapa hal. Cara yang relevan dengan pertanyaan adalah bahwa meskipun ikatan yang mereka tetapkan dibuat sebelum kode langkah-demi-langkah berjalan, itu tidak dapat diakses sampai pernyataan letatau consttercapai.

Jadi sementara ini berjalan:

display(a);    // undefined
var a = 0;
display(a);    // 0

Ini menimbulkan kesalahan:

display(a);    // ReferenceError: a is not defined
let a = 0;
display(a);

Dua cara lain yang letdan constberbeda var, yang tidak benar-benar relevan dengan pertanyaan, adalah:

  1. varselalu berlaku untuk seluruh konteks eksekusi (seluruh kode global, atau seluruh kode fungsi pada fungsi yang muncul), tetapi letdan consthanya berlaku di dalam blok tempat mereka muncul. Artinya, varmemiliki lingkup fungsi (atau global), tetapi letdan constmemiliki lingkup blok.

  2. Mengulangi var adalam konteks yang sama tidak berbahaya, tetapi jika Anda memiliki let a(atau const a), memiliki lain let aatau const aatau var amerupakan kesalahan sintaks.

Berikut ini contoh yang menunjukkan hal itu letdan constsegera berlaku di blok mereka sebelum kode apa pun di dalam blok itu berjalan, tetapi tidak dapat diakses hingga pernyataan letatau const:

var a = 0;
console.log(a);
if (true)
{
  console.log(a); // ReferenceError: a is not defined
  let a = 1;
  console.log(a);
}

Perhatikan bahwa yang kedua console.loggagal, alih-alih mengakses adari luar blok.


Di luar topik: Hindari mengacaukan objek global ( window)

The windowobjek akan sangat, sangat penuh dengan sifat. Kapan pun memungkinkan, sangat disarankan untuk tidak menambah kekacauan. Sebagai gantinya, bungkus simbol Anda dalam paket kecil dan ekspor paling banyak satu simbol ke windowobjek. (Saya sering tidak mengekspor simbol apa pun ke windowobjek.) Anda dapat menggunakan fungsi untuk memuat semua kode Anda agar mengandung simbol Anda, dan fungsi itu bisa anonim jika Anda suka:

(function() {
    var a = 0; // `a` is NOT a property of `window` now

    function foo() {
        alert(a);   // Alerts "0", because `foo` can access `a`
    }
})();

Dalam contoh itu, kita mendefinisikan suatu fungsi dan langsung menjalankannya ( ()akhir).

Fungsi yang digunakan dengan cara ini sering disebut fungsi pelingkupan . Fungsi didefinisikan dalam fungsi scoping variabel akses dapat didefinisikan dalam fungsi scoping karena mereka penutupan atas data itu (lihat: Penutup tidak rumit di blog sedikit anemia saya).


dapat saya lakukan window['a']=0untuk membuatnya jelas saya menggunakan jendela sebagai peta? Apakah windowspesial sehingga beberapa browser tidak mengizinkan ini dan memaksa saya untuk menggunakan window.a?
Jayen

Satu catatan di # 3 yang mungkin perlu diklarifikasi: window.a = 0;hanya berfungsi di lingkungan peramban, dan hanya dengan konvensi. Mengikat objek global ke variabel yang bernama windowtidak ada dalam ES Spec dan karenanya tidak akan berfungsi, misalnya, V8 atau Node.js, sementara this.a = 0;(ketika dipanggil dalam konteks eksekusi global) akan bekerja di lingkungan apa pun karena spec tidak menentukan bahwa harus ada sebuah obyek global. Jika membungkus kode Anda dalam IIFE seperti di bagian Off-topic , Anda dapat lulus thissebagai parameter bernama windowatau globaluntuk mendapatkan referensi langsung ke objek global.
Sherlock_HJ

@Sherlock_HJ: Saya telah menambahkan "di browser;" itu juga dalam jawaban sebelumnya, tetapi saya menambahkannya kalau-kalau orang lewati itu. Itu ada dalam spec sekarang ; sementara ini hanya lewat, Anda tidak akan menemukan browser yang tidak melakukannya. Aku agak terkejut itu tidak dalam Lampiran B .
TJ Crowder

@ TJCrowder, Jadi, variabel global yang dideklarasikan dengan var a = 0;otomatis menjadi properti dari objek global. Jika saya mendeklarasikan var b = 0;dalam deklarasi fungsi, apakah itu juga akan menjadi properti dari beberapa objek yang mendasarinya?
ezpresso

@ezpresso: Tidak dan ya. Mereka menjadi properti dari suatu objek ( EnvironmentRecord dari VariableEnvironment of ExecutionContext di mana mereka muncul; detail di sini dan di sini ), tetapi tidak ada cara untuk secara langsung mengakses objek itu dari kode program.
TJ Crowder

40

Tetap sederhana:

a = 0

Kode di atas memberikan variabel lingkup global

var a = 0;

Kode ini akan memberikan variabel untuk digunakan dalam lingkup saat ini, dan di bawahnya

window.a = 0;

Ini umumnya sama dengan variabel global.


Pernyataan Anda "Kode di atas memberikan variabel lingkup global" dan "Kode ini akan memberikan sebuah variabel yang akan digunakan dalam ruang lingkup saat ini, dan di bawahnya" , diambil bersama-sama, menunjukkan bahwa Anda tidak dapat menggunakan baris pertama dan akses a di bawah ini lingkup saat ini. Kamu bisa. Selain itu, penggunaan "variabel global" Anda sedikit salah - dua tempat yang Anda katakan "variabel global" tidak lebih global daripada tempat yang tidak Anda katakan.
TJ Crowder

global itu sendiri berarti Anda dapat mengakses / membaca / menulis variabel di mana saja, termasuk tempat di mana saya sebutkan ruang lingkup saat ini, yang sangat jelas. Dan jika Anda menyarankan bahwa window.a dan 'a' tidak akan mendunia dalam skrip maka Anda 100% salah.
Umair Jabbar

3
@Umair: "global itu sendiri berarti Anda dapat mengakses / membaca / menulis variabel di mana saja" Benar. Sekali lagi, Anda tampaknya memanggil yang pertama dan terakhir sebagai lebih "global" daripada yang di tengah, yang tentu saja tidak.
TJ Crowder

4
yang tengah dianggap digunakan di dalam suatu fungsi, semuanya akan sama jika digunakan di bawah lingkup utama. menggunakan var di dalam suatu fungsi adalah asumsi saya
Umair Jabbar

4
@Umair: "menggunakan var di dalam fungsi adalah asumsi saya" Ah, oke. Tapi bukan itu pertanyaannya. Pertanyaannya sangat jelas mengatakan "dalam lingkup global" . Jika Anda akan mengubah asumsi (yang cukup adil, untuk memperluas dan menjelaskan poin yang lebih umum), Anda harus jelas bahwa itulah yang Anda lakukan dalam jawaban Anda.
TJ Crowder

10
<title>Index.html</title>
<script>
    var varDeclaration = true;
    noVarDeclaration = true;
    window.hungOnWindow = true;
    document.hungOnDocument = true;
</script>
<script src="external.js"></script>

/* external.js */

console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8

console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!)  *I personally find this more clugy than hanging off window obj

Apakah ada objek global yang semua vars digantung secara default? mis: 'deklarasi global.noVar'


Eksplorasi yang sangat bagus. Panduan pasti untuk menggunakan window.*deklarasi. Deklarasi ini terlihat paling aman terhadap copy-paste kode Anda, dan hapus juga.
Dan

7

Bass pada jawaban yang sangat baik dari TJ Crowder : ( Di luar topik: Hindari berantakanwindow )

Ini adalah contoh dari idenya:

Html

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="init.js"></script>
    <script type="text/javascript">
      MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
    </script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Hello !</h1>
  </body>    
</html>

init.js (Berdasarkan jawaban ini )

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

    return {
        init : function(Args) {
            _args = Args;
            // some other initialising
        },
        helloWorld : function(i) {
            return _args[i];
        }
    };
}());

script.js

// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);

alert(a);

Inilah plnkrnya . Semoga ini bisa membantu!


5

Dalam lingkup global tidak ada perbedaan semantik.

Tetapi Anda harus benar-benar menghindari a=0karena menetapkan nilai ke variabel yang tidak dideklarasikan.

Juga gunakan penutupan untuk menghindari mengedit ruang lingkup global sama sekali

(function() {
   // do stuff locally

   // Hoist something to global scope
   window.someGlobal = someLocal
}());

Selalu gunakan penutupan dan selalu angkat ke ruang lingkup global ketika itu benar-benar diperlukan. Anda harus menggunakan penanganan acara yang tidak sinkron untuk sebagian besar komunikasi Anda.

Seperti @AvianMoncellor menyebutkan ada bug IE dengan var a = foohanya mendeklarasikan global untuk cakupan file. Ini adalah masalah dengan interpreter rusak IE yang terkenal. Bug ini memang terdengar familier sehingga mungkin benar.

Jadi berpegang teguh pada window.globalName = someLocalpointer


2
"Dalam lingkup global tidak ada perbedaan semantik." Sebenarnya, ada perbedaan semantik yang sangat besar, mekanisme di mana properti didefinisikan benar-benar berbeda - tetapi dalam istilah praktis itu bermuara pada hanya perbedaan kecil yang sebenarnya (di mana Anda tidak bisa deletea var).
TJ Crowder

@ TT Crowder, aku tidak tahu itu. Saya pikir deklarasi variabel adalah pengaturan properti pada objek variabel. Tidak tahu itu tidak bisa dihapus.
Raynos

Ya. Mereka juga didefinisikan sebelumnya jika Anda menggunakan var. Mereka hanya mekanisme yang sama sekali berbeda yang memiliki hasil praktis yang sama. :-)
TJ Crowder

@ TT Crowder, saya lupa menyebutkan bahwa varmelompat ke berhenti lingkup.
Raynos
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.