Sao chép giá trị của một biến vào một biến khác


100

Tôi có một biến có đối tượng JSON làm giá trị của nó. Tôi trực tiếp gán biến này cho một số biến khác để chúng có cùng giá trị. Đây là cách nó hoạt động:

var a = $('#some_hidden_var').val(),
    b = a;

Điều này hoạt động và cả hai đều có giá trị như nhau. Tôi sử dụng mousemovetrình xử lý sự kiện để cập nhật bứng dụng của mình. Khi nhấp vào nút, tôi muốn hoàn nguyên bvề giá trị ban đầu, nghĩa là giá trị được lưu trữ trong a.

$('#revert').on('click', function(e){
    b = a;
});

Sau đó, nếu tôi sử dụng cùng một mousemovetrình xử lý sự kiện, nó sẽ cập nhật cả hai abkhi trước đó nó chỉ cập nhật bnhư mong đợi.

Tôi bối rối về vấn đề này! Có gì sai ở đây?


Vui lòng hiển thị trình xử lý di chuyển chuột của bạn. Ngoài ra, cho rằng nó ađược đặt từ .val()tôi giả sử nó là JSON (một chuỗi), không phải là một đối tượng - có đúng không? Bạn có sử dụng JSON.parse(a)tại một số điểm để có được một đối tượng thực tế không?
nnnnnn

Có, nó là một chuỗi nhưng tôi sử dụng $.parseJSONđể chuyển đổi nó thành một đối tượng. Cấu trúc: { 'key': {...}, 'key': {...}, ...}. Xin lỗi không thể đăng bất kỳ mã nào ở đây, không được phép ở nơi làm việc của tôi!
Rutwick Gangurde

1
vậy là amột đối tượng?
Armand

1
Có vẻ như vấn đề về phạm vi .. có phải là abiến toàn cục không?
msturdy

2
Mã bạn đã hiển thị chỉ có chuỗi. Nếu bạn đang nói các đối tượng thì nhiều biến có thể tham chiếu đến cùng một đối tượng và vì vậy đối tượng đó có thể bị thay đổi thông qua bất kỳ biến nào. Vì vậy, nếu không nhìn thấy nhiều mã thao tác các biến (nó $.parseJSON()đi vào đâu?) Thì thật khó để nói vấn đề là gì. Về các quy tắc tại nơi làm việc, bạn không cần phải đăng toàn bộ mã thực thực tế của mình, chỉ cần đưa ra một ví dụ ngắn hơn và chung chung hơn thể hiện vấn đề (và lý tưởng nhất là bao gồm một liên kết đến bản demo trực tiếp tại jsfiddle.net ) .
nnnnnn 16/09/13

Câu trả lời:


198

Điều quan trọng là phải hiểu =toán tử trong JavaScript làm và không làm gì.

Nhà =điều hành không tạo một bản sao của dữ liệu.

Các =nhà điều hành tạo ra một mới tham khảo đến cùng một dữ liệu.

Sau khi bạn chạy mã gốc của mình:

var a = $('#some_hidden_var').val(),
    b = a;

abbây giờ là hai tên khác nhau cho cùng một đối tượng .

Bất kỳ thay đổi nào bạn thực hiện đối với nội dung của đối tượng này sẽ được nhìn thấy giống hệt nhau cho dù bạn tham chiếu nó thông qua abiến hay bbiến. Chúng là cùng một đối tượng.

Vì vậy, khi bạn cố gắng "hoàn nguyên" bvề ađối tượng ban đầu bằng mã này:

b = a;

Mã thực sự không làm gì cả , bởi vì abhoàn toàn giống nhau. Mã giống như khi bạn viết:

b = b;

mà rõ ràng sẽ không làm gì cả.

Tại sao mã mới của bạn hoạt động?

b = { key1: a.key1, key2: a.key2 };

Ở đây bạn đang tạo một đối tượng hoàn toàn mới với {...}đối tượng theo nghĩa đen. Đối tượng mới này không giống với đối tượng cũ của bạn. Vì vậy, bây giờ bạn đang đặt blàm tham chiếu đến đối tượng mới này, đối tượng này sẽ thực hiện những gì bạn muốn.

Để xử lý bất kỳ đối tượng tùy ý nào, bạn có thể sử dụng một hàm sao chép đối tượng, chẳng hạn như hàm được liệt kê trong câu trả lời của Armand hoặc vì bạn đang sử dụng jQuery chỉ cần sử dụng $.extend()hàm . Hàm này sẽ tạo bản sao nông hoặc bản sao sâu của một đối tượng. (Đừng nhầm lẫn điều này với $().clone()phương pháp sao chép các phần tử DOM, không phải các đối tượng.)

Đối với một bản sao cạn:

b = $.extend( {}, a );

Hoặc một bản sao sâu:

b = $.extend( true, {}, a );

Sự khác biệt giữa bản sao cạn và bản sao sâu là gì? Một bản sao cạn tương tự như mã của bạn tạo một đối tượng mới với một đối tượng theo nghĩa đen. Nó tạo ra một đối tượng cấp cao nhất mới chứa các tham chiếu đến các thuộc tính giống như đối tượng ban đầu.

Nếu đối tượng của bạn chỉ chứa các kiểu nguyên thủy như số và chuỗi, thì bản sao sâu và bản sao nông sẽ làm chính xác điều tương tự. Nhưng nếu đối tượng của bạn chứa các đối tượng hoặc mảng khác được lồng vào bên trong nó, thì một bản sao cạn sẽ không sao chép các đối tượng lồng nhau đó, nó chỉ tạo ra các tham chiếu đến chúng. Vì vậy, bạn có thể gặp vấn đề tương tự với các đối tượng lồng nhau mà bạn gặp phải với đối tượng cấp cao nhất của mình. Ví dụ, cho đối tượng này:

var obj = {
    w: 123,
    x: {
        y: 456,
        z: 789
    }
};

Nếu bạn tạo một bản sao cạn của đối tượng đó, thì thuộc xtính của đối tượng mới của bạn là cùng một xđối tượng từ bản gốc:

var copy = $.extend( {}, obj );
copy.w = 321;
copy.x.y = 654;

Bây giờ các đối tượng của bạn sẽ giống như sau:

// copy looks as expected
var copy = {
    w: 321,
    x: {
        y: 654,
        z: 789
    }
};

// But changing copy.x.y also changed obj.x.y!
var obj = {
    w: 123,  // changing copy.w didn't affect obj.w
    x: {
        y: 654,  // changing copy.x.y also changed obj.x.y
        z: 789
    }
};

Bạn có thể tránh điều này bằng một bản sao sâu. Bản sao sâu tái diễn vào mọi đối tượng và mảng lồng nhau (và Ngày tháng trong mã của Armand) để tạo bản sao của các đối tượng đó giống như cách nó tạo bản sao của đối tượng cấp cao nhất. Vì vậy, thay đổi copy.x.ysẽ không ảnh hưởng obj.x.y.

Câu trả lời ngắn gọn: Nếu nghi ngờ, bạn có thể muốn có một bản sao sâu.


Cảm ơn, đây là những gì tôi đang tìm kiếm. Bất kỳ cách nào để làm việc xung quanh nó? Trong tình huống hiện tại của tôi, thật dễ dàng để sửa chữa vì tôi chỉ có 2 thuộc tính. Nhưng có thể có những vật thể lớn hơn.
Rutwick Gangurde

1
Câu trả lời của Armand có một chức năng sao chép đối tượng sẽ thực hiện thủ thuật. Hoặc vì bạn đang sử dụng jQuery, bạn có thể sử dụng hàm tích hợp sẵn $.extend(). Chi tiết ở trên. :-)
Michael Geary

Lời giải thích tuyệt vời Michael!
Rutwick Gangurde 19/09/13

7
@MichaelGeary Nó có thể là nitpicking nhưng không phải quy tắc chỉ áp dụng cho các đối tượng chứ không phải các biến nguyên thủy? nó có thể đáng chú ý trong câu trả lời
Jonathan dos Santos

JS giải pháp này là trong Armands câu trả lời như Michael Geary nói =.
AlexanderGriffin

57

Tôi thấy việc sử dụng JSON hoạt động nhưng hãy xem các tham chiếu vòng tròn của chúng tôi

var newInstance = JSON.parse(JSON.stringify(firstInstance));

2
Tôi nhận ra điều này là hơi muộn, nhưng có vấn đề gì với phương pháp này không? Nó dường như hoạt động đáng kinh ngạc trong lần thử đầu tiên.
Mankind1023,

2
Điều này rất tốt trong hầu hết mọi tình huống, tuy nhiên nếu bạn đang làm việc với dữ liệu có khả năng bị lệch, nó sẽ làm hỏng chương trình của bạn. một số mã để minh họa: let a = {}; let b = {a:a}; a.b = b; JSON.stringify(a)sẽ đưa ra một TypeError
schu34

Giải pháp này hoạt động tuyệt vời. Tuy nhiên, điều này tốn kém như thế nào trong quá trình chế biến? Có vẻ như điều này hoạt động bằng cách phủ định kép.
Abel Callejo

29

câu hỏi đã được giải quyết từ khá lâu, nhưng để tham khảo trong tương lai, một giải pháp khả thi là

b = a.slice(0);

Hãy cẩn thận, điều này chỉ hoạt động chính xác nếu a là một mảng số và chuỗi không lồng nhau


23

newVariable = originalVariable.valueOf();

cho các đối tượng bạn có thể sử dụng, b = Object.assign({},a);


3
đối với các đối tượng bạn có thể sử dụng, b = Object.assign ({}, a);
Kishor Patil

15

Lý do cho điều này là đơn giản. JavaScript sử dụng các b = atham chiếu , vì vậy khi bạn chỉ định, bạn đang gán một tham chiếu cho bdo đó khi cập nhật, abạn cũng đang cập nhậtb

Tôi đã tìm thấy điều này trên stackoverflow và sẽ giúp ngăn chặn những điều như thế này trong tương lai bằng cách chỉ cần gọi phương thức này nếu bạn muốn tạo bản sao sâu của một đối tượng.

function clone(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

Cảm ơn! Tôi đã xem bài viết đó trước khi tôi đăng một câu hỏi mới. Nhưng tôi không chắc liệu điều đó có giúp ích cho tình huống của tôi hay không. Hóa ra đó là thứ tôi cần.
Rutwick Gangurde

Không nên tất cả trừ lần đầu tiên if (obj instanceof x) { ... }khác nếu?
Zac

@Zac Không phải trong trường hợp này. Mọi if-body (được quan tâm) đều trả về một giá trị. (lối hoạt động trước khi tới nếu)
Bitterblue

8

Tôi không hiểu tại sao câu trả lời lại phức tạp như vậy. Trong Javascript, các nguyên thủy (chuỗi, số, v.v.) được chuyển theo giá trị và được sao chép. Các đối tượng, bao gồm cả mảng, được truyền bằng tham chiếu. Trong mọi trường hợp, việc gán giá trị mới hoặc tham chiếu đối tượng cho 'a' sẽ không thay đổi 'b'. Nhưng thay đổi nội dung của 'a' sẽ thay đổi nội dung của 'b'.

var a = 'a'; var b = a; a = 'c'; // b === 'a'

var a = {a:'a'}; var b = a; a = {c:'c'}; // b === {a:'a'} and a = {c:'c'}

var a = {a:'a'}; var b = a; a.a = 'c'; // b.a === 'c' and a.a === 'c'

Dán bất kỳ dòng nào ở trên (từng dòng một) vào nút hoặc bất kỳ bảng điều khiển javascript nào của trình duyệt. Sau đó, nhập bất kỳ biến nào và bảng điều khiển sẽ hiển thị giá trị của biến đó.


4

Đối với các chuỗi hoặc giá trị đầu vào, bạn chỉ cần sử dụng điều này:

var a = $('#some_hidden_var').val(),
b = a.substr(0);

Tại sao bạn sẽ làm chuỗi con một nguyên thủy? Chỉ cần gán nó, nó sẽ sao chép chuỗi. Chỉ các đối tượng và mảng (là các đối tượng) được truyền bằng tham chiếu.
Trenton D. Adams

2

Hầu hết các câu trả lời ở đây là sử dụng các phương pháp có sẵn hoặc sử dụng thư viện / khuôn khổ. Phương pháp đơn giản này sẽ hoạt động tốt:

function copy(x) {
    return JSON.parse( JSON.stringify(x) );
}

// Usage
var a = 'some';
var b = copy(a);
a += 'thing';

console.log(b); // "some"

var c = { x: 1 };
var d = copy(c);
c.x = 2;

console.log(d); // { x: 1 }

Thiên tài! Các phương pháp khác chuyển mọi thứ sang từ điển, bao gồm cả các mảng bên trong chúng. Với điều này, tôi sao chép đối tượng và nội dung vẫn còn nguyên vẹn, không giống như thế này:Object.assign({},a)
Tiki

0

Tôi đã tự giải quyết nó trong thời điểm hiện tại. Giá trị gốc chỉ có 2 thuộc tính con. Tôi đã cải cách một đối tượng mới với các thuộc tính từ ađó và sau đó gán nó cho b. Bây giờ trình xử lý sự kiện của tôi chỉ cập nhật bvà bản gốc của tôi avẫn như cũ.

var a = { key1: 'value1', key2: 'value2' },
    b = a;

$('#revert').on('click', function(e){
    //FAIL!
    b = a;

    //WIN
    b = { key1: a.key1, key2: a.key2 };
});

Điều này hoạt động tốt. Tôi đã không thay đổi một dòng nào ở bất kỳ đâu trong mã của mình ngoại trừ phần trên và nó hoạt động theo cách tôi muốn. Vì vậy, hãy tin tôi, không có gì khác đang cập nhật a.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.