私はAJAX対応のasp.netアプリケーションに取り組んでいました。 Array.prototypeにいくつかのメソッドを追加しました
Array.prototype.doSomething = function(){
...
}
このソリューションは私にとってはうまくいきました。コードを「きれいな」方法で再利用することができました。
しかし、ページ全体で動作するようにテストしたときに問題が発生しました。カスタムのajaxエクステンダーがいくつかあり、それらは予期しない動作を始めました。
その原因は何でしょうか?標準オブジェクトのプロトタイプを変更することについて何かが欠けていますか?
注:Arrayのプロトタイプを変更すると、エラーが始まると確信しています。 IEとのみ互換性がある必要があります。
ビルトインオブジェクトプロトタイプの変更は、同じページの他のコードと常に衝突する可能性があるため、一般的には悪い考えです。
Arrayオブジェクトプロトタイプの場合、_for .. in
など、配列のメンバーを反復処理するコードの一部に干渉する可能性があるため、特に悪い考えです。
例を使用して説明するには( here から借用):
Array.prototype.foo = 1;
// somewhere deep in other javascript code...
var a = [1,2,3,4,5];
for (x in a){
// Now foo is a part of EVERY array and
// will show up here as a value of 'x'
}
逆は真です-n00bがArrayプロトタイプを変更した場合はfor..inを避け、n00bが配列の..inを使用した場合はArrayプロトタイプを変更しないでください。
組み込みのArrayを拡張するよりも、doSomething関数を備えた独自のタイプのオブジェクトコンストラクターを作成する方が良いでしょう。
Object.defineProperty
はどうですか?
現在、新しいプロパティを列挙せずにオブジェクトプロトタイプを拡張する一般的な方法としてObject.defineProperty
が存在しますが、built-inタイプを拡張する正当な理由としてこれを使用することはありません。 for..in
に加えて、他のスクリプトと他の競合が発生する可能性がまだあります。同様の方法で配列を拡張し、同じメソッド名を選択しようとする2つのJavascriptフレームワークを使用している人を考えてみましょう。または、誰かがyourコードをフォークして、元のバージョンとフォークされたバージョンの両方を同じページに配置することを検討してください。 Arrayオブジェクトのカスタム拡張は引き続き機能しますか?
これはJavascriptの現実であり、Object.defineProperty
であっても、組み込み型のプロトタイプの変更を避ける必要がある理由です。独自のコンストラクタで独自の型を定義します。
プロトタイプの関数をオーバーライドする他のコードと衝突する可能性は依然としてリスクですが、JavaScriptの最新バージョンでこれを行いたい場合は、Object.definePropertyメソッドを使用して、列挙可能なビットをオフにできます。例えば.
// functional sort
Object.defineProperty(Array.prototype, 'sortf', {
value: function(compare) { return [].concat(this).sort(compare); }
});
注意があります!たぶんあなたはそれをやった: fiddle demo
最初の要素を返す配列とメソッドfooを考えてみましょう:
var myArray = ["Apple","ball","cat"];
foo(myArray) // <- 'Apple'
function foo(array){
return array[0]
}
解釈時に関数が上に持ち上げられるため、上記は問題ありません。
しかし、これは機能しません:(プロトタイプが定義されていないため)
myArray.foo() // <- 'undefined function foo'
Array.prototype.foo = function(){
return this[0]
}
これを機能させるには、単にプロトタイプを上部で定義します。
Array.prototype.foo = function(){
return this[0]
}
myArray.foo() // <- 'Apple'
はい!プロトタイプをオーバーライドできます!!!許可されています。配列に対して独自の
add
メソッドを定義することもできます。
一般的に、コアjavascriptオブジェクトをいじるのは悪い考えです。サードパーティのライブラリが何を期待しているのか、あなたは決して知りません。javascriptのコアオブジェクトを変更すると、すべてのライブラリが変更されます。
Prototypeを使用する場合、プロトタイプはグローバルスコープを乱し、衝突するかどうかを判断するのが難しいため、特に悪いです。実際には、言語のコア部分を変更することは、javascriptであっても悪い考えです。
(LISPは小さな例外かもしれません)
拡張いわばジェネリック型。おそらく他のライブラリの機能を上書きしているため、機能しなくなったのです。
使用しているライブラリがArray.remove()関数でArrayを拡張しているとします。 libがロードされた後、remove()をArrayのプロトタイプに追加しますが、独自の機能があります。 libが関数を呼び出すと、おそらく予想とは異なる方法で動作し、実行が中断されます...それがここで発生しています。
再帰を使用
function forEachWithBreak(someArray, fn){
let breakFlag = false
function breakFn(){
breakFlag = true
}
function loop(indexIntoSomeArray){
if(!breakFlag && indexIntoSomeArray<someArray.length){
fn(someArray[indexIntoSomeArray],breakFn)
loop(indexIntoSomeArray+1)
}
}
loop(0)
}
テスト1 ... breakは呼び出されません
forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
console.log(element)
})
A b c d e f gを生成します
テスト2 ...要素cの後にbreakが呼び出されます
forEachWithBreak(["a","b","c","d","e","f","g"], function(element, breakFn){
console.log(element)
if(element =="c"){breakFn()}
})
A b cを生成します