2010-04-19 17 views
9

Mam singleton obiektów, które używają innego obiektu (nie singleton), aby wymagać pewnych informacji na serwerze:JavaScript „klasa” i problemy Singleton

var singleton = (function(){ 

    /*_private properties*/ 
    var myRequestManager = new RequestManager(params, 
    //callbacks 
    function(){ 
     previewRender(response); 
    }, 
    function(){ 
     previewError(); 
    } 
); 

    /*_public methods*/ 
    return{ 

    /*make a request*/ 
    previewRequest: function(request){ 
     myRequestManager.require(request); //err:myRequestManager.require is not a func 
    }, 

    previewRender: function(response){ 
     //do something 
    }, 

    previewError: function(){ 
     //manage error 
    } 
    }; 
}()); 

to jest „klasa”, które sprawiają, że wniosek do serwer

function RequestManager(params, success, error){ 
    //create an ajax manager 
    this.param = params; 
    this._success = success; //callbacks 
    this._error = error; 
} 

RequestManager.prototype = { 

    require: function(text){ 
    //make an ajax request 
    }, 
    otherFunc: function(){ 
    //do other things 
    } 

}

problemem jest to, że nie mogę zadzwonić myRequestManager.require od wewnątrz obiektu singleton. Firebug consolle mówi: "myRequestManager.require nie jest funkcją", ale nie rozumiem, gdzie jest problem. Czy istnieje lepsze rozwiązanie dla wdrożenia tej sytuacji?

+0

Nie otrzymałem błędu ... Skąd pochodzą "parametry" podczas konstruowania menedżera RequestManager? –

+0

W przykładowym kodzie, który podajesz, nigdzie nie są zdefiniowane parametry. Czy zostawiłeś coś ze swojego przykładu? – Robusto

Odpowiedz

6

Twój kod jest w podanej kolejności, prawda? Singleton pojawia się nad źródłem w RequestManager?

Jeśli tak, to twój problem. (!) To dość subtelna, ale zakładając, że dwa bity kodu podane są w kolejności Pokazaliśmy im, oto kolejność rzeczy się zdarzają (będę to wyjaśnić więcej poniżej):

  1. Funkcja RequestManager jest zdefiniowany.
  2. Twoja anonimowa funkcja, która tworzy pojedyncze przebiegi, włącznie z instancją instancji RequestManager.
  3. Prototyp RequestManager zostaje zastąpiony nowym.

Ponieważ instancja myRequestManager została instancja przed prototyp został zmieniony, nie ma funkcje zdefiniowane na tej (nowej) prototypu. W dalszym ciągu używa obiektu prototypowego, który był na miejscu, kiedy został utworzony.

Można to łatwo naprawić przez ponowne zamawianie kod, albo przez dodanie właściwości prototypu RequestManager „s zamiast zastępując ją np:

RequestManager.prototype.require = function(text){ 
    //make an ajax request 
}; 
RequestManager.prototype.otherFunc = function(){ 
    //do other things 
}; 

To działa, ponieważ nie masz zastąpiony się prototypowy obiekt, właśnie go dodałeś. myRequestManager widzi dodatki, ponieważ dodałeś je do obiektu, którego używa (zamiast ustawiania nowego obiektu we właściwości funkcji konstruktora: prototype).

Dlaczego tak się dzieje jest nieco techniczny i przeważnie odroczę do specyfikacji. Kiedy interpreter wprowadza nowy "kontekst wykonania" (np. Funkcję lub globalny kontekst, np. Strona-strona —), kolejność, w jakiej to robi, nie jest ścisłym odgórnym porządkiem źródłowym, są fazy . Jedną z pierwszych faz jest utworzenie wszystkich funkcji zdefiniowanych w kontekście; tak się stanie przed wykonywany jest każdy krok po kroku. Szczegóły w całej ich okazałości w sekcjach 10.4.1 (kod globalny), 10.4.3 (kod funkcji) i 10.5 (powiązaniach deklaracji) w the spec, ale w zasadzie funkcje są tworzone przed pierwszą linią kodu krok po kroku.:-)

to najłatwiej zobaczyć z izolowanym przykładzie testowym:

<!DOCTYPE HTML> 
<html> 
<head> 
<meta http-equiv="Content-type" content="text/html;charset=UTF-8"> 
<title>Test Page</title> 
<style type='text/css'> 
body { 
    font-family: sans-serif; 
} 
</style> 
<script type='text/javascript'> 
// Uses Thing1 
var User1 = (function() { 
    var thing1 = new Thing1(); 

    function useIt() { 
     alert(thing1.foo()); 
    } 

    return useIt; 
})(); 

// Uses Thing2 
var User2 = (function() { 
    var thing2 = new Thing2(); 

    function useIt() { 
     alert(thing2.foo()); 
    } 

    return useIt; 
})(); 

// Thing1 gets its prototype *replaced* 
function Thing1() { 
    this.name = "Thing1"; 
} 
Thing1.prototype = { 
    foo: function() { 
     return this.name; 
    } 
}; 

// Thing2 gets its prototype *augmented* 
function Thing2() { 
    this.name = "Thing2"; 
} 
Thing2.prototype.foo = function() { 
    return this.name; 
}; 

// Set up to use them 
window.onload = function() { 
    document.getElementById('btnGo').onclick = go; 
} 

// Test! 
function go() { 

    alert("About to use User1"); 
    try 
    { 
     User1(); 
    } 
    catch (e) 
    { 
     alert("Error with User1: " + (e.message ? e.message : String(e))); 
    } 

    alert("About to use User2"); 
    try 
    { 
     User2(); 
    } 
    catch (e) 
    { 
     alert("Error with User2: " + (e.message ? e.message : String(e))); 
    } 
} 

</script> 
</head> 
<body><div> 
<div id='log'></div> 
<input type='button' id='btnGo' value='Go'> 
</div></body> 
</html> 

Jak widać, jeśli go uruchomić, User1 powiodło się, ponieważ instancja Thing1 jest za pomocą nie posiada własności foo (ponieważ prototyp został zastąpiony), ale User2 działa, ponieważ instancja Thing2 używa *, ponieważ prototyp został rozszerzony, a nie zastąpiony.

+1

+1 Eksplodująca pułapka JavaScript uderza ponownie! – bobince

+1

Dzięki za wyjaśnienia i przykłady, nie dostałem różnicy między prototypem a prototypem zastąpionym prototypem. Teraz działa :-) –

+1

@Kucebe: Świetnie! Tak, to subtelne. :-) Nie tylko prototypy, to kolejność, w której zdarzają się sytuacje, które cię czasami gryzą (na pewno ma * mnie *). –