web-dev-qa-db-ja.com

JavaScript関数を継承するにはどうすればよいですか?

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

関連するes-ディスカッションメール

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
48
Raynos

私はこの権利を理解していることを願っています。

事前定義されたプロトタイプのインスタンス(はい、クラスであり、クラシッククラスではありません)であると同時に直接呼び出し可能なファンクターが必要だと思いますか?正しい?もしそうなら、これは完全に理にかなっており、非常に強力で柔軟性があります(特にJavaScriptのような高度に非同期の環境で)。 残念ながら、JavaScriptで__proto__を操作せずにエレガントにそれを行う方法はありません。これを行うには、無名関数を除外し、すべてのメソッドへのすべての参照をコピーして(これはあなたが向かっていた方向のようです)、プロキシクラスとして機能します。これの欠点は...

  1. 実行時間の点で非常にコストがかかります。
  2. (functorObj instanceof MyClass)trueになることはありません。
  3. プロパティに直接アクセスすることはできません(すべてが参照によって割り当てられている場合、これは別の話になりますが、プリミティブは値によって割り当てられます)。これは、definePropertyを介したアクセサー、または必要に応じて単に名前付きアクセサーメソッドを使用して解決できます(それが探しているようです。そうでない場合は、関数だけでなく、ゲッター/セッターを介してdefinePropertyを使用してすべてのプロパティをファンクターに追加してください。クロスエンジンサポート/後方互換性が必要です)。
  4. 最終的なネイティブプロトタイプ(Object.prototypeやArray.prototype [それを継承している場合]など)が期待どおりに機能しない可能性があるEdgeケースに遭遇する可能性があります。
  5. functorObj(someArg)を呼び出すと、alwaysは、functorObj.call(someOtherObj, someArg)と呼ばれるかどうかに関係なく、thisコンテキストをオブジェクトにします(ただし、メソッド呼び出しの場合はそうではありません)。
  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演算子なしで呼び出されたコンストラクターが新しいオブジェクトをインスタンス化し、functorオブジェクトを返すようにすることもできます。しかし、あなたは好みます(あなたは確かに他の方法でもそれを行うことができます)。

最後に、関数をもう少し洗練された方法で関数の実行演算子にするには、プロキシ関数をFunction.prototypeのメソッドにして、(( templateFunctionthisと交換し、thisを引数と交換する必要があります)...

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

ES6では、Functionから継承することができます。(重複する)質問を参照してください。

javascriptクラスは関数クラスから継承します

default export Attribute extends Function {
...
}
0
Stefan