// 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-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
私はこの権利を理解していることを願っています。
事前定義されたプロトタイプのインスタンス(はい、クラスであり、クラシッククラスではありません)であると同時に直接呼び出し可能なファンクターが必要だと思いますか?正しい?もしそうなら、これは完全に理にかなっており、非常に強力で柔軟性があります(特にJavaScriptのような高度に非同期の環境で)。 残念ながら、JavaScriptで__proto__
を操作せずにエレガントにそれを行う方法はありません。これを行うには、無名関数を除外し、すべてのメソッドへのすべての参照をコピーして(これはあなたが向かっていた方向のようです)、プロキシクラスとして機能します。これの欠点は...
(functorObj instanceof MyClass)
がtrue
になることはありません。defineProperty
を介したアクセサー、または必要に応じて単に名前付きアクセサーメソッドを使用して解決できます(それが探しているようです。そうでない場合は、関数だけでなく、ゲッター/セッターを介してdefineProperty
を使用してすべてのプロパティをファンクターに追加してください。クロスエンジンサポート/後方互換性が必要です)。functorObj(someArg)
を呼び出すと、alwaysは、functorObj.call(someOtherObj, someArg)
と呼ばれるかどうかに関係なく、this
コンテキストをオブジェクトにします(ただし、メソッド呼び出しの場合はそうではありません)。しかし、それを穏やかに使用すれば、これは大したことではないはずです。
クラスのプロトタイプで、次のようなものを定義します...
// 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
のメソッドにして、(( templateFunction
をthis
と交換し、this
を引数と交換する必要があります)...
var functor = (function() { /* something */ }).asFunctor(aobj);
ES6では、Function
から継承することができます。(重複する)質問を参照してください。
default export Attribute extends Function {
...
}