2012-04-02 24 views
6

JavaScript OOP sorunu fikirli.JavaScript sınıfları ve değişken referanslar

Yani şu sınıf var:

var ClassA = function() { 
    this.initialize(); 
} 

ClassA.prototype = { 

    methods : ['alpha','beta','gama'], 

    initialize : function() { 
     for (var i in this.methods) { 
      this[this.methods[i]] = function() { 
       console.log(this.methods[i]); 
      } 
     } 
    } 
} 

var a = new ClassA(); 

Birazdan, onun adını yazdırmak için bekliyoruz her yöntem diyoruz?

a.alpha(); // returns gama ?!? 
a.beta(); // returns gama ?!? 
a.gama(); // returns gama 

Ama sınıf bu benzediği zaman:

var ClassB = function() { 
    this.initialize(); 
} 

ClassB.prototype = { 

    methods : ['alpha', 'beta', 'gama'], 

    initialize: function() { 
     for (var i in this.methods) { 
      this.addMethod(this.methods[i]); 
     } 
    }, 

    addMethod: function(method) { 
     this[method] = function() { 
      console.log(method); 
     } 
    } 

} 

var b = new ClassB(); 

b.alpha(); // returns alpha 
b.beta(); // returns beta 
b.gama(); // returns gama 

neden oluyor Ama burada ben almak nedir?

+0

JS bir dizi döngü bu [yanlış bir şekilde] (http://stackoverflow.com/questions/3010840/loop-through-array-in-javascript#answer-3010848) değil mi? – PeeHaa

+0

@RepWhoringPeeHaa - Evet, düz bir döngü kullanılmalı, ancak sorun bu değil. – nnnnnn

+0

Ben böyle bir soruya cevap Yorum ve – PeeHaa

cevap

6
for (var i in this.methods) { 
     this[this.methods[i]] = function() { 
      console.log(this.methods[i]); 
     } 
} 

Sorununuz burada yatar. Bu döngü bittiğinde, son eleman i'dur. Her işlev aynı i kullanır, bu yüzden hepsi son öğe.

addMethod'u kullandığınızda, doğru değeri "yakalamak" için bir kapatma yapıyorsunuz demektir.

DÜZENLEME: addMethod'u aradığınızda, her döngü yinelemesinde değişiklik gösteren i değerini kullanmak yerine değeri "kopyalarsınız". İlk versiyonda

+0

:-) değil bir cevaptır bu yüzden biliyorum @nnnnnn hemen hemen her gün ... http://stackoverflow.com/questions/9980209/register-onclick-events-from -dynamically oluşturulan-div-dizi-raylar-jquery/9980579 # 9980579 –

+0

kafam karıştı ... sadece başka işlevi console.log ve addMethod tarafından ben sadece başka birinin içine etrafında sarmalanmış ediyorum değil mi? – drinchev

+0

@drinchev: 'console.log' burada önemli değil. Burada önemli olan, "addMethod" dediğinizde, değeri her bir işleve kopyalamanızdır, ancak siz bunu yapmadan yaptığınızda aynı değeri kullanıyor olmanızdır. (Üzgünüm düzgün açıklayamıyorum ediyorsam.) O onu kapsayan kapsamında şey başvuran yeni kapsamını yapar çünkü –

3

:

initialize : function() { 
    for (var i in this.methods) { 
     this[this.methods[i]] = function() { 
      console.log(this.methods[i]); 
     } 
    } 
} 

Eğer initialize tüm dahilinde oluşturmak yöntemleri initialize aynı i değişkene atıfta - ve initialize ishal i sonra bakılmaksızın değeri "gama" vardır, bu yüzden yöntemlerden biri diyorsunuz bu, i değerinin konsola giriş yapacağı değerdir. JS, yöntem oluşturulduğunda geçerli değeri i depolamaz.

JS her bir işlev için bir "kapatma" oluşturur - initialize işlevinde Açıklanan değişkenler (yani i) bittikten bile initialize sonra iç içe fonksiyonu (ler) için kapsam olmaya devam ediyor.

ikinci versiyonu her yöntemi eklemek için addMethod çağırır:

addMethod: function(method) { 
    this[method] = function() { 
     console.log(method); 
    } 
} 

... ve bu yüzden daha sonra ayrı bir kapaması mevcut olduğundan onlar method parametrenin kendi "kopya" referans alırız çalıştırdığınızda her yöntem için.

Düzenleme: Ayrıca şu soruya da bakınız: How do JavaScript closures work? (Buradaki bazı cevaplar bunu benden daha açık bir şekilde açıklamaktadır).

1

Sen anonim kapatma ekleyerek ilk örneğini düzeltebilirsiniz:

initialize : function() { 
    for (var i in this.methods) { 
     (function (i) { // anonymous closure 
      this[this.methods[i]] = function() { 
       console.log(this.methods[i]); 
      } 
     }).call(this, i); // use .call() if you need "this" inside 
    } 
} 

Şimdi sıra ikinci örnekle aynı şekilde çalışacaktır. "Anonim", kapamanın bir isme sahip olmayan ve "oluşturulduğu" anında çağrılan işlev tarafından yapıldığı anlamına gelir.

Not yanlara: denilen işlevi içinde this korumak için .call(this, ...) kullanabilir veya var that = this yapabilirsiniz yerine this ait that kullanmak ve normal işlevini çağırır:

for (var i in this.methods) { 
    var that = this; 
    (function (i) { // anonymous closure 
     that[that.methods[i]] = function() { 
      console.log(that.methods[i]); 
     } 
    })(i); // Called normally so use "that" instead of "this"! 
} 
+1

Sorunu zaten çözdü, ilk etapta bunun neden sorun olduğunu bilmek istedi. –

+0

Evet, ancak bu şekilde düzeltmek daha basit, daha basit. – TMS

+0

Katılıyorum ama yine de asıl soruya cevap vermiyor. –

0

Peki, her şeyden önce için kullanmayı bırakın (nesnede özellik) diziler üzerinde döngüler. Birisi, hem mantıklı hem de çok kullanışlı/popüler bir şey olan Array nesnesine prototip olana kadar tüm eğlence ve oyunlar. Bu, dizi döngülerinde x için eklediğiniz özel yöntemlerle sonuçlanacaktır.

Sorun, tam olarak sürüm 1'de anlattığınız şeyi yapıyor. Sorun şu ki, ateş etmeye başladığınız zamana kadar, en son yaptığım şey, 'gamma'. Bir işlevi argüman olarak bir işleve ilettiğinizde, işlev, geçirildiği haliyle değerinin durumuna geçer.