15

まず、 にメソッドを追加したくありませんFunction.prototype。そうすることで、それらをすべての機能で利用できるようになりますが、それは私が探しているものではありません。

JavaScript では、次のようなカスタム プロトタイプを使用してオブジェクトを作成できます。

function CustomObj() {}
CustomObj.prototype = {};
CustomObj.prototype.sayFoo = function () { return 'foo' };

var myCustomObj = new CustomObj(); //=> returns an object: {}
myCusomObj.sayFoo(); //=> 'foo'

次のようなカスタム プロトタイプを使用して、配列のようなオブジェクトを作成することもできます。

function CustomArr() {}
CustomArr.prototype = [];
CustomObj.prototype.sayFoo = function () { return 'foo' };

var myCustomArr = new CustomArr(); //=> returns an ordered object: []
myCustomArr.sayFoo(); //=> 'foo'

私がやりたいのは、ある種のコンストラクターを使用して、同じ方法で独自のカスタム プロトタイプを持つ関数を作成することです。ただし、次の場合は機能しません。

function CustomFn() {}
CustomFn.prototype = function () {};
CustomFn.prototype.sayFoo = function () { return 'foo' };

var myCustomFn = new CustomFn(); //=> PROBLEM! returns an object: {}
myCustomFn.sayFoo(); //=> 'foo'

// ^^ Here, the prototype was applied but the output was not a function.
myCustomFn(); //=> TypeError: object is not a function

私がやろうとしていることを達成する方法はありますか?

アップデート

この質問をもう少し明確にする別の方法があるかもしれません。

閉鎖の考え方には問題があります:

function makeFn() {
  var output = function () { /* do some stuff */ };
  output.foo = function () { /* do some stuff */ };
  return output;
}
var specialFn = makeFn();

本質的に、このテクニックは私が望むものを与えてくれます。makeFnただし、問題は、を呼び出すたびにoutput.foo、独自のメモリを占有する完全に独立した関数として作成する必要があることです。きもい。したがって、そのメソッドをクロージャーから移動できます。

var protoMethods = {
  "foo" : function () { /* do some stuff */ }
};
function makeFn() {
  var output = function () { /* do some stuff */ };
  for (var i in protoMethods) {
    Object.prototype.hasOwnProperty.call(protoMethods, i) &&
      (output[i] = protoMethods[i]);
  }
  return output;
}
var specialFn = makeFn();

しかし今では、呼び出すたびに手動で反復を行う必要がmakeFnありprotoMethodsoutput. それで、この新しいアップデートで、何かアイデアはありますか?

4

3 に答える 3

5

言語が適切に設計されている場合、それは実際には難しいことであり、本来よりも複雑です...

基本的に、現在のバージョンではきれいに行うことはできません。関数以外のオブジェクトは呼び出すことができません。

将来の Javascript バージョンでは、「呼び出し」ハンドラを定義できる「プロキシ」オブジェクトを使用して実行できます。しかし、私の意見では、それはまだあまりにも複雑で不自然です。

もう 1 つの方法は、オブジェクトをカスタム オブジェクトではなく実際の関数にすることです。__proto__これはまだ標準ではありませんが、Opera と IE 8 以下を除くほとんどの最新のブラウザーで動作します。また、チェックconstructorを偽造するためにそのプロパティを設定することもできinstanceofます...そのようなハッキングは非常にトリッキーであり、結果は環境によって大きく異なります.

次の例は、Firefox で正常に動作します: http://jsfiddle.net/Q3422/2/

function MyFun() {
    if (!this || this==window) {
        return new MyFun();
    }

    var f = function() {
        return "thanks for calling!";
    }
    f.__proto__ = MyFun.prototype;
    f.constructor = MyFun;

    return f;
}

MyFun.prototype = {
    foo: function() {
        return "foo:" + this();
    },
    __proto__: Function.prototype
};

var f = new MyFun();
alert("proto method:"+f.foo()); // try our prototype methods
alert("function method:"+f.call()); // try standard function methods
alert("function call:"+f()); // try use as a function
alert('typeof:' + typeof f); // "function", not "object". No way around it in current js versions
alert('is MyFun:' + (f instanceof MyFun)); // true
alert('is Function:' + (f instanceof Function)); // true

オブジェクトの各インスタンスに関数を「コピー」することについて心配する必要はないことを追加したかっただけです。関数自体はオブジェクトであるため、実際にコピーされることも、再コンパイルされることもありません。関数オブジェクト参照自体とクロージャー変数を除いて、メモリを無駄にしません。

プロトタイプを反復してコピーすることも気にする必要はありません。膨大な数のメソッドはないと思います。

したがって、 protoを設定できない環境をサポートする必要があり、いくつかのオブジェクトが既に作成された後にプロトタイプが拡張され、変更が反映されない可能性があることを心配していない場合は、おそらく最後のソリューションが最適です。

于 2014-06-02T14:28:25.227 に答える
2

あなたは、JavaScript における継承のすべての核心にいます。はい、プロトタイプはオブジェクトであるため、CustomFn のプロトタイプを関数ではなくオブジェクトに設定する必要があります。

ただし、そのオブジェクトは別の関数から取得できます。

function ParentFn() {}
function CustomFn() {}
CustomFn.prototype = Object.create(ParentFn.prototype);
CustomFn.prototype.sayFoo = fun ...

ES5 またはポリフィルがない場合:

CustomFn.prototype = (function() {
                          function F(){}
                          F.prototype = ParentFn.prototype;
                          return new F();
                      }());

次のようにするだけだと言う人もいるかもしれませんが、上記の方法の方が優れています。

CustomFn.prototype = new ParentFn();
于 2013-05-16T20:10:03.473 に答える