Neden bir işlevin içinde izin verilen bazı değişkenler başka bir işlevde kullanılabilir duruma getirilirken, diğerleri bir başvuru hatasına neden oluyor?


158

Bir işlev içinde bildirildiğinde değişkenlerin neden bu kadar garip davrandığını anlayamıyorum.

  1. Olarak firstişlev I ile beyan letdeğişkenler bve cdeğer ile 10 :

    b = c = 10;

    Gösterdiğim secondfonksiyonda:

    b + ", " + c

    Ve bu şunu gösterir:

    10, 10
  2. Ayrıca firstfonksiyonda 10a değeri ile beyan ederim :

    let a = b = c = 10;

    Ancak secondişlevde bir hata gösterir:

    Değişken bulunamıyor: a

  3. Şimdi firstfonksiyonda 20d değeri ile beyan ederim :

    var d = 20;

    Ancak secondişlevde, önceki ile aynı hatayı gösterir, ancak değişkenle d:

    Değişken bulunamıyor: d

Misal:

function first() {
  let a = b = c = 10;
  var d = 20;
  second();
}

function second() {
  console.log(b + ", " + c); //shows "10, 10"

  try{ console.log(a); }  // Rreference error
  catch(e){ console.error(e.message) }

  try{ console.log(d); } // Reference error
  catch(e){ console.error(e.message) }
}
first()


31
O zamandan beri, globals ilan ediyoruz bve cöneki olmayan varanahtar kelime. ave dyereldir first.
VLAZ

1
Yorumlar uzun tartışmalar için değildir; bunun iyi bir röportaj sorusu olup olmayacağına dair teğetsel bir konuşma sohbet sırasında arşivlendi .
Cody Gray

1
Bu bana Visual Basic benzer bir durumu hatırlatıyor; Dim Apple, Banana, Pear As Fruitanlamına gelir Dim Apple / Dim Banana / Dim Pear As Fruit, ve değil Dim Apple As Fruit / ....
Eric Lippert

Yanıtlar:


179

Çünkü aslında şunları söylüyorsunuz:

c = 10;
b = c;
let a = b;

Söylediğini düşündüğün gibi değil, yani:

let a = 10;
let b = 10;
let c = 10;

Zincire kaç değişken ekleseniz de, sadece hataya neden olan ilk (a) olacağını fark edeceksiniz.

Bunun nedeni, "izin ver" inizin, bildirdiğiniz bloğa (veya "yerel olarak", daha fazla veya daha az "parantez içinde" anlamına gelmesine) neden olmasıdır.

Bir değişkeni "let" olmadan bildirirseniz, değişkeni global olarak kapsamaktadır.

Yani, değişkenlerinizi ayarladığınız fonksiyonda, her şey 10 değerini alır (bir kesme noktası koyarsanız bunu hata ayıklayıcıda görebilirsiniz). Bu ilk işlevde a, b, c için bir konsol günlüğü koyarsanız, her şey yolunda demektir.

Ancak bu işlevden ayrılır ayrılmaz, ilk işlev (a) - ve yine, teknik olarak atama sırasına göre, sonuncusudur - "kaybolur" (tekrar, bunu ikinci işlevde bir kesme noktası ayarlarsanız hata ayıklayıcı), ancak diğer ikisi (veya eklediğiniz pek çok) hala kullanılabilir.

Bunun nedeni, "izin" SADECE İLK DEĞİŞKEN - sadece teknik olarak bildirilen ve bir değer atanan son zincir olan - sadece yerel kapsamları) için geçerlidir. Geri kalanı teknik olarak önlerinde "izin" yoktur. Yani bunlar teknik olarak küresel olarak (yani küresel nesne üzerinde) ilan edilir, bu yüzden ikinci fonksiyonunuzda görünürler.

Deneyin: "let" anahtar kelimesini kaldırın. Tüm çeşitleriniz şimdi mevcut olacak.

"var", benzer bir yerel kapsam etkisine sahiptir, ancak değişkenin nasıl "kaldırıldığı" konusunda farklılık gösterir, bu kesinlikle anlamanız gereken bir şeydir, ancak sorunuzla doğrudan ilgili değildir.

(BTW, bu soru iyi bir JS yapmak için yeterince profesyonel JS geliştiricileri güdük olurdu).

Değişkenlerin JS'de nasıl bildirilebileceği konusundaki farklılıklarla zaman ayırmanızı öneririz: anahtar kelime olmadan, "let" ve "var" ile.


4
Aynı anda programlama ile ilgili en iyi ve en kötü şey: bilgisayar tam olarak ne söylediğinizi yapacak. Bunu yapmayı söylemek istediğin şey değil. Programlar mükemmel. Sorunları yaratıyoruz.
Niet the Dark Absol

8
@Thevs Neden tavsiye edersiniz varüzerinde letbu cevap bağlamında? Anlamıyorum.
Klaycon

4
@ Sana kesinlikle katılmıyorum. vardikkatsizce kullanılırsa hatalara eğilimli olabilir. Bu kemanı
Cid

2
Ne durumda @Thevs gelmez varsahip herhangi üzerinde avantaj let? Bana açıklığa kavuşturacağım: her ikisi de seçenekler olduğunda modern bir bağlam ve ben bir kod yazmanız gerekir . Bunu daha önce sorduğumda, "bir değişkeni yeniden bildirebilirsiniz" hakkında yanıtlar varaldım, bu durumda insanlara değişkenleri yeniden bildirmemeniz gerektiğini hatırlatmak zorundayım . Bu ya bir hata ya da kodun mantığındaki bir hatadır - bu yüzden yeniden bildirimin yararı ... hatalı kod yazmanıza izin vermesidir. Henüz varne zaman letbir seçenek olduğu için herhangi bir mantıklı sebep görmedim .
VLAZ

2
Javascript'e karşı başka bir not çizin. Yerel olarak bildirilmedikçe tüm değişkenler globaldir. :(
JRE

68

İşlevde first() , değişkenler bve ckullanmadan anında oluşturulur varveya let.

let a = b = c = 10; // b and c are created on the fly

Farklı

let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)

Örtülü küresel olurlar. Bu yüzdensecond()

Gönderen dokümantasyon

Tanımlanmamış bir değişkene bir değer atamak, atama yürütüldüğünde dolaylı olarak genel bir değişken (genel nesnenin bir özelliği haline gelir) oluşturur.

Bundan kaçınmak "use strict"için, bildirilmemiş bir değişken kullanıldığında hatalar sağlayacak şekilde kullanabilirsiniz .

"use strict"; // <-------------- check this

function first() {
   /*
    * With "use strict" c is not defined.
    * (Neither is b, but since the line will be executed from right to left,
    * the variable c will cause the error and the script will stop)
    * Without, b and c become globals, and then are accessible in other functions
    */
   let a = b = c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //reference error
   console.log(a); //reference error
   console.log(d); //reference error
}

first();


15
Ayrıca: let a = 10, b = 10, c = 10;veya let a, b, c; a = b = c = 10;değişkenleri bildirmenin doğru yolu olabilir.
Rickard Elimää

Peki katı modda, b değişkeni ne olacak?
Tick20

2
@ Tick20 değişkeni bdeğerlendirilmez / ulaşmaz, hata satırda meydana gelir, sağdan solalet a = b = c = 10; okunur . neden olan ilk değişken olduğu için , satırın geri kalanı yürütülmez (komut dosyası durdu)cReferenceError
Cid

2
gibi bir şey let a = 10, b = a, c = b;de geçerlidir
Kaddath

8
esas olarak "katı kullanın" için seçildi. Bir röportaj sorusu bağlamında, bu da bu kod hakkındaki yorumumun başlangıcı olacaktır.
Pac0

23

İşleri garip olarak çağırmadan önce, önce bazı temel bilgileri öğrenelim:

var ve let her ikisi de JavaScript'te değişken bildirim için kullanılır. Örneğin,

var one = 1;
let two = 2;

Değişkenler varveya kullanılmadan da bildirilebilir let. Örneğin,

three = 3;

Şimdi yukarıdaki yaklaşımlar arasındaki fark şudur:

var fonksiyon kapsamlıdır

ve

let blok kapsamlıdır.

var/ letanahtar kelime olmadan bildirilen değişkenlerin kapsamı küreselleşirken nerede bildirildiğinden bağımsız olarak .

Global değişkenler web sayfasının herhangi bir yerinden erişilebilir (globaller yanlışlıkla değiştirilebileceği için önerilmez).

Şimdi bu kavramlara göre söz konusu koda bir göz atalım:

 function first() {
   let a = b = c = 10;
   /* The above line means:
    let a=10; // Block scope
    b=10; // Global scope
    c=10; // Global scope
    */

   var d = 20; // Function scope
   second();
}

function second() {
   alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
   alert(a); // Error not accessible because block scope has ended
   alert(d); // Error not accessible because function scope has ended
}

1
JonoJames'in cevabının bir kısmını intihal ettin . Neden?
Peter Mortensen

2
Üzgünüm ama böyle bir niyetim yoktu, benzer bir şey olabilir, çünkü aynı kaynaktan bir parça bilgi toplamış olabiliriz.
fatimasajjad

2
Her iki yanıtın da, görünürde alıntı yapılmaksızın aynı orijinal kaynaktan kopyalanmış içerik barındırması olabilir - muhtemelen tutorialsteacher.com/javascript/javascript-variable . - intihal varlığı gramer hatası Orijinalden yeniden edildiğinden, belirgindir "olmadan değişkenlerin kapsamı ilan varbunun ilan nereye küresel bakılmaksızın haline Keyword" şeklinde olmalıdır "... kapsam olur" veya " kapsamları ... olur " . Başka birinin kesin kelimelerini kullanmak, ister buradan ister başka bir yerden alıntı yapılmasını gerektirir. meta.stackexchange.com/q/160071/211183
Michael - sqlbot

Teşekkürler çocuklar, kaynak için bir referans bağlantısı ekledim.
fatimasajjad

6

Kullanarak değişkenler letAnahtar kelimeyi sadece blok kapsamında mevcut olmalı ve dış fonksiyonda mevcut olmamalıdır ...

Bu şekilde bildirdiğiniz her değişken letveyavar . Değişken bildiriminde virgül eksik.

O edilir önerilmez olmadan bir değişken bildirmek için varanahtar kelime. Yanlışlıkla mevcut bir global değişkenin üzerine yazabilir. varAnahtar kelime olmadan bildirilen değişkenlerin kapsamı, nerede belirtildiğine bakılmaksızın global hale gelir. Global değişkenlere web sayfasının herhangi bir yerinden erişilebilir.

function first() {
   let a = 10;
   let b = 10;
   let c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //shows "10, 10"
   console.log(a); //reference error
   console.log(d); //reference error
}

first();


3

Bunun nedeni, kullanmadığınız letveya vardeğişkenin anında bildirim alması nedeniyle, aşağıdaki gibi daha iyi beyan etmenizdir.

let a = 10;
let b = 10;
let c = 10;

2

Garip sorun, JavaScript'te kapsam belirleme kurallarından kaynaklanıyor

function first() {
   let a = b = c = 10; // a is in local scope, b and c are in global scope
   var d = 20; // d is in local scope
   second(); // will have access to b and c from the global scope
}

Aynı değere ayarlanmış 3 yerel değişkeni bildirmek istediğinizi varsayarsak (100). İlkiniz () aşağıdaki gibi görünecektir. Bu durumda, second () değişkeninin hiçbirine erişimi olmaz, çünkü bunlar first (ilk) için yereldir ()

function first() {
   let a = 100; // a is in local scope init to 100
   let b = a; // b is in local scope init to a
   let c = b // c is in local scope init to b

   var d = 20; // d is in local scope
   second(); // will not have access a, b, c, or d
}

Ancak, global değişkenler istiyorsanız ilk () yönteminiz aşağıdaki gibi görünecektir. Bu durumda, ikincisi global kapsamda oldukları için tüm değişkenlere erişebilecek

function first() {
   a = 100; // a is in global scope
   b = a; // b is in global scope
   c = b // c is in global scope

   d = 20; // d is in global scope
   second(); // will have access to a, b, c, and d from the global scope
}

Yerel değişkenler (diğer adıyla bildirildikleri kod bloğundan erişilebilir).
Kod bloğu, arasında kod satırları olan herhangi bir {}.

  • function () {var, let, in const burada tüm fonksiyona erişilebilir},
  • for () {Buradaki var dış kapsama erişilebilir, izin, const sadece buradan erişilebilir},
  • vb.

Global değişkenler (küresel kapsamda erişilebilir).
Bu değişkenler genel nesneye eklenir. Global nesne çevreye bağlıdır. Tarayıcılardaki pencere nesnesidir.

Özel not: var, let, const anahtar sözcüklerini kullanmadan JavaScript'te değişkenler bildirebilirsiniz. Bu şekilde bildirilen bir değişken genel nesneye iliştirilir, bu nedenle genel kapsamda erişilebilir.
a = 100 // is valid and is in global scope

Daha fazla okumak için bazı makaleler: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / topluluk / öğreticiler / anlama-değişkenler-kapsam-kaldırma-in-javascript


0

Temel fark, kapsam belirleme kurallarıdır. Var anahtar sözcüğü ile bildirilen değişkenler, hemen işlev gövdesine (dolayısıyla işlev kapsamına) dahil edilirken, değişkenler, {} ile belirtilen hemen çevreleme bloğuna (dolayısıyla blok kapsamına) dahil edilir. Ve dediğinde

c = 10;
b = c;
let a = b;

c ve b'nin ömrü eğlencelidir, ancak sadece blok aralığı vardır ve eğer başvuruda bulunarak erişmeye çalışırsanız, her zaman hata gösterir, ancak c ve b küreseldir, böylece yoktur. değişkenleri zincirinize eklediğinizde, bu yalnızca hataya neden olan ilk (a) olacaktır. Bunun nedeni, "let" değişkeninizin bloğa kapsamasıdır (veya "yerel olarak", daha fazla veya daha az "parantez içinde" anlamına gelir) "let" olmadan bir değişken bildirirseniz, değişkeni global olarak kapsamaktadır. Yani, değişkenlerinizi ayarladığınız işlevde her şey 10 değerini alır (bunu hata ayıklayıcıda görebilirsiniz. kırılma noktası). Bu ilk işlevde a, b, c için bir konsol günlüğü koyarsanız, her şey yolundadır, ancak bu işlevden ayrılır ayrılmaz, ilk (a) - ve tekrar aklınızda bulundurun,


0

JavaScript'teki değişken bildirimlerin 3 ilginç yönü şunlardır:

  1. var , değişkenin kapsamını tanımlandığı bloğa sınırlar. ( 'var' şunun içindir: lokal kapsamı ).

  2. let verirbir blok içindeki harici değişkenin değerinin geçici olarak geçersiz kılınmasına.

  3. Basitçe olmadan bir değişken bildirerek var veya let bildirilmesi, nerede bildirildiğine bakılmaksızın değişkeni global hale getirir.

İşte bir demo let dilin en son :

// File name:  let_demo.js

function first() {
   a = b = 10
   console.log("First function:    a = " + a)
   console.log("First function:    a + b = " + (a + b))
}

function second() {
    let a = 5
    console.log("Second function:    a = " + a)
    console.log("Second function:    a + b = " + (a + b))
}

first()   

second()

console.log("Global:    a = " + a)
console.log("Global:    a + b = " + (a + b))

Çıktı:

$ node let_demo.js 

First function:    a = 10
First function:    a + b = 20

Second function:    a = 5
Second function:    a + b = 15

Global:    a = 10
Global:    a + b = 20

Açıklama:

A ve b değişkenleri ' first () içinde delcared edildi var veya let anahtar sözcükleri olmadan ' .

Bu nedenle, a ve b küreseldir ve bu nedenle program boyunca erişilebilir.

Adlı fonksiyonda 'ikinci' , deyim 'let = 5 bir' geçici 'değerini belirler bir ' hiç ' 5'e ', sadece işlevi kapsamında.

' Second () ' kapsamının dışında , IE, global kapsamda, ' a ' değeri daha önce tanımlandığı gibi olacaktır.

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.