如何继承 javascript 函数?

发布于 2024-12-06 07:29:08 字数 2529 浏览 2 评论 0原文

// 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 返回一个对象而不是函数。

new Constructor 返回一个对象而不是函数。

动机: - 跨浏览器 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 提出一个 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;
  };
})();

相关es-discuss 邮件

正如 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
// 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.

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.

new Constructor returns an Object not a Function.

Motivation: - A cross-browser 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

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

久隐师 2024-12-13 07:29:08

我希望我能正确理解这一点。

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

  1. 就运行时间而言,成本非常高。
  2. (functorObj instanceof MyClass) 永远不会是 true
  3. 属性将无法直接访问(如果它们都是通过引用分配的,这将是一个不同的故事,但基元是通过值分配的)。这可以通过 defineProperty 使用访问器来解决,或者在必要时简单地命名访问器方法(这似乎就是您正在寻找的,只需使用 defineProperty 将所有属性添加到仿函数中即可> 如果您不需要跨引擎支持/向后兼容性,则通过 getter/setter 而不仅仅是函数)。
  4. 您可能会遇到边缘情况,其中最终的本机原型(例如 Object.prototype 或 Array.prototype [如果您要继承它])可能无法按预期运行。
  5. 调用 functorObj(someArg)始终使 this 上下文成为对象,无论它是否被调用 functorObj.call(someOtherObj, someArg) (不过方法调用的情况并非如此)
  6. 因为函子对象是在请求时创建的,所以它会及时被锁定,并且操作初始原型不会影响分配的函子像普通对象这样的对象会受到影响(修改 MyClass.prototype 不会影响任何函子对象,反之亦然)。

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

在类的原型中定义类似的东西...

// 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 的方法,然后将其传递给要包装的对象(如果需要)做类似的事情(当然,您必须将 templateFunctionthis 交换,并将 this 与参数交换)...

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

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

  1. It's very costly in terms of runtime.
  2. (functorObj instanceof MyClass) will never be true.
  3. 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).
  4. 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.
  5. 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)
  6. 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).

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).

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)...

var functor = (function() { /* something */ }).asFunctor(aobj);
暖阳 2024-12-13 07:29:08

使用 ES6 可以从 Function 继承,请参阅(重复)问题

javascript类继承自Function类

default export Attribute extends Function {
...
}

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

javascript class inherit from Function class

default export Attribute extends Function {
...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文