JavaScript değişkenleri döngü içinde mi yoksa dışında mı bildiriyor?


213

AS3'te performans artışı için döngülerin dışındaki tüm değişkenleri başlatmanız gerektiğine inanıyorum. JavaScript ile de durum böyle mi? Hangisi daha iyi / daha hızlı / en iyi uygulama?

var value = 0;

for (var i = 0; i < 100; i++)
{
    value = somearray[i];
}

veya

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}

7
Dışarıda! her zaman dışarıda.
BGerrissen

37
Hmm, değişken bildirimleri hem Javascript hem de AS3'te işlev kapsamına getirilmiyor mu? Doğruysam, o zaman gerçekten önemli değil.
harcayan

3
@Andy - bir işlev gövdesinde bildirmeden önce atamayı denediniz mi? Belki de önyargılarınız sizi saptırıyor. WRT performansı, push-up kapsamı ile, eğer JS yorumlanırsa, bir döngü bloğu içinde ekstra döngüleri çiğner. Derlendiğinde (çoğu motorun bugünlerde yaptığı) önemli değil.
harcamacı

2
Harika bir soru! Teşekkürler. Tüm cevapları okuduktan sonra, sadece küçük bir döngü ya da sadece geçici bir değişken olması gerektiğinde onları tutacağım ve performansı etkilemediğine inanıyorum. Bir var bir kereden fazla işlev içinde kullanılırsa, neden işlevi içinde başvurmak değildir ve son olarak daha sonra fn () dışında oturdu edilebilir Globaller
Dekan Meehan

3
Kimsenin performansı ölçmeye çalışmadığıma şaşırdım. Bir jsperf oluşturdum . Safari ve Firefox, Chrome'un tersi için döngü içinde ilan edildiğinde biraz daha hızlı görünüyor ...
Buzut

Yanıtlar:


281

JavaScript veya ActionScript'te anlam veya performans açısından kesinlikle hiçbir fark yoktur .

varayrıştırıcı için bir direktiftir ve çalışma zamanında yürütülen bir komut değildir . Belirli bir tanımlayıcı varbir işlev gövdesinde (*) bir veya daha fazla herhangi bir yerde bildirilmişse , bu tanımlayıcının bloktaki tüm kullanımı yerel değişkene atıfta bulunacaktır. Döngünün içinde, döngünün dışında veya her ikisinin birden valuebildirildiği fark etmez var.

Sonuç olarak en okunaklı bulduğunuz şeyi yazmalısınız. Crockford ile tüm değişkenleri bir işlevin üstüne koymanın her zaman en iyi şey olduğunu kabul etmiyorum. Bir değişkenin bir kod bölümünde geçici olarak kullanıldığı durumda var, bu bölümde bildirmek daha iyidir , bu nedenle bölüm tek başına kalır ve kopyalanabilir. Aksi takdirde, birkaç kod satırını yeniden ilişkilendirme sırasında ilişkili olanı ayrı olarak seçip taşımadan yeni bir işleve kopyalayıp yapıştırın varve kendinize yanlışlıkla bir global var.

Özellikle:

for (var i; i<100; i++)
    do something;

for (var i; i<100; i++)
    do something else;

Crockford ikinciyi varkaldırmanızı (ya da her ikisini de kaldırmanızı varve var i;yukarıdaki işlemleri yapmanızı ) tavsiye eder ve jslint bunun için size sızlanır. Ancak IMO var, işlevin üstünde ekstra, kolayca unutulan bir kod parçasına sahip olmak yerine, her iki s'yi de tutmak, ilgili tüm kodu bir arada tutmak daha sürdürülebilir .

Şahsen ben var, aynı fonksiyonun başka bir bölümünde aynı değişken adının başka bir ayrı kullanımı olsun veya olmasın, bağımsız bir kod bölümünde bir değişkenin ilk ataması olarak beyan etme eğilimindeyim . Benim için hiç beyan etmek varistenmeyen bir JS siğilidir (değişkenlerin yerel olarak varsayılana sahip olması daha iyi olurdu); JavaScript'te de [eski bir düzeltme] ANSI C'nin sınırlamalarını çoğaltmak benim görevim olarak görmüyorum.

(*: iç içe fonksiyon gövdeleri dışında)


4
Hala Crockford'la beraber olup olmadığım konusunda karar veremiyorum. Blokların içindeki değişkenleri bildirmek, koşullu işlev ifadelerini (yaramaz) kullanmak gibi bir şey hisseder ... Ancak, puanlarınıza da katılıyorum :) #confused
Daniel Vassallo

20
+1 Crockford'un muhakemesine katılmıyorum (Daniel'in cevabında alıntılanmıştır), JavaScript geliştiricileri olarak diğer "C ailesi" programcılarının anlayabilmesi için kod yazmamalıyız. Bloklar ve döngüler varsa genellikle içeride var kullanıyorum çünkü bana daha mantıklı geliyor.
Andy E

4
-1 OP, döngü başlamadan önce, döngü gövdesindeki değişkenlerin bildirilmesi gerekip gerekmediğini soruyor. Döngünün dizin değeri açıkça özel bir durumdur (ve çekilir) ve OP'ye hiç yardımcı olmaz.
mkoistinen

21
Döngü dizinleri özel bir durum değildir, normal bir atama ile aynı şekilde ele alınır ve kaldırılırlar.
bobince

31
+1 Crockford bu konuda yanlış (ve diğerleri, ama ben kazıyorum). varSadece bir fonksiyonun üstünde kullanılmasını istemek , sadece yanlışlıkla global değişken yaratmayı istemektir. Ve hepsi bir noktada bildirilen ilişkisiz değişkenler kütlesine sahip olmak anlamsal olarak anlamsızdır, özellikle de bu değişkenlerden bazıları hiç kullanılmayabilir.
MooGoo

64

Teoride, dilin blok kapsamı değil, sadece işlev kapsamı olduğu için JavaScript'te herhangi bir fark yaratmamalıdır.

Performans argümanından emin değilim, ancak Douglas Crockford hala varifadelerin işlev gövdesindeki ilk ifadeler olmasını önermektedir . JavaScript Programlama Dili için Kod Kurallarından Alıntı :

JavaScript'in blok kapsamı yoktur, bu nedenle bloklardaki değişkenleri tanımlamak, diğer C ailesi dillerinde deneyimli programcıları karıştırabilir. Fonksiyonun üstündeki tüm değişkenleri tanımlayın.

Aşağıdaki örnekte gördüğünüz gibi bir anlamı olduğunu düşünüyorum. Fonksiyonun üstündeki değişkenleri bildirmek, okuyucuları değişkenin döngü bloğu i kapsamında tutulduğunu düşünmeyle karıştırmamalıdır for:

function myFunction() {
  var i;    // the scope of the variables is very clear

  for (i = 0; i < 10; i++) {
    // ...
  }
}

8
JS kapsamı hakkında OP gerçeklerini anlatmak için +1. Aksini söyleyen cevapları küçümsemek isteyip istemediğinizi merak ediyorum!
harcayan

1
@Kieranmaine: Performansı etkilemediğini söylemedim. Ben onları döngülerin dışına koymak için bir argüman yaptım, performansla ilgisiz .... Performans argümanları için herhangi bir referansım yok, ama cevabınızda da alıntı yapmadınız :)
Daniel Vassallo

1
@Kieranmaine: Bunun için bir kaynağın var mı?
Andy E

5
@Kieranmaine: Bir döngü içindeki değişkenleri bildirseniz bile AFAIK ecma- / javascript, çalışma zamanında bunları çarptıracaktır . Buna "Kaldırma" denir. Bu yüzden fark olmamalı.
jAndy

1
ES6'lar letbu yanıtı nasıl etkiler?
jbyrd

58

ECMA-/JavascriptDil hoistsbir fonksiyonun üstüne yerde ilan edilir herhangi değişkeni. Bu dil olmasıdır mu var function scopeve yok değil var block scopediğer birçok dilde C benzeri gibi.
Bu olarak da bilinir lexical scope.

Gibi bir şey beyan ederseniz

var foo = function(){
    for(var i = 0; i < 10; i++){
    }
};

Bu şunlara ulaşır hoisted:

var foo = function(){
    var i;
    for(i = 0; i < 10; i++){
    }
}

Bu yüzden performansta herhangi bir fark yaratmaz (Ama burada tamamen yanılıyorsam beni düzeltin).
A daha iyi argüman değil bir fonksiyonun üstünde başka bir yerde daha değişken bildirerek olduğunu okunabilirliği . Bir değişkenin bir değişken içinde bildirilmesi for-loop, bu değişkenin yalnızca döngü gövdesi içinde erişilebileceği yanlış varsayımına yol açabilir, ki bu tamamen yanlıştır . Infact, bu değişkenin geçerli kapsamın herhangi bir yerine erişebilirsiniz.


Kabul edilenle aynı temel cevap ama IMO, daha okunabilir ve neredeyse bilgilendirici. İyi iş.
Anne Gunn

5
ES6'lar letbu yanıtı nasıl etkiler?
jbyrd

13

Gelecek yıl, tüm tarayıcılar kodu önceden derleyen JS motorlarına sahip olacak, böylece performans farkı (aynı kod bloğunu tekrar tekrar ayrıştırma ve atamayı yürütme) ihmal edilebilir hale gelecektir.

Ayrıca, gerekmedikçe asla performans için optimize etmeyin. Değişkenleri ilk kez ihtiyacınız olan yere yakın tutmak kodunuzu temiz tutar. Olumsuz tarafta, blok kapsamı olan dillere alışkın olan insanlar karışık olabilir.


6

Şimdi sahip olduğumuz letve constES2015'teki bir başka husus, değişkenleri özellikle döngü bloğuna dahil edebilmenizdir. Dolayısıyla, döngü dışında aynı değişkene ihtiyacınız yoksa (veya her yineleme önceki yinelemede bu değişken için yapılan bir işleme bağlı değilse), muhtemelen bunu yapmanız tercih edilir:

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}

4

Chrome'da yeni bir test yaptım. Tarayıcınızda kemanı deneyin ve sonuçları görün

  var count = 100000000;
    var a = 0;
    console.log(new Date());

    for (var i=0; i<count; i++) {
      a = a + 1
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
      a = a + 1;
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
        var x;
        x = x + 1;
    }

    console.log(new Date());

Sonuç, son testin ~ 8 saniye sürmesi ve önceki 2'nin sadece ~ 2 saniye olmasıdır. Çok tekrarlanabilir ve sipariş ne olursa olsun.

Yani, bu bana, her zaman döngü dışındaki değişkenleri beyan etmesi gerektiğini kanıtlıyor. Meraklı durum i, for () ifadesinde bildirdiğim ilk dava . Bu, indeksi önceden bildirdiğim 2. test kadar hızlı görünüyor.


14
@KP: sonuçlar yalnızca bunları kendiniz test ederseniz veya çok sayıda kişi bunları doğrularsa kanıtlanır. @mkoistinen: Daha adil bir test yaptım , jsfiddle.net/GM8nk . Komut dosyasını Chrome 5'te birkaç kez çalıştırdıktan sonra, kazanan bir kazanan olmadığını görebiliyordum. Üç yenileme sonrasında da üç varyasyon diğerlerinden daha iyi performans gösterdi. Benden -1, korkarım. Bunu başka tarayıcılarda da çalıştırmak isteyebileceğinizi unutmayın . IE ve Fx 100 milyon iterasyondan hoşlanmadı.
Andy E

1
@AndyE. Vay be, bu basit teste dayanarak, IE 100X daha fazla berbat? =)
mkoistinen

2
Sonuçlar benim için her yerde, bazen belirgin hız farklılıkları olsa da, çapraz tarayıcı kazananı yok. Tuhaf. Sanırım Andy'nin kemanı daha iyi bir test, her adayı kendi işlevine sokuyor ... kesinlikle orijinal komut dosyası bir fonksiyonun dışında çalıştırılırsa, gerçekten varküresel olarak bir değişken olarak ilan ettiği gibi bir şey test etmemeliydi. yine de küresel olun.
bobince

4
Aslında bir yıl sonra, ama SHAPOW
sdleihssirhc

2
Bu benim değil, ama bazılarınızın ilgileneceğini düşündüm: jsperf.com/var-in-for-loop
m1.

1

JavaScript altta C veya C ++ ile yazılmış bir dildir, hangisinin olduğundan emin değilim. Amaçlarından biri de dahili hafızayı kullanma lavourunu kurtarmaktır. C veya C ++ 'da bile, bir döngü içinde değişkenler bildirildiğinde çok fazla kaynak tüketip tüketmeyeceği konusunda endişelenmenize gerek yoktur. JavaScript için neden endişelenmelisiniz?


1
C veya C ++, JavaScript'in altında olabilir. Ancak unutmamalıyız ki, tarayıcı JavaScript'i temel dile dönüştürür (C, C ++). Performans tarayıcıya bağlıdır
Kira

3
@Kira Aslında C / C ++ 'a dönüştürülmez. Javascript, JS çalışma zamanı (yani tarayıcıda çalışan bir sanal makine) tarafından yürütülen bir dizi talimatta derlenir. Aynı prensip Python ve Ruby gibi diğer dinamik diller için de geçerlidir.
Anthony E

@AnthonyE, Bilgi için teşekkürler. JS C veya C ++ dönüştürme emin değildi. Kullanmış Yani olabilir benim yorumunda
Kira

0

Bu, neyi başarmaya çalıştığınıza bağlıdır ... valuedöngü bloğunun içinde sadece geçici bir değişken olduğunu varsayalım, o zaman ikinci formu kullanmak çok daha açıktır. Aynı zamanda daha mantıklı ve ayrıntılı.


Geçici değişkenler de dahil olmak üzere tüm değişken bildirimini en üste itmenin, sadece 'gürültülü' olduğu için karışıklığa yol açabileceğini buldum.
Daniel Sokolowski

0

Döngü içinde veya dışında değişkenler bildirirseniz fark etmez. Test edilecek örnek kod aşağıdadır.

function a() {
   console.log('Function a() starts');
   console.log(new Date());
    var j;
    for (j=0; j<100000000; j++) {
        var x;
        x = x + 1;
    }
    console.log(new Date());
    console.log('Function a() Ends');
}
a()
function b() {
console.log('Function B() starts');
   console.log(new Date());
    var a;
    var j;
    for (j=0; j<100000000; j++) {
      a = a + 1;
    }
    console.log(new Date());
    console.log('Function B() Ends');
}
b()

Benim durumumda gösterilen sonuçlar

Function a() starts
VM121:3 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:9 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:10 Function a() Ends
VM121:14 Function B() starts
VM121:15 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:21 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:22 Function B() Ends

Teşekkür ederim - MyFavs.in


her iki durumda da j'yi döngü dışında bildirirsiniz! X_x
john ktejik

Chromium 81 ile letyerine denedim varve a()biraz daha yavaş olma eğilimindedir (120 vs 115 ms = ~% 6 = IMO önemsiz gibi)
mikiqex

-1

Buradaki soru temelde bir döngü içinde bir değişken bildirmektir. Bunu yaparsanız ne olacağını düşünün:

var a = 30;
var a = 50;
var a = 60;

Bunun doğru olduğunu düşünüyor musun? Hayır ... çünkü bir değişkeni defalarca bildirmek istemiyorsunuz. Döngü içindeki bir değişkeni bildirdiğinizde, döngü çalıştığında kaç kez bildirilmiyor? Açıkçası 'katı kullanın' modundayken sizi tokatlayacaktır. İnsanlar orijinal soruyu düşünmeden Crockford'la aynı fikirde değiller.

Bu yüzden değişkenleri en üste beyan etmek her zaman iyidir - 1. Okunabilirlik için 2. İyi alışkanlıklar kazanmak.


1
"Bir döngü içindeki bir değişkeni bildirdiğinizde, döngü çalıştığında kaç kez bildirilmez?" <- Hayır, bu doğru değil. Değişken bildirimi kaldırılır, böylece kalan tek şey atamadır.
Matthias

-2

Bir Linux işletim sisteminde Chrome, Firefox ve jsperf üzerinde test yaptıktan sonra performansla ilgili olarak, bir döngüdeki değişkenlerin bildirimi ile döngü dışı arasında bir performans farkı var gibi görünüyor. Küçük bir farktır, ancak bu aynı zamanda yineleme miktarı ve değişken bildirimlerin miktarı ile de birleştirilir.

Bu nedenle en iyi performans için değişkenlerin döngü dışında bildirilmesini öneririm. Ya da daha iyisi, değişkenlerinizi sıra ile bildiriniz. Örneğe bakın.

// inline
for (var ai = 0, al = 100000000, av; ai < al; ai++) {
    av = av + 1;
}

// outside
var bv;
var bl = 100000000;
for (var bi = 0; bi < bl; bi++) {
    bv = bv + 1;
}

'Al' ve 'av' değişkeninin for döngüsü bildirim satırında nasıl olduğuna dikkat edin. Bu satır içi bildirim bana sürekli olarak daha iyi performans sağladı. Döngü dışındaki değişkenlerin beyanı üzerinde bile. Yine performans farkı gerçekten küçük.

https://jsperf.com/outside-inline-for-loop-ase/1


Benim için testin döngü içinde verdi. Ve bu olmadı, fark sonuçlandırmak için çok küçük ve kabul edilen cevap açıkça bir fark olmadığını
Ulysse BN

Değişken bildirimler kaldırıldığından, gerçekten hiçbir fark yoktur.
trincot
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.