我如何继承javascript函数?

时间:2022-09-24 23:17:04
// 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__ is non-standard and deprecated.

.__ proto__是非标准的并且已弃用。

How am I supposed to inherit prototypically creating an object but having that object be a function.

我怎么能继承原型创建一个对象,但让该对象成为一个函数。

Object.create returns an Object not a Function.

Object.create返回Object而不是Function。

new Constructor returns an Object not a Function.

new构造函数返回Object而不是Function。

Motivation: - A cross-browser finherit

动机: - 跨浏览器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;
};

I don't believe this is possible, so we should probably propose a Function.create to the es-discuss mailing list

我不相信这是可能的,所以我们应该向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邮件

As mentioned in the es-discuss thread there exists a ES:strawman <| prototype operator which would allow for this.

正如es-discuss线程中提到的,存在ES:strawman <|原型操作员,允许这样做。

Let's see what it would look like using <|

让我们看看使用<|的样子

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

#1


9  

I hope that I'm understanding this right.

我希望我理解这一点。

I believe you want a functor that's both an instance of a predefined prototype (yes, a class, just not a classic class) as well as directly callable? Right? If so, then this makes perfect sense and is very powerful and flexible (especially in a highly asynchronous environment like JavaScript). Sadly there is no way to do it elegantly in JavaScript without manipulating __proto__. You can do it by factoring out an anonymous function and copying all of the references to all of the methods (which seems to be the direction you were heading) to act as a proxy class. The downsides to this are...

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

  1. It's very costly in terms of runtime.
  2. 它在运行时方面非常昂贵。
  3. (functorObj instanceof MyClass) will never be true.
  4. (functorObj instanceof MyClass)永远不会成真。
  5. Properties will not be directly accessible (if they were all assigned by reference this would be a different story, but primitives are assigned by value). This can be solved with accessors via defineProperty or simply named accessor methods if necessary (it appears that that is what you're looking for, just add all properties to the functor with defineProperty via getters/setters instead of just functions if you don't need cross-engine support/backwards compatability).
  6. 属性不能直接访问(如果它们都是通过引用分配的,那么这将是一个不同的故事,但原语是​​按值分配的)。这可以通过defineProperty使用访问器来解决,或者如果需要可以简单地命名为访问器方法(看起来这就是你正在寻找的东西,只需通过getter / setter将所有属性添加到带有defineProperty的仿函数中,而不仅仅是函数,如果你没有需要跨引擎支持/向后兼容性)。
  7. You're likely to run into edge cases where final native prototypes (like Object.prototype or Array.prototype [if you're inheriting that]) may not function as expected.
  8. 您可能遇到边缘情况,最终的原生原型(如Object.prototype或Array.prototype [如果您继承])可能无法按预期运行。
  9. Calling functorObj(someArg) will always make the this context be the object, regardless of if it's called functorObj.call(someOtherObj, someArg) (this is not the case for method calls though)
  10. 调用functorObj(someArg)将始终使该上下文成为对象,无论它是否被称为functorObj.call(someOtherObj,someArg)(虽然方法调用不是这种情况)
  11. Because the functor object is created at request time, it will be locked in time and manipulating the initial prototype will not affect the allocated functor objects like a normal object would be affected (modifying MyClass.prototype will not affect any functor objects and the reverse is true as well).
  12. 因为functor对象是在请求时创建的,它将被及时锁定并且操纵初始原型不会影响分配的仿函数对象,就像普通对象会受到影响一样(修改MyClass.prototype不会影响任何仿函数对象,反之亦然)也是如此)。

If you use it gently though, none of this should be a big deal.

如果你轻轻地使用它,这一切都不是什么大问题。

In your prototype of your class define something like...

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

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

Resulting usage would look something like...

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

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.

You could even chain bind countless other objects/functions/etc until the cows came home. Just refactor the proxy call a bit.

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

Hope that helps. Oh, and of course you could change the factory flow so that a constructor called without the new operator then instantiates a new object and returns the functor object. However you prefer (you could surely do it other ways too).

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

Finally, to have any function become the execution operator for a functor in a bit more elegant of a manner, just make the proxy function a method of Function.prototype and pass it the object to wrap if you want to do something like (you would have to swap templateFunction with this and this with the argument of course)...

最后,让任何函数以更优雅的方式成为函子的执行运算符,只需将代理函数作为Function.prototype的方法,并将对象传递给它,如果你想做某事(你会必须与此交换templateFunction,这与当然的参数交换)...

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

#2


0  

With ES6 it's possible to inherit from Function, see (duplicate) question

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

javascript class inherit from Function class

javascript类继承自Function类

default export Attribute extends Function {
...
}

#1


9  

I hope that I'm understanding this right.

我希望我理解这一点。

I believe you want a functor that's both an instance of a predefined prototype (yes, a class, just not a classic class) as well as directly callable? Right? If so, then this makes perfect sense and is very powerful and flexible (especially in a highly asynchronous environment like JavaScript). Sadly there is no way to do it elegantly in JavaScript without manipulating __proto__. You can do it by factoring out an anonymous function and copying all of the references to all of the methods (which seems to be the direction you were heading) to act as a proxy class. The downsides to this are...

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

  1. It's very costly in terms of runtime.
  2. 它在运行时方面非常昂贵。
  3. (functorObj instanceof MyClass) will never be true.
  4. (functorObj instanceof MyClass)永远不会成真。
  5. Properties will not be directly accessible (if they were all assigned by reference this would be a different story, but primitives are assigned by value). This can be solved with accessors via defineProperty or simply named accessor methods if necessary (it appears that that is what you're looking for, just add all properties to the functor with defineProperty via getters/setters instead of just functions if you don't need cross-engine support/backwards compatability).
  6. 属性不能直接访问(如果它们都是通过引用分配的,那么这将是一个不同的故事,但原语是​​按值分配的)。这可以通过defineProperty使用访问器来解决,或者如果需要可以简单地命名为访问器方法(看起来这就是你正在寻找的东西,只需通过getter / setter将所有属性添加到带有defineProperty的仿函数中,而不仅仅是函数,如果你没有需要跨引擎支持/向后兼容性)。
  7. You're likely to run into edge cases where final native prototypes (like Object.prototype or Array.prototype [if you're inheriting that]) may not function as expected.
  8. 您可能遇到边缘情况,最终的原生原型(如Object.prototype或Array.prototype [如果您继承])可能无法按预期运行。
  9. Calling functorObj(someArg) will always make the this context be the object, regardless of if it's called functorObj.call(someOtherObj, someArg) (this is not the case for method calls though)
  10. 调用functorObj(someArg)将始终使该上下文成为对象,无论它是否被称为functorObj.call(someOtherObj,someArg)(虽然方法调用不是这种情况)
  11. Because the functor object is created at request time, it will be locked in time and manipulating the initial prototype will not affect the allocated functor objects like a normal object would be affected (modifying MyClass.prototype will not affect any functor objects and the reverse is true as well).
  12. 因为functor对象是在请求时创建的,它将被及时锁定并且操纵初始原型不会影响分配的仿函数对象,就像普通对象会受到影响一样(修改MyClass.prototype不会影响任何仿函数对象,反之亦然)也是如此)。

If you use it gently though, none of this should be a big deal.

如果你轻轻地使用它,这一切都不是什么大问题。

In your prototype of your class define something like...

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

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

Resulting usage would look something like...

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

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.

You could even chain bind countless other objects/functions/etc until the cows came home. Just refactor the proxy call a bit.

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

Hope that helps. Oh, and of course you could change the factory flow so that a constructor called without the new operator then instantiates a new object and returns the functor object. However you prefer (you could surely do it other ways too).

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

Finally, to have any function become the execution operator for a functor in a bit more elegant of a manner, just make the proxy function a method of Function.prototype and pass it the object to wrap if you want to do something like (you would have to swap templateFunction with this and this with the argument of course)...

最后,让任何函数以更优雅的方式成为函子的执行运算符,只需将代理函数作为Function.prototype的方法,并将对象传递给它,如果你想做某事(你会必须与此交换templateFunction,这与当然的参数交换)...

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

#2


0  

With ES6 it's possible to inherit from Function, see (duplicate) question

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

javascript class inherit from Function class

javascript类继承自Function类

default export Attribute extends Function {
...
}