【问题标题】:Replicating python's __call__ in javascript?在javascript中复制python的__call__?
【发布时间】:2013-07-25 20:32:23
【问题描述】:

我想复制一个可调用类的实例化而不使用模块模式。

以下是我对此的最佳尝试。但是,它使用我不确定的__proto__。这可以在没有__proto__ 的情况下完成吗?

function classcallable(cls) {
    /*
     * Replicate the __call__ magic method of python and let class instances
     * be callable.
     */
    var new_cls = function () {
        var obj = Object.create(cls.prototype);
        // create callable
        // we use func.__call__ because call might be defined in
        // init which hasn't been called yet.
        var func = function () {
            return func.__call__.apply(func, arguments);
        };
        func.__proto__ = obj;
        // apply init late so it is bound to func and not cls
        cls.apply(func, arguments);
        return func;
    }
    new_cls.prototype = cls.prototype;
    return new_cls

}

【问题讨论】:

  • 利用原型继承有什么问题?这只是多态性的另一种方案。
  • 它实际上是跨浏览器的(虽然 IE 和 [旧版本?] Opera 似乎不支持它),and is in the ECMAScript ed6 spec. draft,所以在某些时候它将是标准的(除非他们将来将其从规范中删除)。不过,您说得对,目前它还不是标准的。
  • 您避免使用模块模式的动机是什么?看到 Javascript 显然没有真正的可调用对象,并且与 Python 相比,似乎以相反的方式继续语言设计的这一方面
  • 注意.__proto__.constructor.prototype一样
  • @SimonBoudrias 仅在获取.__proto__ 时,而不是在设置时。事后设置构造函数的原型对已创建的对象没有影响,要更改已创建对象使用的原型,您必须直接设置它们。虽然您可能会删除 .constructor.prototype 中的所有属性,然后添加新属性,但这意味着原型属性的更改将影响该构造函数创建的所有对象,而不仅仅是给定函数(当然,这可能是可取的,但可能不是这种情况)。

标签: javascript prototype-programming


【解决方案1】:

代理

如果您反对使用__proto__ 或与之相关的任何内容,但仍希望具有可继承性,则可以使用Proxies

var ObjectCallable_handler = {
    get: function get(self, key) {
        if (self.hasOwnProperty(key)) {
            return self[key];
        } else { return self.__inherit__[key]; }
    },
    apply: function apply(self, thisValue, args) {
        return (self.__call__ || self.__inherit__.__call__).apply(self, args);
    }
};

function ObjectCallable(cls) {
    var p = new Proxy(function() { }, ObjectCallable_handler);
    p.__inherit__ = cls;
    return p;
}

优点

  • 保持可继承性。
  • 不涉及传统原型链。
  • ES6 draft

缺点


setPrototypeOf

如果缺少对代理的支持让您感到沮丧,您可以尝试使用setPrototypeOf polyfill。

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
}

function ObjectCallable(cls) {
    var f = function() { return f.__call__.apply(f, arguments); };
    Object.setPrototypeOf(f, cls);
    return f;
}

优点

  • 现在和将来都可能发挥最佳效果。
  • ES6 draft

缺点

  • 通过 polyfill 使用非标准 __proto__需要在您想要支持的引擎/浏览器上进行测试。
  • setPrototypeOf 在任何浏览器中都是 not currently implemented

复制

这是功能最少的最简单的解决方案,但它保证几乎可以在任何地方工作。创建一个新函数并将对象的属性克隆到它上面。

function ObjectCallable(cls) {
    var f = function() { return f.__call__.apply(f, arguments); }, k;
    for (k in cls) {
        f[k] = cls[k];
    }
    return f;
}

优点

  • 几乎可以在任何地方使用。

缺点

  • 不维护任何类似的继承或原型结构。

结论

在 JavaScript 中复制 Python 的 __call__ 功能需要更多时间,因为 ES6 开发并在更多引擎中实现,或者依赖非标准功能,例如 __proto__

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-30
    • 2020-04-20
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    相关资源
    最近更新 更多