我如何继承javascript函数?

时间:2011-09-24 12:54:21

标签: javascript oop function inheritance prototypal-inheritance

// Don't break the function prototype.
// pd - https://github.com/Raynos/pd
var proto = Object.create(Function.prototype, pd({
  "prop": 42
}));

var f = function() { return "is a function"; };
f.__proto__ = proto;

console.log(f.hasOwnProperty("prop")); // false
console.log(f.prop); // 42
console.log(f()); // "is a function"

.__proto__是非标准的,已弃用。

我应该如何继承原型创建一个对象,但让该对象成为一个函数。

Object.create返回一个Object而不是Function。

new Constructor返回一个Object而不是Function。

动机: - 跨浏览器finherit

var finherit = function (parent, child) {
    var f = function() { 
        parent.apply(this, arguments);
        child.apply(this, arguments);
    };
    f.__proto__ = parent;
    Object.keys(child).forEach(function _copy(key) {
        f[key] = child[key];
    });
    return f;
};

我不相信这是可能的,所以我们应该向es-discuss邮件列表提出Function.create

/*
  Creates a new function whose prototype is proto.
  The function body is the same as the function fbody.
  The hash of propertydescriptors props is passed to defineproperties just like
  Object.create does.
*/
Function.create = (function() {
  var functionBody = function _getFunctionBody(f) {
    return f.toString().replace(/.+\{/, "").replace(/\}$/, "");
  };
  var letters = "abcdefghijklmnopqrstuvwxyz".split("");

  return function _create(proto, fbody, props) {
    var parameters = letters.slice(0, fbody.length);
    parameters.push(functionBody(fbody));
    var f = Function.apply(this, parameters);
    f.__proto__ = proto;
    Object.defineProperties(f, props);
    return f;
  };
})();

Related es-discuss mail

正如es-discuss主题中所提到的,存在一个ES:strawman <|原型运算符,它允许这样做。

让我们看看使用<|

会是什么样子
var f1 = function () {
  console.log("do things");
};

f1.method = function() { return 42; };

var f2 = f1 <| function () {
  super();
  console.log("do more things");
}
console.log(f1.isPrototypeOf(f2)); // true
console.log(f2()); // do things do more things
console.log(f2.hasOwnProperty("method")); // false
console.log(f2.method()); // 42

2 个答案:

答案 0 :(得分:9)

我希望我理解这一点。

我相信你想要一个仿函数既是预定义原型的实例(是的,类,不是经典类),也可以直接调用?对?如果是这样,那么这是非常有意义的,并且非常强大和灵活(特别是在像JavaScript这样的高度异步环境中)。 遗憾的是,在没有操纵__proto__ 的情况下,没有办法在JavaScript中优雅。您可以通过分解匿名函数并将所有引用复制到所有方法(这似乎是您前进的方向)来充当代理类来实现。这方面的缺点是......

  1. 在运行时方面非常昂贵。
  2. (functorObj instanceof MyClass)永远不会是true
  3. 属性不能直接访问(如果它们都是通过引用分配的,那么这将是一个不同的故事,但原语是​​按值分配的)。这可以通过defineProperty使用访问器来解决,或者在必要时简单地命名为访问器方法(看起来这就是您要查找的内容,只需通过getter / setter将所有属性添加到带有defineProperty的仿函数中如果你不需要跨引擎支持/向后兼容性,只需要功能。)
  4. 您可能遇到最终原生原型(如Object.prototype或Array.prototype [如果您继承])可能无法按预期运行的边缘情况。
  5. 调用functorObj(someArg)总是使this上下文成为对象,无论它是否被称为functorObj.call(someOtherObj, someArg)(这不是方法调用的情况) )
  6. 因为functor对象是在请求时创建的,所以它将被及时锁定并且操纵初始原型不会影响分配的仿函数对象,就像普通对象会受到影响一样(修改MyClass.prototype不会影响任何仿函数对象和反过来也是如此)。
  7. 如果你轻轻地使用它,这一切都不是什么大问题。

    在你的班级原型中定义类似......

    // This is you're emulated "overloaded" call() operator.
    MyClass.prototype.execute = function() {
       alert('I have been called like a function but have (semi-)proper access to this!');
    };
    
    MyClass.prototype.asFunctor = function(/* templateFunction */) {
       if ((typeof arguments[0] !== 'function') && (typeof this.execute !== 'function'))
          throw new TypeError('You really should define the calling operator for a functor shouldn\'t you?');
       // This is both the resulting functor proxy object as well as the proxy call function
       var res = function() {
          var ret;
          if (res.templateFunction !== null)
             // the this context here could be res.asObject, or res, or whatever your goal is here
             ret = res.templateFunction.call(this, arguments);
          if (typeof res.asObject.execute === 'function')
             ret = res.asObject.execute.apply(res.asObject, arguments);
          return ret;
       };
       res.asObject = this;
       res.templateFunction = (typeof arguments[0] === 'function') ? arguments[0] : null;
       for (var k in this) {
          if (typeof this[k] === 'function') {
             res[k] = (function(reference) {
                var m = function() {
                   return m.proxyReference.apply((this === res) ? res.asObject : this, arguments);
                };
                m.proxyReference = reference;
                return m;
             })(this.asObject[k]);
          }
       }
       return res;
    };
    

    结果用法看起来像......

    var aobj = new MyClass();
    var afunctor = aobj.asFunctor();
    aobj.someMethodOfMine(); // << works
    afunctor.someMethodOfMine(); // << works exactly like the previous call (including the this context).
    afunctor('hello'); // << works by calling aobj.execute('hello');
    
    (aobj instanceof MyClass) // << true
    (afunctor instanceof MyClass) // << false
    (afunctor.asObject === aobj) // << true
    
    // to bind with a previous function...
    var afunctor = (new MyClass()).asFunctor(function() { alert('I am the original call'); });
    afunctor() // << first calls the original, then execute();
    // To simply wrap a previous function, don't define execute() in the prototype.
    

    你甚至可以链接无数其他对象/功能/等,直到奶牛回家。只需稍微重构代理调用。

    希望有所帮助。哦,当然你可以改变工厂流程,这样一个没有new运算符的构造函数然后实例化一个新对象并返回一个仿函数对象。不过你更喜欢(你也可以通过其他方式去做)。

    最后,要让任何函数以更优雅的方式成为函子的执行运算符,只需使代理函数成为Function.prototype的方法,并将对象传递给它,如果你想做的话类似的东西(你必须用templateFunctionthisthis交换当然的参数... ...

    var functor = (function() { /* something */ }).asFunctor(aobj);
    

答案 1 :(得分:0)

使用ES6,可以继承Function,查看(重复)问题

javascript class inherit from Function class

default export Attribute extends Function {
...
}