2009-02-14 10 views
15

Powiel możliwe:
Can a JavaScript object have a prototype chain, but also be a function?Jak utworzyć obiekt wywoływalny JS z dowolnym prototypem?

szukam dokonać wpłacone obiekt JavaScript, z dowolnego łańcucha prototypów, ale bez modyfikacji Function.prototype.

Innymi słowy, to musi działać:

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

bez dokonywania jakichkolwiek zmian w skali globalnej.

+0

podstawowego mechanizmu jest taka sama, ale to pytanie jest nieco inny (przykład zaczynając punkty są zupełnie inne), a odpowiedź dodaje znacznej wartości. Nominowany do ponownego otwarcia. – kanaka

Odpowiedz

12

Nic nie powstrzyma cię przed dodaniem dowolnych właściwości do funkcji, np.

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

Wierzę, że powinien zrobić to, co chcesz.

ten działa poprzez wstrzyknięcie obiekt o do łańcucha prototypów, jednak istnieje kilka rzeczy do odnotowania:

  • ja nie wiem, czy IE obsługuje __proto__, lub nawet ma odpowiednika, Frome niektórych na komentarze wygląda na to, że działa tylko w przeglądarkach Firefox i Safari (więc camino, chrome itp. działają również).
  • o.__proto__ = f.__proto__; jest niezbędny tylko dla funkcji prototypowych funkcji, takich jak function.toString, więc możesz po prostu go pominąć, szczególnie jeśli spodziewasz się, że o będzie miał znaczący prototyp.
+0

Masz! po cześć świat w twierdzeniu, więc jest fałszywy;) – some

+1

Nie działa w IE lub Opera 9.61 – some

+0

Bah, powinienem przetestować, ale właśnie skopiowałem twierdzenie z pierwotnego pytania – olliej

1

Najbliższy rzeczą przeglądarka krzyż doszedłem to tego (testowane w FF, IE, Crome i 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

Niestety, myślę, że będzie to modyfikacja prototypu funkcji globalnej: -/ – olliej

+0

@Olliej: Dlaczego? Tworzę nową pustą funkcję i modyfikuję jej prototyp, a nie globalny prototyp funkcji. – some

+0

Ach, tak, masz rację, moje złe - jednak nowe f tworzy obiekt, a nie funkcję, którą można nazwać. np. create (fun, obj)() nie zrobi tego, o co go proszono. Podejrzewam, że pytanie, o które prosi, nie jest możliwe w IE/Opera. – olliej

3

szukam dokonać wpłacone obiekt JavaScript, ze związkiem dowolny łańcuch prototypów, ale bez modyfikacji Function.prototype.

nie sądzę istnieje przenośny sposób to zrobić:

Musisz albo ustawić [[prototyp]] własności funkcja obiektu lub dodać [[CALL]] własności do zwykłego obiektu. Pierwszy z nich można wykonać za pomocą niestandardowej właściwości __proto__ (patrz: olliej's answer), z drugiej strony jest to niemożliwe, o ile wiem.

Opcja [[Prototyp]] może zostać ustawiona tylko przenośnie podczas tworzenia obiektu za pomocą właściwości funkcji konstruktora o wartości prototype. Niestety, o ile mi wiadomo, nie ma implementacji JavaScript, która pozwoliłaby tymczasowo zmienić przydział Function.prototype.

0

Nie można tego zrobić w sposób przenośny. Jeśli jednak się nad tym zastanowisz, jeśli celem jest delete foo.x; resetowanie wartości x, możesz podać reset() metodę na foo, która przywróci brakujące właściwości do ich wartości domyślnych.

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

(Testowane w Chromium i Internet Explorer, ale powinno działać we wszystkich przeglądarkach.)

W wierszu oznaczonym [*] połączenie z numerem reset nie jest konieczne, ale ma na celu wykazanie, że nie ma znaczenia, czy wywołasz je przypadkowo, i że generalizuje to z łatwością więcej niż jednej własności.

Należy zauważyć, że w ciele funkcji naszego obiektu wywoływalnego this będzie się odnosił do zakresu zawierającego, co prawdopodobnie nie będzie dla nas przydatne, ponieważ chcemy, aby ciało funkcji uzyskiwało dostęp do elementów obiektu. Aby to ograniczyć, zawinąć go w zamknięciu tak:

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