2010-04-19 14 views
9

Tengo un objeto único que utilizar otro objeto (no Singleton), para requerir algo de información al servidor:JavaScript 'clase' y los problemas únicos

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 
    } 
    }; 
}()); 

Ésta es la 'clase' que hace la solicitud a la servidor

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 
    } 

}

El problema es que no puedo llamar myRequestManager.require desde el interior de objeto único. Firebug consolle dice: "myRequestManager.require no es una función", pero no entiendo dónde está el problema. ¿Existe una mejor solución para implementar esta situación?

+0

No recibí el error ... ¿De dónde viene 'params' cuando construye el RequestManager? –

+0

En la muestra de código que proporciona, params no está definido en ninguna parte. ¿Dejaste algo fuera de tu ejemplo? – Robusto

Respuesta

6

Su código está en el orden en que lo citó, ¿no es así? ¿El singleton aparece sobre el RequestManager en la fuente?

Si es así, ese es su problema. (!) Es bastante sutil, pero asumiendo sus dos bits de código indicadas son en el orden en que se ha mostrado, aquí está el orden en el que las cosas suceden (lo explicaré más adelante):

  1. La función RequestManager se define.
  2. Su función anónima que crea las ejecuciones de singleton, incluida la instancia de una instancia de RequestManager.
  3. El prototipo RequestManager se reemplaza por uno nuevo.

Dado que la instancia se crea una instancia myRequestManagerantes se cambió el prototipo, que no tiene las funciones que ha definido en ese (nuevo) prototipo. Continúa utilizando el objeto prototipo que estaba en su lugar cuando se creó una instancia.

Puedes solucionar este problema fácilmente reordenando el código, o añadiendo propiedades a RequestManager 's prototipo en lugar de reemplazarlo, por ejemplo:

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

que funciona porque tiene no sustituyen la objeto prototipo, que acaba de agregar. myRequestManager ve las adiciones porque las ha agregado al objeto que está usando (en lugar de establecer un nuevo objeto en la propiedad prototype de la función del constructor).

Por qué esto sucede es un poco técnico y voy a ceder a la especificación. Cuando el intérprete ingresa a un nuevo "contexto de ejecución" (por ejemplo, una función, o el   — global, por ejemplo, contexto de página   —), el orden en el que hace las cosas no es estricto orden de origen descendente, hay fases . Una de las primeras fases es crear una instancia de todas las funciones definidas en el contexto; sucede antes de se ejecuta cualquier código paso a paso. Detalles en todo su esplendor en las secciones 10.4.1 (código global), 10.4.3 (código de función) y 10.5 (enlaces de declaración) en the spec, pero básicamente, las funciones se crean antes de la primera línea del código paso a paso.:-)

Esto es más fácil de ver con un ejemplo aislado de prueba:

<!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> 

Como se puede ver si se ejecuta, User1 falla porque el Thing1 instancia que está usando no tiene una propiedad foo (porque el prototipo fue reemplazado), pero User2 funciona porque la instancia Thing2 usa * does (porque el prototipo fue aumentado, no reemplazado).

+1

+1 ¡La trampa de elevación de JavaScript ataca de nuevo! – bobince

+1

Gracias por la explicación y los ejemplos, no obtuve la diferencia entre el prototipo aumentado y el prototipo reemplazado. Ahora funciona :-) –

+1

@Kucebe: ¡Genial! Sí, es sutil. :-) No solo son prototipos, es el orden en que suceden las cosas que te morderán a veces (sin duda tiene * me *). –

Cuestiones relacionadas