JavaScriptコードにオブジェクトfoo
があるとします。 foo
は複雑なオブジェクトであり、別の場所で生成されます。 foo
オブジェクトのプロトタイプを変更するにはどうすればよいですか?
私の動機は、.NETからJavaScriptリテラルにシリアル化されたオブジェクトに適切なプロトタイプを設定することです。
ASP.NETページ内に次のJavaScriptコードを記述したとします。
var foo = <%=MyData %>;
MyData
は、Dictionary<string,string>
オブジェクトで.NET JavaScriptSerializer
を呼び出した結果であるとします。
実行時に、これは次のようになります。
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
ご覧のとおり、foo
はオブジェクトの配列になります。適切なプロトタイプでfoo
を初期化できるようにしたいと思います。 notObject.prototype
もArray.prototype
も変更したい。これどうやってするの?
2012年2月編集:以下の回答は正確ではありません。 __proto__は「規範的オプション」としてECMAScript 6に追加されています。つまり、実装する必要はありませんが、実装されている場合は、所定の規則に従う必要があります。これは現在未解決ですが、少なくとも公式にはJavaScriptの仕様の一部になります。
この質問は、表面上に見えるよりもはるかに複雑であり、Javascript内部の知識に関してはほとんどの人の賃金等級を超えています。
オブジェクトのprototype
プロパティは、そのオブジェクトの新しい子オブジェクトを作成するときに使用されます。変更してもオブジェクト自体には反映されず、そのオブジェクトが他のオブジェクトのコンストラクターとして使用される場合に反映され、既存のオブジェクトのプロトタイプの変更には使用できません。
function myFactory(){};
myFactory.prototype = someOtherObject;
var newChild = new myFactory;
newChild.__proto__ === myFactory.prototype === someOtherObject; //true
オブジェクトには、現在のプロトタイプを指す内部[[prototype]]プロパティがあります。動作方法は、オブジェクトのプロパティが呼び出されるたびに、オブジェクトで開始し、ルートObjectプロトタイプの後に一致するか失敗するまで[[prototype]]チェーンをたどります。これにより、Javascriptがオブジェクトのランタイム構築と変更を可能にします。必要なものを検索する計画があります。
__proto__
プロパティは、いくつかの実装(多くの場合)に存在します。Mozillaの実装、私が知っているすべてのWebkit実装、その他の実装です。このプロパティは内部[[prototype]]プロパティを指し、オブジェクトの作成後の変更を許可します。この連鎖ルックアップにより、プロパティと関数はすぐにプロトタイプに一致するように切り替わります。
この機能は現在標準化されていますが、JavaScriptの必須部分ではないため、サポートする言語ではコードが「最適化されていない」カテゴリに分類される可能性が高くなります。 JSエンジンは、コード、特に非常に頻繁にアクセスされる「ホット」コードを分類するために最善を尽くす必要があり、__proto__
を変更するような空想的なことをしている場合、コードはまったく最適化されません。
これは投稿します https://bugzilla.mozilla.org/show_bug.cgi?id=60786 は、__proto__
の現在の実装とそれらの違いについて具体的に説明しています。それは難しい未解決の問題だからです。 a。)構文b。)ホストオブジェクト(DOMは技術的にJavascriptの外部に存在する)およびc。)__proto__
を除く、Javascriptのすべては変更可能です。残りは完全にあなたと他のすべての開発者の手に委ねられているので、__proto__
が親指のように突き出ている理由を見ることができます。
__proto__
で許可されていることが1つあります。それ以外の場合は不可能です。実行時にコンストラクタとは別にオブジェクトプロトタイプを指定することです。これは重要なユースケースであり、__proto__
がまだ死んでいない主な理由の1つです。 Harmonyの定式化における真剣な議論のポイントであるか、まもなくECMAScript 6として知られるようになることは十分に重要です。作成中にオブジェクトのプロトタイプを指定する機能は、Javascriptの次のバージョンの一部になります。 __proto__
の日を示すベルには正式な番号が付けられます。
短期的には、それをサポートするブラウザをターゲットにしている場合、__proto__
を使用できます(IEではなく、IEは使用できません)。 ES6は2013年まで最終決定されないため、今後10年間はwebkitとmozで動作する可能性があります。
Brendan Eich-re: ES5の新しいObjectメソッドのアプローチ :
申し訳ありませんが、設定可能な
__proto__
は、オブジェクト初期化のユースケース(つまり、ES5のObject.createに類似した、まだ到達できない新しいオブジェクト)を除いて、ひどい考えです。これは、12年以上前に設定可能な__proto__
を設計および実装したものです。...階層化の欠如が問題です(JSONデータをキー
"__proto__"
と考えてください)。さらに悪いことに、可変性は、iloopingを回避するために、実装が循環プロトタイプチェーンをチェックする必要があることを意味します。 [無限再帰の定数チェックが必要]最後に、既存のオブジェクトで
__proto__
を変更すると、新しいプロトタイプオブジェクトの非ジェネリックメソッドが破損する可能性があり、__proto__
が設定されているレシーバー(直接)オブジェクトでは機能しない可能性がありますこれは単に悪い習慣であり、一般的な意図的なタイプの混乱の一種です。
ES6は最終的に Object.setPrototypeOf(object、prototype) を指定します。これはChromeおよびFirefoxで既に実装されています。
オブジェクトのインスタンスでconstructor
を使用して、オブジェクトのプロトタイプをインプレースで変更できます。これがあなたの求めていることだと思います。
これは、foo
のインスタンスであるFoo
がある場合を意味します。
function Foo() {}
var foo = new Foo();
以下を実行することにより、bar
のすべてのインスタンスにプロパティFoo
を追加できます。
foo.constructor.prototype.bar = "bar";
概念実証を示すフィドルは次のとおりです。 http://jsfiddle.net/C2cpw/ 。古いブラウザがこのアプローチを使用してどのように機能するかはあまりわかりませんが、これで仕事がうまくいくと確信しています。
機能をオブジェクトにミックスインすることが意図されている場合、このスニペットは次の作業を行う必要があります。
function mix() {
var mixins = arguments,
i = 0, len = mixins.length;
return {
into: function (target) {
var mixin, key;
if (target == null) {
throw new TypeError("Cannot mix into null or undefined values.");
}
for (; i < len; i += 1) {
mixin = mixins[i];
for (key in mixin) {
target[key] = mixin[key];
}
// Take care of IE clobbering `toString` and `valueOf`
if (mixin && mixin.toString !== Object.prototype.toString) {
target.toString = mixin.toString;
} else if (mixin && mixin.valueOf !== Object.prototype.valueOf) {
target.valueOf = mixin.valueOf;
}
}
return target;
}
};
};
foo.__proto__ = FooClass.prototype
、Firefoxでサポートされている限り、ChromeおよびSafariを実行できます。 __proto__
プロパティは非標準であり、ある時点でなくなる可能性があることに注意してください。
ドキュメント: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/proto 。 Object.setPrototypeOf()
がない理由の説明については、 http://www.mail-archive.com/[email protected]/msg00392.html も参照してください。なぜ__proto__
は非推奨になりました。
クロスブラウザの方法で既にインスタンス化されているJavaScriptオブジェクトのプロトタイプを変更することはできません。他の人が述べたように、あなたのオプションは次のとおりです。
__proto__
プロパティの変更特に、オブジェクト全体を内部オブジェクトに再帰的にループしてプロトタイプ全体の要素を効果的に変更する必要がある場合は、どちらも特に優れていません。
あなたが望むように見える機能について、より抽象的な見方をします。
基本的にプロトタイプ/メソッドは、オブジェクトに基づいて関数をグループ化する方法を許可します。
書く代わりに
function trim(x){ /* implementation */ }
trim(' test ');
あなたが書く
' test '.trim();
上記の構文は、object.method()構文のためにOOPという用語を作成しました。従来の関数型プログラミングに対するOOPの主な利点は次のとおりです。
obj.replace('needle','replaced')
vs str_replace ( 'foo' , 'bar' , 'subject')
などの名前と異なる変数の場所を覚える必要があるstring.trim().split().join()
)は、ネストされた関数join(split(trim(string))
よりも変更および記述が簡単になる可能性があります残念ながら(上記のように)JavaScriptでは、既存のプロトタイプを変更することはできません。上記のオブジェクトのObject.prototype
のみを修正できれば理想的ですが、残念ながらObject.prototype
を修正するとスクリプトが破損する可能性があります(プロパティの衝突とオーバーライドが発生します)。
これらの2つのスタイルのプログラミングの間に一般的に使用される妥協点はなく、カスタム関数を編成するOOP方法もありません。
nlimitJS は、カスタムメソッドを定義できる中間基盤を提供します。回避します:
上記のコードを使用して、オブジェクトに対して呼び出す予定の関数の名前空間を作成します。
以下に例を示します。
var foo = [{"A":"1","B":"2"},{"X":"7","Y":"8"}];
// define namespace with methods
var $ = {
log:function(){
console.log(this);
return this;
}[Unlimit](),
alert:function(){
alert(''+this);
}[Unlimit]()
}
foo[$.log]()
[$.log]()
[$.alert]();
より多くの例をここで読むことができます nlimitJS 。基本的に、関数で[Unlimit]()
を呼び出すと、その関数をオブジェクトのメソッドとして呼び出すことができます。 OOPと機能的な道路の中間地点のようなものです。
プロキシコンストラクター関数を定義してから、新しいインスタンスを作成し、元のオブジェクトからすべてのプロパティをコピーできます。
// your original object
var obj = { 'foo': true };
// your constructor - "the new prototype"
function Custom(obj) {
for ( prop in obj ) {
if ( obj.hasOwnProperty(prop) ) {
this[prop] = obj[prop];
}
}
}
// the properties of the new prototype
Custom.prototype.bar = true;
// pass your original object into the constructor
var obj2 = new Custom(obj);
// the constructor instance contains all properties from the original
// object and also all properties inherited by the new prototype
obj2.foo; // true
obj2.bar; // true
ライブデモ:http://jsfiddle.net/6Xq3P/
Custom
コンストラクタは、新しいプロトタイプergoを表し、そのCustom.prototype
オブジェクトには、元のオブジェクトで使用するすべての新しいプロパティが含まれます。
Custom
コンストラクター内では、すべてのプロパティを元のオブジェクトから新しいインスタンスオブジェクトにコピーするだけです。
この新しいインスタンスオブジェクトには、元のオブジェクトのすべてのプロパティ(コンストラクタ内でコピーされた)と、Custom.prototype
内で定義されたすべての新しいプロパティが含まれます(新しいオブジェクトはCustom
インスタンスであるため) 。
私の知る限り、すでに構築されたオブジェクトの[[prototype]]
参照を変更することはできません。元のコンストラクター関数のprototypeプロパティを変更できますが、既にコメントしたように、そのコンストラクターはObject
であり、コアJSコンストラクトの変更は悪いことです。
ただし、必要な追加機能を実装する構築済みオブジェクトのプロキシオブジェクトを作成できます。また、問題のオブジェクトに直接割り当てることで、追加のメソッドと動作をモンキーパッチすることもできます。
別の角度からアプローチしたい場合は、おそらく他の方法で欲しいものを手に入れることができます:プロトタイプをいじる必要があるのは何ですか?
プロトタイプを知っているなら、なぜそれをコードに挿入しないのですか?
var foo = new MyPrototype(<%= MyData %>);
したがって、データがシリアル化されると、次のようになります
var foo = new MyPrototype([{"A":"1","B":"2"},{"X":"7","Y":"8"}]);
これで、引数として配列を取るコンストラクタのみが必要になります。
その場でプロトタイプを作成したい場合、これは方法の1つです
function OntheFlyProto (info){
this.items = info;
this.y =-1;
for(var i = 0; i < this.items.length ; i++){
OntheFlyProto.prototype["get"+this.items[i].name] = function (){
this.y++;
return this.items[this.y].value;
}
}
}
var foo = [{name:"one", value:1},{name:"two", value:2}];
v = new OntheFlyProto(foo);
foo.prototype.myFunction = function(){alert("me");}
Array
または「サブクラス」から実際に継承する方法はありません。
あなたができることはこれです(警告:コードを先に進める):
function Foo(arr){
[].Push.apply(this, arr)
}
Foo.prototype = []
Foo.prototype.something = 123
var foo = new Foo(<%=MyData %>)
foo.length // => 2
foo[0] // => {"A":"1","B":"2"}
foo.something // => 123
これは機能しますが、パスを横切る人には特定の問題を引き起こします(配列のように見えますが、操作しようとすると問題が発生します)。
健全なルートに進み、メソッド/プロパティをfoo
に直接追加するか、コンストラクターを使用して配列をプロパティとして保存してみませんか?
function Foo(arr){
this.items = arr
}
Foo.prototype = {
someMethod : function(){ ... }
//...
}
var foo = new Foo(<%=MyData %>)
foo.items // => [{"A":"1","B":"2"},{"X":"7","Y":"8"}]