Neden bir işlevi JavaScript'te tanımlanmadan önce kullanabilirim?


168

Bu kod, farklı tarayıcılarda bile her zaman çalışır:

function fooCheck() {
  alert(internalFoo()); // We are using internalFoo() here...

  return internalFoo(); // And here, even though it has not been defined...

  function internalFoo() { return true; } //...until here!
}

fooCheck();

Yine de neden çalışması gerektiğine dair tek bir referans bulamadım. Bunu ilk olarak John Resig'in sunum notunda gördüm, ancak sadece bahsedildi. Bu konuda orada veya hiçbir yerde bir açıklama yok.

Birisi beni aydınlatabilir mi?


3
Firefox'un daha yeni sürümlerinde, kod bir try / catch durumundaysa bu çalışmaz. Bu kemanı görün: jsfiddle.net/qzzc1evt
Joshua Smith

Yanıtlar:


217

functionDeklarasyon sihirli ve onun tanımlayıcı * yürütülür onun kod bloğunda şeyden önce tabi olmayı neden olur.

Bu function, normal yukarıdan aşağıya sıralanan bir ifadeye sahip bir ödevden farklıdır .

Örneği şu şekilde değiştirdiyseniz:

var internalFoo = function() { return true; };

çalışmayı bırakırdı.

İşlev bildirimi, neredeyse aynı görünseler ve bazı durumlarda belirsiz olabilse de işlev ifadesinden işlev ifadesinden oldukça farklıdır.

Bu, ECMAScript standardı , bölüm 10.1.3'te belgelenmiştir . Ne yazık ki ECMA-262, standartlar tarafından bile okunabilir bir belge değildir!

*: içeren fonksiyon, blok, modül veya script.


Sanırım gerçekten okunamıyor. Sadece 10.1.3'ü işaret ettiğiniz bölümü okudum ve oradaki hükümlerin neden bu davranışa neden olacağını anlamadım. Bilgi için teşekkürler.
Edu Felipe

2
@bobince Tamam, bu sayfada “kaldırma” teriminin tek bir sözünü bulamadığım zaman kendimden şüphe etmeye başladım. İnşallah bu yorumlar işleri
düzeltmek

2
Bu popüler bir soru / cevap kombinasyonudur. ES5 açıklamalı spesifikasyonuna bir bağlantı / alıntı ile güncelleme yapmayı düşünün. (Bu biraz daha erişilebilir.)

1
Bu makalede bazı örnekler bulunmaktadır: JavaScript Kapsam Belirleme ve Kaldırma
Lombas

Tanımlamadan önce oldukça az kütüphane işlevini kullandığını gördüm, hatta bazı diller resmen izin veriyor, eski. Haskell. Dürüst olmak gerekirse, bu kötü bir şey olmayabilir, çünkü bazı durumlarda biraz daha etkileyici yazabilirsiniz.
windmaomao

28

Buna HOISTING - Bir işlevi tanımlanmadan önce çağırma (çağırma) adı verilir.

Hakkında yazmak istediğim iki farklı işlev türü:

İfade İşlevleri ve Bildirim İşlevleri

  1. İfade İşlevleri:

    İşlev ifadeleri, işlev adlarına ihtiyaç duymadan bir değişkende saklanabilir. Ayrıca anonim işlev (adsız bir işlev) olarak adlandırılacaktır.

    Bu işlevleri çağırmak (çağırmak) için her zaman değişken bir isme ihtiyaçları vardır . Bu tür bir işlev, tanımlanmadan önce çağrılırsa çalışmaz, bu da Kaldırma'nın burada gerçekleşmediği anlamına gelir. Her zaman önce ifade işlevini tanımlamalı ve sonra çağırmalıyız.

    let lastName = function (family) {
     console.log("My last name is " + family);
    };
    let x = lastName("Lopez");

    ECMAScript 6'da şöyle yazabilirsiniz:

    lastName = (family) => console.log("My last name is " + family);
    
    x = lastName("Lopez");
  2. Beyan Fonksiyonları:

    Aşağıdaki sözdizimiyle bildirilen işlevler hemen yürütülmez. "Daha sonra kullanılmak üzere kaydedilirler" ve çağrıldıklarında (çağrıldıklarında) daha sonra çalıştırılırlar. Bu tür bir işlev, tanımlanmış olandan ÖNCE veya SONRA çağırırsanız çalışır. Tanımlama işlevini tanımlanmadan önce çağırırsanız Kaldırma düzgün çalışır.

    function Name(name) {
      console.log("My cat's name is " + name);
    }
    Name("Chloe");

    Kaldırma örneği:

    Name("Chloe");
    function Name(name) {
       console.log("My cat's name is " + name);
    }

let fun = theFunction; fun(); function theFunction() {}ayrıca çalışır (Düğüm ve tarayıcılar)
fider

14

Tarayıcı HTML'nizi baştan sona okur ve çalıştırılabilir parçalar halinde (değişken bildirimleri, işlev tanımları vb.) Okunup ayrıştırıldığında yürütebilir. Ancak herhangi bir noktada komut dosyasında tanımlananları yalnızca bu noktadan önce kullanabilir.

Bu, tüm kaynak kodunuzu işleyen (derleyen), belki de referansları çözümlemek için ihtiyacınız olan herhangi bir kütüphaneyle birbirine bağlayan ve yürütme işleminin başladığı yürütülebilir bir modül oluşturan diğer programlama bağlamlarından farklıdır.

Kodunuz, daha sonra tanımlanmış olan adlandırılmış nesnelere (değişkenler, diğer işlevler, vb.) Atıfta bulunabilir, ancak tüm parçalar mevcut olana kadar yönlendirme kodunu yürütemezsiniz.

JavaScript'i tanıdıkça, bir şeyleri doğru sırayla yazma ihtiyacınızın farkında olacaksınız.

Düzeltme: Kabul edilen cevabı onaylamak için (yukarıda), bir web sayfasının komut dosyası bölümüne geçmek için Firebug'u kullanın. Herhangi bir kodu çalıştırmadan önce yalnızca ilk satırı ziyaret ederek işlevden işleve atladığını göreceksiniz.


3

Bazı diller kullanımdan önce tanımlayıcıların tanımlanması gerekliliğine sahiptir. Bunun bir nedeni, derleyicinin kaynak kodunda tek bir geçiş kullanmasıdır.

Ancak birden fazla geçiş varsa (veya bazı kontroller ertelenirse), bu gereksinim olmadan mükemmel bir şekilde yaşayabilirsiniz. Bu durumda, kod muhtemelen ilk önce okunur (ve yorumlanır) ve sonra bağlantılar ayarlanır.


2

Sadece biraz JavaScript kullandım. Bunun yardımcı olup olmayacağından emin değilim, ama konuştuğunuz şeye çok benziyor ve biraz fikir verebilir:

http://www.dustindiaz.com/javascript-function-declaration-ambiguity/


Bağlantı artık ölmedi.
mwclarke

1
Bağlantı öldü.
Jerome Indefenzo

İşte archive.org'dan bir anlık görüntü . Görünüşe göre yazar , eski blog içeriğine sahip olduğu için tüm web sitelerini indirmiş gibi görünüyor , bu blog gönderisi bu kategoriye girmiyor.
jkmartindale

1

"İnternalFoo" işlevinin gövdesinin ayrıştırma zamanında bir yere gitmesi gerekir, bu nedenle kod JS yorumlayıcısı tarafından okunduğunda (aka ayrıştırma), işlevin veri yapısı oluşturulur ve ad atanır.

Ancak daha sonra kod çalıştırılır, JavaScript aslında "internalFoo" nun var olup olmadığını ve ne olduğunu ve çağrılıp çağrılamayacağını vb.


-4

Aynı nedenden ötürü aşağıdakiler daima fooglobal ad alanına konacaktır :

if (test condition) {
    var foo;
}

8
Aslında, bunun çok farklı nedenleri var. ifBir süre blok, bir kapsam yaratmaz function()blok hep birini oluşturur. Gerçek neden, global javascript adlarının tanımının derleme aşamasında gerçekleşmesiydi, böylece kod çalışmazsa bile ad tanımlandı. (Üzgünüm yorum yapmak çok uzun sürdü)
Edu Felipe
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.