0

私はjavascriptを学んでいて、以下の関数が期待どおりに機能しない理由を理解しようとしています。2つのコード例の説明を参照してください。

// First time I call the shoplist function I pass [1] in the argument. Results are as I expect:

var shopitems = [];

function shoplist(ids) {
    alert("ids passed to shoplist function: " + ids); // 1
    alert("current ids in shopitems var: " + shopitems); // (empty)
    shopitems.push(ids);
    alert("ids in shopitems after pushing: " + shopitems); // 1
    }


// Second time I call the shoplist functions I pass [1, 2] in the argument. Results are not what I would expect:

function shoplist(ids) {
    alert("ids passed to shoplist function: " + ids); // 1, 2
    alert("current ids in shopitems var: " + shopitems); // 1, 2  <--- Why is there 1, 2 and not only 1?
    shopitems.push(ids);
    alert("ids in shopitems after pushing: " + shopitems); // 1, 2, 1, 2

編集:ここに完全なコードがあります(警告:おそらく非常に紛らわしいです):http ://dpaste.org/wXMy5/

4

2 に答える 2

2

の2番目の呼び出しを呼び出す前に、SAME ids配列を変更すると、問題が発生しますshoplist()。javascriptは参照によって配列を渡し、参照のみがshopitems配列に入るため、2番目の呼び出しに渡す前にids配列を変更すると、shoplist()誤って変更することにもなりshopitems[0]ます。1回目と2回目の呼び出しの各引数shoplist()が完全に別個の配列である場合、この問題は発生しませんが、2回目の呼び出しが最初の配列の変更を渡されたばかりの場合、この問題が発生します。

簡単な図は次のとおりです。

// this will not have the problem because each call to shoplist
// is passing a completely separate array
var list = [1];
shoplist(list);
list = [1,2];      // create new array
shoplist(list);    // shoplist is [[1], [1,2]]

// this will have the problem because they are the same array
var list = [1];
shoplist(list);
list.push(2);     // modify first array
shoplist(list);   // shoplist is [[1,2], [1,2]] and both array elements are actually the same array

より詳細な説明については、配列の最後に新しいアイテムとして.push(ids)内容を追加します。したがって、shoplistを呼び出すたびに、shopitemsの最後に新しいアイテムが表示されます。ただし、追加するアイテムは配列であるため、その配列のコピーではなく、その配列への参照が追加されます。その後、その配列を変更すると、shopitems配列エントリは変更されたバージョンの配列を指します。idsshopitems

あなたはこのコードでそれを見ることができます:

var x = [];
var list = [];
x.push(1);       // contains contains [1]
list.push(x);    // list is [[1]]
x.push(2);       // x is [1,2]
list.push(x);    // list is [[1,2], [1,2]]  (contains two references to x)

このコード例では、listには2つの要素が含まれ、それぞれが同じライブバージョンを指しxます[1,2]

これは、デフォルトでは、javascriptが配列やオブジェクトなどの参照を渡すためです。配列要素をコンテナ配列にプッシュすると、その変数の静的コピーが配列に配置されません。元の変数へのポインタを置いています。その後、元の変数を変更すると、その変更は配列にも反映されます。

2番目のエントリを最初のエントリから分離するには、最初の配列のコピーを意識的に作成してそのコピーをコンテナ配列にプッシュするか、新しい配列を最初から作成してコンテナ配列にプッシュする必要があります。

たとえば、コンテナ配列に2つの独立した要素を作成する方法は次のとおりです。

var x = [];
var list = [];
x.push(1);       // contains contains [1]
list.push(x);    // list is [[1]]
x = [];          // set x to a new array (the old version of x is still in list)
x.push(1);       // x is [1]
x.push(2);       // x is [1,2]
list.push(x);    // list is [[1], [1,2]]  (contains two separate items)

または、次のコピーを作成しますx

var x = [];
var list = [];
x.push(1);       // contains contains [1]
list.push(x);    // list is [[1]]
x = x.slice(0);  // make a copy of x, the old version of x is still in list
x.push(1);       // x is [1]
x.push(2);       // x is [1,2]
list.push(x);    // list is [[1], [1,2]]  (contains two separate items)

ここで覚えておくべき重要なことは、JavaScriptでは、オブジェクトの割り当てや配列の割り当てはコピーを作成しないということです。元のデータ構造へのポインタを割り当てるだけです。元のデータ構造を変更すると、それはあなたが行ったすべての割り当てに反映されます。

コピーする場合は、明示的に新しい配列を作成するか、明示的にコピーを作成する必要があります。

于 2012-08-21T19:30:54.197 に答える
1

このコードを実行しました...

var shopitems = [];

shoplist([1]);
shoplist([1,2]);

function shoplist(ids) {
    WScript.Echo("ids passed to shoplist function: " + ids); // 1
    WScript.Echo("current ids in shopitems var: " + shopitems); // (empty)
    shopitems.push(ids);
    WScript.Echo("ids in shopitems after pushing: " + shopitems); // 1
    }

そしてこの出力を得ました。

ids passed to shoplist function: 1
current ids in shopitems var:
ids in shopitems after pushing: 1
ids passed to shoplist function: 1,2
current ids in shopitems var: 1
ids in shopitems after pushing: 1,1,2

それはうまく機能しているように私には見えます。

于 2012-08-21T19:34:23.510 に答える