Как создать вызываемый JS-объект с произвольным прототипом?
Возможный дубликат:
Может ли объект JavaScript иметь цепочку прототипов, а также быть функцией?
Я хочу создать вызываемый JavaScript-объект с произвольной цепочкой прототипов, но без изменения Function.prototype.
Другими словами, это должно работать:
var o = { x: 5 };
var foo = bar(o);
assert(foo() === "Hello World!");
delete foo.x;
assert(foo.x === 5);
Без каких-либо глобальных изменений.
Ответы
Ответ 1
Вам нечего мешать добавлять какие-либо функции к функции, например.
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);
Я считаю, что нужно делать то, что вы хотите.
Это работает, введя объект o
в цепочку прототипов, однако есть несколько замечаний:
- Я не знаю, поддерживает ли IE
__proto__
или даже имеет эквивалент, из некоторых комментариев это выглядит только для работы в браузерах firefox и сафари (так работают и camino, chrome и т.д.).
-
o.__proto__ = f.__proto__;
действительно необходим только для функций прототипа функций, таких как function.toString, поэтому вы можете просто пропустить его, особенно если вы ожидаете, что o
будет иметь значимый прототип.
Ответ 2
Я хочу создать вызываемый JavaScript-объект с произвольной цепочкой прототипов, но без изменения Function.prototype.
Я не думаю, что есть переносной способ сделать это:
Вы должны либо установить свойство объекта [[Prototype]], либо добавить свойство [[Call]] к обычным объектам. Первый из них можно выполнить с помощью нестандартного свойства __proto__
(см. olliej answer), второй, насколько мне известно, невозможно.
[[Прототип]] можно настраивать только при создании объекта с помощью функции конструктора prototype
. К сожалению, насколько я знаю, нет реализации JavaScript, которая позволила бы временно переназначить Function.prototype
.
Ответ 3
Ближайшая кросс-браузерная вещь, к которой я пришел, - это (протестировано в FF, IE, Crome и 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
Ответ 4
Вы не можете сделать это переносимым способом. Однако если вы думаете об этом, если целью delete foo.x;
является reset значение x
, вы можете предоставить метод reset()
на foo
, который восстановит отсутствующие свойства по умолчанию.
// 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
(Протестировано в Chromium и Internet Explorer, но это должно работать во всех браузерах.)
В строке, помеченной [*]
, вызов reset
действительно не нужен, но он там, чтобы продемонстрировать, что это не имеет значения, если вы называете это случайно, и что это обобщает несколько свойств.
Обратите внимание, что в теле функции нашего вызываемого объекта this
будет ссылаться на область содержимого, которая, вероятно, не будет такой полезной для нас, так как мы хотим, чтобы тело функции обращалось к членам объекта. Чтобы уменьшить это, оберните его в закрытие следующим образом:
foo = (function()
{
var self = function()
{
self.x = 42;
};
return self;
})();
foo(); alert(foo.x); // 42