Apakah variabel dideklarasikan dengan membiarkan atau const tidak diangkat dalam ES6?

266

Saya telah bermain dengan ES6 untuk sementara waktu dan saya perhatikan bahwa sementara variabel yang dideklarasikan dengan vardinaikkan seperti yang diharapkan ...

console.log(typeof name); // undefined
var name = "John";

... variabel yang dideklarasikan dengan letatau consttampaknya memiliki beberapa masalah dengan pengangkatan:

console.log(typeof name); // ReferenceError
let name = "John";

dan

console.log(typeof name); // ReferenceError
const name = "John";

Apakah ini berarti bahwa variabel dideklarasikan dengan letatau consttidak diangkat? Apa yang sebenarnya terjadi di sini? Apakah ada perbedaan antara letdan constdalam hal ini?

Luboš Turek
sumber

Jawaban:

346

@thefourtheye benar dalam mengatakan bahwa variabel-variabel ini tidak dapat diakses sebelum dideklarasikan. Namun, ini sedikit lebih rumit dari itu.

Apakah variabel dideklarasikan dengan letatau consttidak diangkat? Apa yang sebenarnya terjadi di sini?

Semua deklarasi ( var, let, const, function, function*, class) yang "mengangkat" dalam JavaScript. Ini berarti bahwa jika nama dideklarasikan dalam lingkup, dalam lingkup itu pengidentifikasi akan selalu referensi variabel tertentu:

x = "global";
// function scope:
(function() {
    x; // not "global"

    var/let/… x;
}());
// block scope (not for `var`s):
{
    x; // not "global"

    let/const/… x;
}

Ini berlaku untuk fungsi dan blok cakupan 1 .

Perbedaan antara var/ function/ function*deklarasi dan let/ const/ classdeklarasi adalah inisialisasi .
Yang pertama diinisialisasi dengan undefinedatau fungsi (generator) tepat ketika mengikat dibuat di bagian atas ruang lingkup. Namun variabel yang dinyatakan secara leksikal tetap tidak diinisialisasi . Ini berarti bahwa ReferenceErrorpengecualian dilemparkan ketika Anda mencoba mengaksesnya. Ini hanya akan diinisialisasi ketika pernyataan let/ const/ classdievaluasi, semuanya sebelum (di atas) yang disebut zona mati sementara .

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

Perhatikan bahwa let y;pernyataan akan menginisialisasi variabel dengan undefinedlike let y = undefined;.

The sementara zona mati bukanlah lokasi sintaksis, melainkan waktu antara variabel (lingkup) penciptaan dan inisialisasi. Itu bukan kesalahan untuk referensi variabel dalam kode di atas pernyataan selama kode itu tidak dieksekusi (misalnya fungsi tubuh atau kode mati), dan itu akan membuang pengecualian jika Anda mengakses variabel sebelum inisialisasi bahkan jika mengakses kode di bawah deklarasi (mis. dalam deklarasi fungsi yang diangkat yang disebut terlalu dini).

Apakah ada perbedaan antara letdan constdalam hal ini?

Tidak, mereka bekerja sama sejauh mengangkat dianggap. Satu-satunya perbedaan di antara mereka adalah bahwa constsemut harus dan hanya dapat ditugaskan di bagian initialiser dari deklarasi ( const one = 1;, baik const one;dan kemudian penugasan kembali seperti one = 2tidak valid).

1: vardeklarasi masih bekerja hanya pada level fungsi, tentu saja

Bergi
sumber
16
Saya menemukan bahwa sesuatu seperti let foo = () => bar; let bar = 'bar'; foo();menggambarkan semua deklarasi adalah efek mengangkat lebih baik, karena tidak jelas karena zona mati sementara.
Estus Flask
1
Saya akan bertanya tentang referensi definisi let dalam suatu fungsi yang dideklarasikan sebelum let (yaitu penutupan). Saya pikir ini menjawab pertanyaan, itu legal tetapi akan menjadi kesalahan ref jika fungsi dipanggil sebelum pernyataan let dieksekusi, dan akan baik-baik saja jika fungsi dipanggil setelah itu. mungkin ini bisa ditambahkan ke jawabannya jika benar?
Mike Lippert
2
@ MikeLippert Ya, itu benar. Anda tidak boleh memanggil fungsi yang mengakses variabel sebelum diinisialisasi. Skenario ini terjadi dengan setiap deklarasi fungsi yang diangkat, misalnya.
Bergi
1
Keputusan untuk membuat constsuka letadalah cacat desain. Dalam ruang lingkup, constseharusnya dibuat untuk diangkat dan tepat waktu diinisialisasi ketika diakses. Sungguh, mereka harus memiliki kata kunci a const, a let, dan lainnya yang menciptakan variabel yang berfungsi seperti "hanya baca" let.
Pacerier
1
" Yang pertama diinisialisasi dengan undefined ..." mungkin ok untuk deklarasi var tetapi tampaknya tidak sesuai untuk deklarasi fungsi, yang diberi nilai sebelum eksekusi dimulai.
RobG
87

Mengutip bagian spesifikasi letdan constdeklarasi ECMAScript 6 (ECMAScript 2015) ,

Variabel dibuat ketika Lingkungan Lexis yang mengandungnya dipakai tetapi tidak dapat diakses dengan cara apa pun sampai LexicalBinding variabel dievaluasi .

Jadi, untuk menjawab pertanyaan Anda, ya, letdan constangkat tetapi Anda tidak dapat mengaksesnya sebelum deklarasi aktual dievaluasi saat runtime.

thethourtheye
sumber
22

ES6memperkenalkan Letvariabel yang muncul dengan block level scoping. Sampai ES5kita tidak punya block level scoping, jadi variabel yang dideklarasikan di dalam blok selalu hoistedberfungsi scoping level.

Pada dasarnya Scopemerujuk ke mana dalam program Anda variabel Anda terlihat, yang menentukan di mana Anda diizinkan untuk menggunakan variabel yang telah Anda nyatakan. Di ES5kita miliki global scope,function scope and try/catch scope, dengan ES6kita juga mendapatkan pelingkupan tingkat blok dengan menggunakan Let.

  • Saat Anda mendefinisikan variabel dengan varkata kunci, itu diketahui seluruh fungsi dari saat itu didefinisikan.
  • Ketika Anda mendefinisikan variabel dengan letpernyataan itu hanya diketahui di blok itu didefinisikan.

     function doSomething(arr){
         //i is known here but undefined
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(var i=0; i<arr.length; i++){
             //i is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(let j=0; j<arr.length; j++){
             //j is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
     }
    
     doSomething(["Thalaivar", "Vinoth", "Kabali", "Dinesh"]);

Jika Anda menjalankan kode, Anda bisa melihat variabel jhanya dikenal di loopdan bukan sebelum dan sesudah. Namun, variabel kami idikenal entire functionsejak saat itu didefinisikan seterusnya.

Ada keuntungan besar lainnya menggunakan let karena menciptakan lingkungan leksikal baru dan juga mengikat nilai segar daripada mempertahankan referensi lama.

for(var i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

for(let i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

forLoop pertama selalu mencetak nilai terakhir , dengan letitu menciptakan ruang lingkup baru dan mengikat nilai baru mencetak kita 1, 2, 3, 4, 5.

Datang ke constants, itu bekerja pada dasarnya seperti let, satu-satunya perbedaan adalah nilainya tidak dapat diubah. Dalam konstanta mutasi diperbolehkan tetapi pengalihan tugas tidak diizinkan.

const foo = {};
foo.bar = 42;
console.log(foo.bar); //works

const name = []
name.push("Vinoth");
console.log(name); //works

const age = 100;
age = 20; //Throws Uncaught TypeError: Assignment to constant variable.

console.log(age);

Jika sebuah konstanta merujuk pada object, ia akan selalu merujuk ke objecttetapi konstanta objectitu sendiri dapat diubah (jika bisa berubah). Jika Anda ingin memiliki yang kekal object, Anda bisa menggunakannyaObject.freeze([])

Thalaivar
sumber
5

Dari dokumen web MDN:

Dalam ECMAScript 2015, letdan constdiangkat tetapi tidak diinisialisasi. Merujuk variabel di blok sebelum deklarasi variabel menghasilkan dalam ReferenceErrorkarena variabel berada dalam "zona mati temporal" dari awal blok sampai deklarasi diproses.

console.log(x); // ReferenceError
let x = 3;
YourAboutMeIsBlank
sumber
0

di es6 ketika kita menggunakan let atau const kita harus mendeklarasikan variabel sebelum menggunakannya. misalnya. 1 -

// this will work
u = 10;
var u;

// this will give an error 
k = 10;
let k;  // ReferenceError: Cannot access 'k' before initialization.

misalnya. 2-

// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9
pengguna260778
sumber