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

これが可能だとは思わないのでFunction.create、es-discuss メーリング リストに提案する必要があります。

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

2 に答える 2

10

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

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

  1. 実行時間の点で非常にコストがかかります。
  2. (functorObj instanceof MyClass)決してなりませんtrue
  3. プロパティに直接アクセスすることはできません(すべてが参照によって割り当てられている場合、これは別の話になりますが、プリミティブは値によって割り当てられます)。これは、必要に応じてアクセサメソッドまたは単に名前付きアクセサメソッドを使用して解決できますdefineProperty(これが探しているようです。definePropertyクロスが必要ない場合は、関数だけでなく、ゲッター/セッターを介してすべてのプロパティをファンクターに追加するだけです。 -エンジンサポート/後方互換性)。
  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して、次のようなことをしたい場合はラップするオブジェクトを渡します(スワップする必要があります)もちろん、議論とともに)templateFunction..。thisthis

var functor = (function() { /* something */ }).asFunctor(aobj);
于 2011-10-14T03:25:19.567 に答える