2009-02-14 10 views
15

Duplicar posible:
Can a JavaScript object have a prototype chain, but also be a function?¿Cómo puedo hacer un objeto JS llamable con un prototipo arbitrario?

Estoy buscando para hacer un objeto JavaScript exigible, con una cadena de prototipo arbitraria, pero sin modificar Function.prototype.

En otras palabras, esto tiene que trabajar:

var o = { x: 5 }; 
var foo = bar(o); 
assert(foo() === "Hello World!"); 
delete foo.x; 
assert(foo.x === 5); 

Sin hacer ningún cambio a nivel mundial.

+0

El mecanismo subyacente es el mismo, pero esta pregunta es un poco diferente (el ejemplo puntos de partida son muy diferentes) y la respuesta añade un valor significativo. Nominado para reapertura. – kanaka

Respuesta

12

No hay nada que le impida agregar propiedades arbitrarias a una función, por ej.

function bar(o) { 
    var f = function() { return "Hello World!"; } 
    o.__proto__ = f.__proto__; 
    f.__proto__ = o; 
    return f; 
} 

var o = { x: 5 }; 
var foo = bar(o); 
assert(foo() === "Hello World!"); 
delete foo.x; 
assert(foo.x === 5); 

Creo que debería hacer lo que quiera.

Esto funciona inyectando el objeto o en la cadena de prototipo, sin embargo, hay algunas cosas a tener en cuenta:

  • No sé si IE apoya __proto__, o incluso tiene un equivalente, Frome los comentarios de algunos esto parece funcionar solo en los navegadores basados ​​en safor y firefox (así que camino, cromo, etc. también funcionan).
  • o.__proto__ = f.__proto__; solo es realmente necesario para las funciones del prototipo de función como function.toString, por lo que es posible que desee omitirlo, especialmente si espera que o tenga un prototipo significativo.
+0

¡Tienes a! después de hola mundo en la afirmación, por lo que es falso;) – some

+1

No funciona en IE u Opera 9.61 – some

+0

Bah, debería haber probado, pero acabo de copiar la afirmación de la pregunta original – olliej

1

Lo más cerca que navegadores que he llegado es esta (probado en FF, IE, Crome y Opera):

function create(fun,proto){ 
    var f=function(){}; 
    //Copy the object since it is going to be changed. 
    for (var x in proto) 
     f.prototype[x] = proto[x]; 
    f.prototype.toString = fun; 
    return new f; 
} 
var fun=function(){return "Hello world";} 
var obj={x:5} 

var foo=create(fun,obj); 
foo.x=8; 
alert(foo); //Hello world 
alert(foo.x); // 8 
delete foo.x; 
alert(foo.x); // 5 
+0

Desafortunadamente, creo que se modificará el prototipo de función global: -/ – olliej

+0

@Olliej: ¿Por qué? Estoy creando una nueva función vacía y modificando su prototipo, no el prototipo de Función global. – some

+0

Ah, sí, tiene razón, es malo, por más nuevo que sea crear un objeto, no una función que pueda llamarse. por ej. create (fun, obj)() no hará lo que se le pidió. Sospecho que la pregunta no es posible en IE/Opera. – olliej

3

Estoy buscando para hacer un objeto JavaScript exigible, con una cadena de prototipos arbitraria, pero sin modificar Function.prototype.

No creo que hay una manera portátil para hacer esto:

Debe alguno de los conjuntos [[Prototype]] propiedad de un objeto de función o añadir una propiedad [[Call]] a un objeto de regular. El primero se puede hacer a través de la propiedad no estándar __proto__ (ver olliej's answer), el segundo es imposible hasta donde yo sé.

El [[Prototype]] solo se puede establecer de forma portátil durante la creación del objeto mediante la propiedad prototype de una función del constructor. Desafortunadamente, hasta donde sé, no hay implementación de JavaScript que permita reasignar temporalmente Function.prototype.

0

No puede hacerlo de forma portátil. Sin embargo, si lo piensa, si el propósito de delete foo.x; es restablecer el valor de x, podría proporcionar un método reset() en foo que restaurará las propiedades faltantes a sus valores predeterminados.

// Object creation and initialisation 
(foo=function() 
{ 
    alert("called"); 
}).reset = function() 
{ 
    if(!("x"in this)) this.x=42; 
}; 
foo.reset(); 

// Using our callable object 
          alert(foo.x); // 42 
foo();      alert(foo.x); // called; 42 
foo.x=3;  foo.reset(); alert(foo.x); // 3 [*] 
delete foo.x; foo.reset(); alert(foo.x); // 42 

(Probado en Chromium e Internet Explorer, pero debería funcionar en todos los navegadores.)

En la línea marcada con [*], la llamada a reset no es realmente necesaria, pero está ahí para demostrar que no importa si la llama accidentalmente, y que esto se generaliza fácilmente a más de una propiedad.

Tenga en cuenta que en el cuerpo de la función de nuestro objeto invocable this se referirá al ámbito que contiene, que probablemente no nos sea útil ya que querremos que el cuerpo de la función acceda a los miembros del objeto. Para mitigar esto, envolverlo en un cierre de la siguiente manera:

foo = (function() 
{ 
    var self = function() 
    { 
     self.x = 42; 
    }; 
    return self; 
})(); 
foo(); alert(foo.x); // 42 
Cuestiones relacionadas