如何使用任意原型创建可调用的JS对象?

时间:2009-02-14 04:34:35

标签: javascript functional-programming

  

可能重复:
  Can a JavaScript object have a prototype chain, but also be a function?

我正在寻找一个可调用的JavaScript对象,使用任意原型链,但不修改Function.prototype。

换句话说,这必须起作用:

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

不做任何全局变更。

4 个答案:

答案 0 :(得分:12)

没有什么可以阻止你向函数添加任意属性,例如。

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和Safari的浏览器(因此camino,chrome等工作)。< / LI>
  • o.__proto__ = f.__proto__;仅对函数原型函数(如function.toString)非常必要,因此您可能只想跳过它,特别是如果您期望o拥有有意义的原型。

答案 1 :(得分:3)

  

我正在寻找一个可调用的JavaScript对象,使用任意原型链,但不修改Function.prototype。

我认为没有一种可移植的方法可以做到这一点:

您必须设置函数对象的[[Prototype]]属性或将[[Call]]属性添加到常规对象。第一个可以通过非标准__proto__属性完成(参见olliej's answer),据我所知,第二个属性是不可能的。

[[Prototype]]只能在对象创建期间通过构造函数的prototype属性进行设置。不幸的是,据我所知,没有JavaScript实现可以暂时重新分配Function.prototype

答案 2 :(得分:1)

我最接近的浏览器是这个(在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

答案 3 :(得分:0)

你不能以便携的方式做到这一点。但是,如果您考虑一下,如果delete foo.x;的目的是重置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中测试过,但这应该适用于所有浏览器。)

在标有[*]的行中,对[{1}}的调用确实没有必要,但是它可以证明如果你不小心调用它并不重要,并且这可以推广到一个属性很容易。

请注意,在我们的可调用对象reset的函数体中将引用包含范围,这可能对我们没有用,因为我们希望函数体访问对象成员。要缓解这个问题,请将其包装在一个这样的闭包中:

this