2009-02-14 14 views
15

Eventuali duplicati:
Can a JavaScript object have a prototype chain, but also be a function?Come si crea un oggetto JS richiamabile con un prototipo arbitrario?

Sto cercando di fare un oggetto JavaScript callable, con una catena di prototipi arbitraria, ma senza modificare Function.prototype.

In altre parole, questo deve lavorare:

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

senza apportare modifiche a livello globale.

+0

Il meccanismo sottostante è lo stesso, ma questa domanda differisce leggermente (nell'esempio punti di partenza sono molto diverse) e la risposta aggiunge valore significativo. Nominato per la riapertura. – kanaka

risposta

12

Non c'è nulla che impedisca di aggiungere proprietà arbitrarie a una funzione, ad es.

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

Credo che dovrebbe fare quello che vuoi.

Questo funziona iniettando l'oggetto o nella catena di prototipi, tuttavia ci sono alcune cose da notare:

  • Non so se IE supporta __proto__, o addirittura ha un equivalente, Frome di alcuni commenti questo sembra funzionare solo con i browser basati su Firefox e Safari (quindi funzionano anche Camino, Chrome, ecc.).
  • o.__proto__ = f.__proto__; è veramente necessario solo per funzioni di prototipo di funzione come function.toString, quindi potresti voler semplicemente saltarlo, specialmente se ti aspetti che o abbia un prototipo significativo.
+0

Hai un! dopo ciao mondo nell'affermazione, quindi è falso;) – some

+1

Non funziona in IE o Opera 9.61 – some

+0

Bah, avrei dovuto provare, ma ho appena copiato l'asserita dalla domanda originale – olliej

1

Il più vicino cosa cross browser sono venuto è questo (testato in FF, IE, Crome e 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

Purtroppo penso che modificherà il prototipo della funzione globale: -/ – olliej

+0

@Olliej: perché? Sto creando una nuova funzione vuota e modificando il suo prototipo, non il prototipo della funzione globale. – some

+0

Ah, sì, hai ragione, mio ​​male - tuttavia la nuova f sta creando un oggetto, non una funzione che può essere chiamata. es. creare (fun, obj)() non farà ciò che è stato chiesto. Sospetto che la domanda non sia possibile in IE/Opera. – olliej

3

Sto cercando di fare un oggetto JavaScript callable, con una catena di prototipi arbitraria, ma senza modificare Function.prototype.

Non credo ci sia un modo portatile per fare questo:

è necessario impostare la proprietà di un oggetto funzione [[Prototype]] o di aggiungere una proprietà [[Chiamata]] per un oggetto normale. Il primo può essere fatto tramite la proprietà non standard __proto__ (vedere olliej's answer), il secondo è impossibile per quanto ne so.

Il [[Prototipo]] può essere impostato solo durante la creazione dell'oggetto tramite la proprietà prototype della funzione costruttore. Purtroppo, per quanto ne so, non esiste un'implementazione JavaScript che consenta di riassegnare temporaneamente Function.prototype.

0

Non puoi farlo in modo portatile. Tuttavia, se ci pensate, se lo scopo di delete foo.x; è quello di ripristinare il valore di x, è possibile fornire un metodo reset() su foo che ripristinerà le proprietà mancanti ai valori predefiniti.

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

(Testato in Chromium e Internet Explorer, ma questo dovrebbe funzionare in tutti i browser.

Nella linea contrassegnata con [*] la chiamata a reset non è realmente necessaria, ma è lì per dimostrare che non importa se la si chiama accidentalmente, e che questo generalizza a più di una proprietà facilmente.

Si noti che nel corpo della funzione del nostro oggetto callable this si farà riferimento all'ambito di contenimento, che probabilmente non ci sarà molto utile dal momento che desideriamo che il corpo della funzione acceda ai membri dell'oggetto. Per attenuare questo, avvolgerlo in una chiusura simile:

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