【问题标题】:How do I make a callable JS object with an arbitrary prototype? [duplicate]如何使用任意原型制作可调用的 JS 对象? [复制]
【发布时间】:2010-10-07 14:28:12
【问题描述】:

可能重复:
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);

不进行任何全局更改。

【问题讨论】:

  • 底层机制是一样的,但是这个问题略有不同(示例起点差异很大),答案增加了重要价值。被提名重新开放。

标签: javascript functional-programming


【解决方案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__,或者甚至有一个等效的,来自一些 cmets,这看起来只能在基于 firefox 和 safari 的浏览器中工作(所以 camino、chrome 等也可以工作)。
  • o.__proto__ = f.__proto__; 仅对 function.toString 之类的函数原型函数真正需要,因此您可能想跳过它,尤其是如果您希望 o 有一个有意义的原型。

【讨论】:

  • 你有一个!在断言中的 hello world 之后,所以它是 false ;)
  • 不适用于 IE 或 Opera 9.61
  • 呸,我应该测试过,但我只是从原始问题中复制了断言
  • Opera 真的不行吗? O_o
  • 至少在 9.61 中没有,proto 是未定义的。
【解决方案2】:

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

我认为没有可移植的方式来做到这一点:

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

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

【讨论】:

    【解决方案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
    

    【讨论】:

    • 不幸的是,我认为这将修改全局函数原型:-/
    • @Olliej:为什么?我正在创建一个新的空函数并修改它的原型,而不是全局函数原型。
    • 啊,是的,你是对的,我的错——但是 new f 正在创建一个对象,而不是一个可以调用的函数。例如。 create(fun,obj)() 不会做被问到的事情。我怀疑问题要求在 IE/Opera 中是不可能的。
    • @Olliej:正确,这就是为什么我以“我来过的最接近的跨浏览器的东西......”开头;)我也怀疑它不能在 IE/Opera 中完成。
    【解决方案4】:

    您不能以便携的方式进行。但是,如果您考虑一下,如果delete foo.x; 的目的是重置x 的值,您可以在foo 上提供一个reset() 方法,它将丢失的属性恢复为其默认值。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-08
      • 2015-12-31
      • 2021-07-31
      • 2013-03-24
      • 2021-07-29
      • 2022-01-17
      • 1970-01-01
      相关资源
      最近更新 更多