JavaScript işlev sırası: neden önemlidir?


106

Orijinal Soru:

JSHint , JavaScript'im, çağrıya göre daha aşağıda tanımlanan bir işlevi çağırdığında şikayet ediyor. Bununla birlikte, sayfam bir oyun içindir ve her şey indirilene kadar hiçbir işlev çağrılmaz. Öyleyse, kodumda sıralama işlevleri neden görünür?

DÜZENLEME: Cevabı bulduğumu düşünüyorum.

http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting

İçimde inliyorum. Görünüşe göre altı bin satırlık kodu yeniden sipariş etmek için BAŞKA bir gün geçirmem gerekiyor. Javascript ile öğrenme eğrisi hiç de dik değil, ancak çok uzun.


Güncellemedeki mükemmel referans için +1. Ve umarım bu sizi, kodunuzu gerçekten yeniden sipariş etmeniz gerekmediğine ikna eder. :)
awm

Yanıtlar:


294

tl; dr Her şey yüklenene kadar hiçbir şey aramıyorsanız, iyi olmalısınız.


Düzenleme: Bazı ES6 bildirimlerini de kapsayan bir genel bakış için ( let, const): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Scope_Cheatsheet

Bu garip davranış şunlara bağlıdır:

  1. İşlevleri nasıl tanımlarsınız ve
  2. Onları aradığınızda.

İşte bazı örnekler.

bar(); //This won't throw an error
function bar() {}

foo(); //This will throw an error
var foo = function() {}
bar();
function bar() {
    foo(); //This will throw an error
}
var foo = function() {}
bar();
function bar() {
    foo(); //This _won't_ throw an error
}
function foo() {}
function bar() {
    foo(); //no error
}
var foo = function() {}
bar();

Bunun nedeni, kaldırma denen bir şeydir !

İşlevleri tanımlamanın iki yolu vardır: İşlev bildirimi ve işlev ifadesi . Aradaki fark can sıkıcı ve çok küçük, bu yüzden biraz yanlış bir şey söyleyelim: Eğer böyle yazıyorsanız function name() {}, bu bir bildiridir ve bunu şöyle yazdığınızda var name = function() {}(veya bir dönüşe atanmış anonim bir işlev, bunun gibi şeyler), bir işlev ifadesi .

İlk olarak, değişkenlerin nasıl ele alındığına bakalım:

var foo = 42;

//the interpreter turns it into this:
var foo;
foo = 42;

Şimdi, işlev bildirimleri nasıl işlenir:

var foo = 42;
function bar() {}

//turns into
var foo; //Insanity! It's now at the top
function bar() {}
foo = 42;

varİfadeleri "atar" oluşturulmasını ait fooçok üstüne, ancak henüz buna değer atamaz. İşlev bildirimi sırada gelir ve son olarak bir değer atanır foo.

Peki ya bu?

bar();
var foo = 42;
function bar() {}
//=>
var foo;
function bar() {}
bar();
foo = 42;

Sadece beyan ait fooüstüne taşınır. Atama, yalnızca arama yapıldıktan sonra, bartüm kaldırma gerçekleşmeden önce olduğu yerde gelir.

Ve son olarak, özlülük için:

bar();
function bar() {}
//turns to
function bar() {}
bar();

Şimdi, fonksiyon ifadeleri ne olacak ?

var foo = function() {}
foo();
//=>
var foo;
foo = function() {}
foo();

Sadece düzenli değişkenler gibi, ilk fooedilir ilan o zaman bir değer atanır, kapsamı en yüksek noktasında.

İkinci örneğin neden bir hata verdiğini görelim.

bar();
function bar() {
    foo();
}
var foo = function() {}
//=>
var foo;
function bar() {
    foo();
}
bar();
foo = function() {}

Daha önce gördüğümüz gibi, sadece oluşturulması fookaldırılır, görev "orijinal" (kaldırılmamış) kodda göründüğü yere gelir. Çağrıldığında bar, daha önce foobir değer atanır, yani foo === undefined. Şimdi işlev gövdesinde, baryapıyormuşsunuz gibi undefined(), bu da bir hata verir.


Bunu araştırdığım için üzgünüm, ancak Array.prototype.someMethod = function () {} gibi aşırı yüklemeler kaldırıldı mı? Senaryomun sonunda bu tür şeyler varsa hatalar alıyorum.
Edge

Her şeyin yüklendiğinden nasıl emin oluyorsunuz ? Herhangi bir ortak uygulama var mı?
zwcloud

6

Temel nedeni bu size bilmiyor böylece JSLint dosya üzerinde tek geçiş yaptığı muhtemelen olacak böyle bir işlevi tanımlar.

İşlevler deyimi söz dizimini kullandıysanız

function foo(){ ... }

Aslında işlevi bildirdiğiniz yerde hiçbir fark yoktur (her zaman bildirim baştaymış gibi davranır).

Öte yandan, işleviniz normal bir değişken gibi ayarlanmışsa

var foo = function() { ... };

Başlatma işleminden önce çağırmayacağınızı garanti etmelisiniz (bu aslında bir hata kaynağı olabilir).


Tonlarca kodu yeniden sıralamak karmaşık olduğundan ve kendi başına bir hata kaynağı olabileceğinden, bir geçici çözüm aramanızı öneririm. JSLint'e önceden küresel değişkenlerin adını söyleyebileceğinizden eminim, böylece bildirilmemiş şeyler hakkında şikayet etmez.

Dosyanın başlangıcına bir yorum yapın

/*globals foo1 foo2 foo3*/

Veya bunun için bir metin kutusu kullanabilirsiniz. (Ayrıca, buna karışabilirseniz, bunu argümanlarda iç jslint işlevine aktarabileceğinizi düşünüyorum.)


Teşekkürler. Yani / * globals * / satırı çalışacak mı? İyi - JsHint'in benden hoşlanmasını sağlayacak her şey. Hala JavaScript konusunda yeniyim ve bir sayfayı yenilediğimde açıklanamayan duraklamalar alıyorum, ancak rapor edilen hata yok. Bu yüzden çözümün tüm kurallara göre oynamak ve sonra hala devam edip etmediğini görmek olduğunu düşündüm.
Chris Tolworthy

4

JavaScript'in nasıl yazılması gerektiğine dair keyfi kuralları zorlayan pek çok insan var. Çoğu kural tamamen saçmadır.

İşlev kaldırma JavaScript'te bir özelliktir çünkü iyi bir fikirdir.

Genellikle iç işlevlerin faydası olan bir iç işleviniz olduğunda, onu dış işlevin başlangıcına eklemek kabul edilebilir bir kod yazma tarzıdır, ancak neye ulaşmak için ayrıntıları okumanız gereken dezavantajı vardır. dış işlev yapar.

Kod tabanınız boyunca özel işlevleri modülünüzde veya işlevinizde önce veya son olarak yerleştirmeniz gerekir. JSHint tutarlılığı sağlamak için iyidir, ancak .jshintrc'yi KESİNLİKLE ihtiyaçlarınıza göre ayarlamalısınız, kaynak kodunuzu diğer insanların tuhaf kodlama kavramlarına göre AYARLAMAMALISINIZ.

Vahşi doğada görebileceğiniz bir kodlama stili, size hiçbir avantaj sağlamadığı ve yalnızca ağrıyı yeniden düzenleme olasılığını sağladığı için kaçınmanız gerekir:

function bigProcess() {
    var step1,step2;
    step1();
    step2();

    step1 = function() {...};
    step2 = function() {...};
}

Bu tam olarak kaldırmanın önlenmesi gereken işlevdir. Sadece dili öğrenin ve güçlü yönlerinden yararlanın.


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.