web-dev-qa-db-ja.com

Array.prototypeにカスタム関数を追加する

私はAJAX対応のasp.netアプリケーションに取り組んでいました。 Array.prototypeにいくつかのメソッドを追加しました

Array.prototype.doSomething = function(){
   ...
}

このソリューションは私にとってはうまくいきました。コードを「きれいな」方法で再利用することができました。

しかし、ページ全体で動作するようにテストしたときに問題が発生しました。カスタムのajaxエクステンダーがいくつかあり、それらは予期しない動作を始めました。

その原因は何でしょうか?標準オブジェクトのプロトタイプを変更することについて何かが欠けていますか?

注:Arrayのプロトタイプを変更すると、エラーが始まると確信しています。 IEとのみ互換性がある必要があります。

47
Matias

ビルトインオブジェクトプロトタイプの変更は、同じページの他のコードと常に衝突する可能性があるため、一般的には悪い考えです。

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であっても、組み込み型のプロトタイプの変更を避ける必要がある理由です。独自のコンストラクタで独自の型を定義します。

46
thomasrutter

プロトタイプの関数をオーバーライドする他のコードと衝突する可能性は依然としてリスクですが、JavaScriptの最新バージョンでこれを行いたい場合は、Object.definePropertyメソッドを使用して、列挙可能なビットをオフにできます。例えば.

// functional sort
Object.defineProperty(Array.prototype, 'sortf', {
    value: function(compare) { return [].concat(this).sort(compare); }
});
39
csells

注意があります!たぶんあなたはそれをやった: 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メソッドを定義することもできます。

11
tika

一般的に、コアjavascriptオブジェクトをいじるのは悪い考えです。サードパーティのライブラリが何を期待しているのか、あなたは決して知りません。javascriptのコアオブジェクトを変更すると、すべてのライブラリが変更されます。

Prototypeを使用する場合、プロトタイプはグローバルスコープを乱し、衝突するかどうかを判断するのが難しいため、特に悪いです。実際には、言語のコア部分を変更することは、javascriptであっても悪い考えです。

(LISPは小さな例外かもしれません)

1
Jeremy Wall

拡張いわばジェネリック型。おそらく他のライブラリの機能を上書きしているため、機能しなくなったのです。

使用しているライブラリがArray.remove()関数でArrayを拡張しているとします。 libがロードされた後、remove()をArrayのプロトタイプに追加しますが、独自の機能があります。 libが関数を呼び出すと、おそらく予想とは異なる方法で動作し、実行が中断されます...それがここで発生しています。

1
Robert Koritnik

再帰を使用

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を生成します

0
grabbag