1
var a = [3, 4, 5];
var b = [6, 7, 8];

function why() {
    b = a;
    b[0] = 1;
    alert(a[0] + '    ' + b[0]);
}
why();

結果はa[0]=1, b[0]=1;です。JavaScript が参照渡ししているように見えますか?

しかし、この場合:

var a = [3, 4, 5];
var b = [6, 7, 8];

function why() {
    b = a;
    b = [1, 2, 3];
    alert(a + '    ' + b);
}
why();

結果はa=[3,4,5]b = [1,2,3]です。なぜ値渡しなのですか?参照渡しを避けるには?

4

6 に答える 6

3

どちらの場合も値渡しです。JavaScript は常に値渡しであり、参照渡しする方法はありません。特に、渡される値は常にオブジェクトへのポインターです。(実際には、プリミティブは値によって直接渡されますが、違いは変更可能な値に対してのみ観察でき、プリミティブは不変であるため、違いはありません。)

最初のケースでは、ポインターが指すオブジェクトを変更しています。ただし、参照は変更されず、参照が指すオブジェクトのみが変更されます。参照は依然として同じオブジェクトを指していますが、オブジェクトは以前とは異なって見えるだけです。

この値渡しの特定のケースで、値がポインターである場合は、オブジェクト共有による呼び出し、オブジェクトによる呼び出し、または共有による呼び出しと呼ばれることがあり、引数を渡す方法です。ほとんどすべてのオブジェクト指向言語で動作します: Smalltalk、Python、Ruby、Java、C# (デフォルトでは、ref修飾子を明示的に指定することで参照渡しが可能) など。

于 2013-05-27T11:53:37.367 に答える
0

これらすべての優れた回答の後、これ以上言うことはありません。ECMAScript 5 仕様に基づいて説明します。

また、回答の最後にある deepclone 関数。

ES5仕様で定義されているように、

11.13.1 単純代入 ( = ) プロダクション AssignmentExpression : LeftHandSideExpression = AssignmentExpression は次のように評価されます。

  1. lref を LeftHandSideExpression の評価結果とします。
  2. AssignmentExpression を評価した結果を rref とします。
  3. rval とするGetValue(rref)
  4. 次の条件がすべて真の場合、SyntaxError 例外をスローします。
    • Type(lref) is Reference is true
    • IsStrictReference(lref) は真です
    • Type(GetBase(lref)) は環境レコードです
    • GetReferencedName(lref) は「eval」または「arguments」のいずれかです
  5. コールしPutValue(lref, rval)ます。
  6. rval を返します。

つまり、ポイント3に到達し、それrrefがオブジェクトである場合に何が起こっているかは、
§8.7.1 です (In のセクション 4b がGetValue(V) //V==rref 興味深いポイントです)。

4. IsPropertyReference(V) の場合、

  • (b) base を this 値として get 内部メソッドを呼び出し、引数に GetReferencedName(V) を渡した結果を返します。

この時点でオブジェクトへの参照rvalが保持され、 5 に挿入されます。

§8.7.2 (ここでも 4b がPutValue(V,W) //V==lref , W==rval興味深い部分です) が登場します。

注: W は値ではなく、割り当てたいオブジェクトへの参照です。

4. IsPropertyReference(V) の場合

  • (b) base を this 値として使用して put 内部メソッドを呼び出し、プロパティ名に GetReferencedName(V)、値に W、Throw フラグに IsStrictReference(V) を渡します。

あなたのケースでわかるように、オブジェクトへの参照である atmのは、いわば への参照である の結果に置き換えられますb[6, 7, 8]GetValue(rref)[1, 2, 3];

でも


どうやら深いクローン機能を探しているようです

これが1つです。

 Object.defineProperty(Object.prototype, "clone", {
        value: function (deep) {
            var type = Object.prototype.toString.call(this).match(/^\[object (.+?)\]$/)[1];
            if (type !== "Object") {
                return this.valueOf;
            }

            var clone = {};
            if (!deep) {
                for (var prp in this) {
                    clone[prp] = this[prp];
                }
            } else {
                for (var prop in this) {
                    if (typeof this[prop] !== "undefined" && this[prop] !== null)
                        clone[prop] = (typeof this[prop] !== "object" ? this[prop] : this[prop].clone((typeof deep == "boolean" ? deep : (deep - 1))));
                    else
                        clone[prop] = "";
                }
            }
            return clone;
        },
        enumerable: false
    });
Object.defineProperty(Array.prototype, "clone", {
        value: function (deep) {
            var clone = [];
            if (!deep) clone = this.concat();
            else this.forEach(function (e) {
                        if (typeof e !== "undefined" && e !== null)
                            clone.push((typeof e !== "object" ? e : e.clone((deep - 1))));
                        else
                            clone.push("");
                    });

            return clone;
        },
        enumerable: false
    });

var a = [1, [2, { a: 3 } ], 4];
var b = a.clone(Infinity);

a[1][1]["a"] = "cloned";

console.log(a[1][1]["a"], b[1][1]["a"]); //"cloned" , 3

そしてJSBinのデモ

.clone(levelOfDeepness)それを使用するには、オブジェクトまたは配列を呼び出すだけです

注: 簡単にするために Objects プロトタイプを使用しました。これは、.cloneObject 要素と Array 要素を複製しながら直接呼び出すことができるためです (単一の関数で型チェック バリアントよりもパフォーマンスが向上します)。

于 2013-05-27T13:57:58.380 に答える
0

最初のケースでは、配列項目の 1 つの値を変更しています。b は a と等しくなり、a[0] と b[0] は同じ値になります。

2 番目のケースでは、b が a を指すようにしたにもかかわらず、新しいオブジェクトを b に割り当てました。繰り返しますが、b と a は 2 つの異なるオブジェクトです。つまり、値が異なります。

于 2013-05-27T11:53:15.000 に答える