ES6'da let veya const ile bildirilen değişkenler kaldırılmıyor mu?


266

Bir süredir ES6 ile oynuyorum ve beyan edilen değişkenlerin varbeklendiği gibi kaldırıldığını fark ettim ...

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

... kaldırma ile beyan edilen letveya constkaldırma ile ilgili bazı problemleri olan değişkenler :

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

ve

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

Bu, beyan edilen letveya constkaldırılmayan değişkenlerin olduğu anlamına mı geliyor ? Burada gerçekten neler oluyor? Bu konuda letve constbu konuda herhangi bir fark var mı ?

Yanıtlar:


346

@thefourtheye, bu değişkenlere bildirilmeden önce erişilemeyeceğini söylerken doğrudur . Ancak, bundan biraz daha karmaşık.

Değişkenler beyan edilmiş letveya constkaldırılmamış mı? Burada gerçekten neler oluyor?

Tüm beyanlar ( var, let, const, function, function*, class) "hoisted" olan JavaScript. Bu, bir isim bir kapsamda bildirilirse, bu kapsamda tanımlayıcının her zaman bu belirli değişkene başvuracağı anlamına gelir:

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

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

    let/const/… x;
}

Bu hem fonksiyon hem de blok kapsamları 1 için geçerlidir .

var/ function/ function*Bildirimleri ve let/ const/ classbildirimleri arasındaki fark başlatmadır . Ciltleme, kapsamın en üstünde oluşturulduğunda,
öncekiyle undefinedveya (jeneratör) işleviyle hemen başlatılır . Ancak, sözlü olarak bildirilen değişkenler başlatılmamış olarak kalır . Bu ReferenceError, erişmeye çalıştığınızda bir istisna atıldığı anlamına gelir . Yalnızca let/ const/ classifadesi değerlendirildiğinde, geçici ölü bölge olarak adlandırılan önceki (yukarıda) her şey başlatılır .

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

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

Bir let y;deyimin değişkeni undefinedbenzer şekilde başlattığına dikkat edin let y = undefined;.

Zamansal ölü bölge bir sentaks yer değil fakat daha çok zaman değişkeni (kapsam) oluşturulması ve başlatma arasında. Bu kod yürütülmediği sürece (örn. Bir işlev gövdesi veya yalnızca ölü kod), koddaki değişkene başvurmak bir hata değildir ve erişime rağmen bile başlatma işleminden önce değişkene erişirseniz bir istisna atar kodu bildirimin altındadır (örneğin, çok erken çağrılan bir kaldırma işlevi bildiriminde).

Bu konuda letve constbu konuda herhangi bir fark var mı ?

Hayır, kaldırma işlemi göz önüne alındığında aynı şekilde çalışırlar. Aralarındaki tek fark, bir constkarıncanın beyanın başlatıcı kısmında atanması ve atanabilmesidir ( const one = 1;hem const one;ve daha sonra yeniden atamalar one = 2geçersizdir).

1: varbildirimler elbette sadece işlev düzeyinde çalışıyor


16
Gibi birşeyler bulabiliriz let foo = () => bar; let bar = 'bar'; foo();göstermektedir tüm beyanlar askıya alır o zamansal ölü bölge nedeniyle açık değildir, çünkü daha iyi bir etki.
Estus Flask

1
Let (yani bir kapatma) önce bildirilen bir işlev bir let tanımı başvuru hakkında sormak üzereydi. Bence bu soruya cevap veriyor, yasal ama let ifadesi yürütülmeden önce işlev çağrılırsa bir ref hatası olacak ve işlev daha sonra çağrılırsa iyi olacak. belki bu doğruysa cevaba eklenebilir mi?
Mike Lippert

2
@MikeLippert Evet, bu doğru. Başlatılmadan önce değişkene erişen işlevi çağırmamalısınız. Bu senaryo, örneğin, her çekilen işlev bildirimiyle oluşur.
Bergi

1
constBöyle letbir karar bir tasarım hatasıdır. Bir kapsamda, consterişildiğinde kaldırılmalı ve tam zamanında başlatılmalıdır. Gerçekten de, "salt okunur" gibi çalışan bir değişken oluşturan bir const, a letve başka bir anahtar kelimeye sahip olmalıdırlar let.
Pacerier

1
" Birincisi tanımlanmamış olarak başlatıldı ..." var bildirimleri için uygun olabilir, ancak yürütme başlamadan önce bir değer atanan işlev bildirimleri için uygun görünmüyor.
RobG

87

ECMAScript 6 (ECMAScript 2015) spesifikasyonları letve constbeyanlar bölümüne alıntı yapmak ,

Değişkenler, Lexical Environment içeren örnekleri başlatıldığında oluşturulur, ancak değişkenin LexicalBinding değeri değerlendirilene kadar hiçbir şekilde erişilemez .

Bu nedenle, sorunuzu cevaplamak için evet letve constkaldırma, ancak gerçek bildirim çalışma zamanında değerlendirilmeden önce bunlara erişemezsiniz.


22

ES6ortaya çıkan Letdeğişkenleri tanıtır block level scoping. Kadar ES5biz yoktu block level scoping, bu yüzden bloğu içinde bildirilen değişkenler her zaman hoistedişlev düzeyi kapsam belirleme için.

Temel olarak Scope, değişkenlerinizin nerede göründüğünü belirtir; bu, bildirdiğiniz değişkenleri nerede kullanmanıza izin verildiğini belirler. Sahip ES5olduğumuzda global scope,function scope and try/catch scope, ES6Let kullanarak blok seviyesi kapsamını da elde ederiz.

  • varAnahtar kelimeyle bir değişken tanımladığınızda , tanımlandığı andan itibaren tüm işlev bilinir.
  • İfadeli bir değişken tanımladığınızda, letyalnızca tanımlandığı blokta bilinir.

     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"]);

Kodu çalıştırırsanız, değişkenin jyalnızca loopve öncesinde ve sonra bilinmediğini görebilirsiniz . Ancak değişkenimiz i, entire functiondaha sonra tanımlandığı andan itibaren bilinmektedir .

Yeni bir sözlüksel ortam yarattığı ve eski bir referans tutmak yerine yeni değeri bağladığı için let'i kullanmanın başka bir büyük avantajı daha var.

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)
}

İlk fordöngü her zaman son değeri yazdırır, letyeni bir kapsam oluşturur ve bize baskı yapan yeni değerleri bağlar 1, 2, 3, 4, 5.

Gelmek constants, temelde olduğu gibi çalışır let, tek fark değerlerinin değiştirilememesidir. Sabitlerde mutasyona izin verilir ancak yeniden atamaya izin verilmez.

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);

Eğer bir sabit bir 'e atıfta bulunursa, daima' 'anlamına gelir object, objectfakat objectkendisi değiştirilebilir (eğer değiştirilebilirse). Eğer değişmez bir objectşeye sahip olmak isterseniz,Object.freeze([])


5

Gönderen MDN web docs:

ECMAScript 2015'te letve constkaldırıldı, ancak başlatılmadı. Değişken bildiriminden önce değişkene blokta başvuruda bulunulması ReferenceError, değişkenin blok başlangıcından bildirim işlenene kadar bir "geçici ölü bölge" içinde olmasından kaynaklanır.

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

0

es6'da let veya const kullandığımızda değişkeni kullanmadan önce bildirmek zorundayız. Örneğin. 1 -

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

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

Örneğin. 2-

// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.