Phép gán biến hoạt động như thế nào trong JavaScript?


97

Vì vậy, tôi đã chơi xung quanh một ngày chỉ để xem chính xác cách phân công khối lượng hoạt động trong JavaScript.

Đầu tiên, tôi đã thử ví dụ này trong bảng điều khiển:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

Kết quả là "thanh" được hiển thị trong một cảnh báo. Điều đó là đủ công bằng, abthực sự chỉ là bí danh cho cùng một đối tượng. Sau đó, tôi nghĩ, làm thế nào tôi có thể làm cho ví dụ này đơn giản hơn.

a = b = 'foo';
a = 'bar';
console.log(b);

Đó là điều khá giống nhau, phải không? Lần này, nó trả về fookhông barnhư tôi mong đợi từ hành vi của ví dụ đầu tiên.

Lý do tại sao điều này xảy ra?

NB Ví dụ này có thể được đơn giản hóa hơn nữa với đoạn mã sau:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(Tôi nghi ngờ rằng JavaScript xử lý các nguyên hàm như chuỗi và số nguyên khác với mã băm. Hàm băm trả về một con trỏ trong khi các nguyên hàm "cốt lõi" trả về một bản sao của chính chúng)


Giải thích nhiệm vụ ở đây: syntaxsuccess.com/viewarticle/...
TGH

Câu trả lời:


114

Trong ví dụ đầu tiên, bạn đang thiết lập một thuộc tính của một đối tượng hiện có. Trong ví dụ thứ hai, bạn đang gán một đối tượng hoàn toàn mới.

a = b = {};

abbây giờ là các con trỏ đến cùng một đối tượng. Vì vậy, khi bạn làm:

a.foo = 'bar';

Nó cũng thiết lập b.fookể từ abtrỏ đến cùng một đối tượng.

Tuy nhiên!

Nếu bạn làm điều này thay thế:

a = 'bar';

abây giờ bạn đang nói rằng chỉ đến một đối tượng khác. Điều này không ảnh hưởng đến những gì ađã chỉ đến trước đó.

Trong JavaScript, gán một biến và gán một thuộc tính là 2 hoạt động khác nhau. Tốt nhất bạn nên coi các biến là con trỏ đến các đối tượng và khi bạn gán trực tiếp cho một biến, bạn sẽ không sửa đổi bất kỳ đối tượng nào, chỉ bổ nhiệm biến của bạn cho một đối tượng khác.

Nhưng việc gán một thuộc tính, chẳng hạn như a.foo, sẽ sửa đổi đối tượng atrỏ đến. Tất nhiên, điều này cũng sửa đổi tất cả các tham chiếu khác trỏ đến đối tượng này đơn giản vì chúng đều trỏ đến cùng một đối tượng.


3
"bạn đang nói rằng một chỉ đến một đối tượng khác." Không, không sử dụng đối tượng từ. Một chuỗi không phải là một đối tượng trong JavaScript.
Nosredna

9
Có thể về mặt kỹ thuật các chuỗi không thuộc loại javascript "Đối tượng", nhưng chúng có thể được coi là các đối tượng theo nghĩa OO.
Alex Wayne

2
@Squeegy: chuỗi là nguyên thủy, không phải đối tượng: bạn không thể gán thuộc tính tùy ý cho chuỗi! Chúng chỉ hoạt động giống như đối tượng vì cái được gọi là autoboxing trong Java
Christoph

11
Nhưng chuỗi có các phương thức và thuộc tính, và nguyên mẫu của String chắc chắn có thể được sửa đổi. Chúng chắc chắn hoạt động như đồ vật.
Cameron Martin

9
@ddlshack: Như Christoph đã giải thích, điều này là do autoboxing. Nếu bạn định nghĩa một chuỗi thông qua var foo = new String('foo');, thì đó sẽ là một đối tượng chuỗi (và typeofsẽ xác nhận điều này). Nhưng nếu bạn khai báo nó thông qua một chuỗi ký tự, thì chúng là chuỗi nguyên thủy. Xem: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Lèse majesté vào

26

Câu hỏi của bạn đã được giải đáp một cách hài lòng bởi Squeegy - nó không liên quan gì đến các đối tượng so với các nguyên thủy, mà chỉ liên quan đến việc gán lại các biến so với thiết lập các thuộc tính trong cùng một đối tượng được tham chiếu.

Dường như có nhiều sự nhầm lẫn về các loại JavaScript trong các câu trả lời và nhận xét, vì vậy đây là phần giới thiệu nhỏ về hệ thống loại của JavaScript:

Trong JavaScript, về cơ bản có hai loại giá trị khác nhau: nguyên thủy và đối tượng (và không có thứ gì giống như 'băm').

Chuỗi, số và boolean cũng như nullundefinedlà nguyên thủy, các đối tượng là mọi thứ có thể có thuộc tính. Ngay cả các mảng và hàm là các đối tượng thông thường và do đó có thể chứa các thuộc tính tùy ý. Chúng chỉ khác nhau về thuộc tính [[Class]] bên trong (các hàm cũng có thuộc tính là [[Call]] và [[Construct]], nhưng này, đó là chi tiết).

Lý do mà các giá trị nguyên thủy có thể hoạt động giống như các đối tượng là do tính năng autoboxing, nhưng bản thân các giá trị nguyên thủy không thể chứa bất kỳ thuộc tính nào.

Đây là một ví dụ:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

Điều này sẽ xuất ra undefined: agiữ một giá trị nguyên thủy, giá trị này được thăng cấp cho một đối tượng khi gán thuộc tính foo. Nhưng đối tượng mới này ngay lập tức bị loại bỏ, vì vậy giá trị của foobị mất.

Hãy nghĩ về nó như thế này:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created

Trang của Tổ chức Mozilla 'Giới thiệu lại về JavaScript (hướng dẫn JS)' mô tả các đối tượng JavaScript "là các tập hợp đơn giản của các cặp tên-giá trị. Như vậy, chúng tương tự như ...", sau đó là danh sách các từ điển, băm , bảng băm và bản đồ băm từ các ngôn ngữ lập trình khác nhau. Trang tương tự mô tả các tham chiếu thuộc tính đối tượng dưới dạng tra cứu bảng băm. Vì vậy, các đối tượng là mọi thứ giống như một bảng 'băm'. Điều này không làm mất đi thông tin hữu ích khác, nhưng đặc điểm ban đầu của Chris Lloyd không phải là không chính xác.
C Perkins

2

Bạn ít nhiều đúng, ngoại trừ việc những gì bạn đang đề cập đến dưới dạng "băm" thực ra chỉ là cú pháp viết tắt cho một Đối tượng.

Trong ví dụ đầu tiên, ab đều tham chiếu đến cùng một đối tượng. Trong ví dụ thứ hai, bạn thay đổi a để chỉ một thứ khác.


Vậy tại sao lại có các tiêu chuẩn kép cho Object?
Chris Lloyd

Nó không phải là một tiêu chuẩn kép. Trong ví dụ đầu tiên, a và b vẫn tham chiếu đến cùng một đối tượng, bạn chỉ đang sửa đổi một thuộc tính của đối tượng đó. Trong ví dụ thứ hai, bạn đang trỏ một đối tượng khác.
Kevin

1
Không, sự khác biệt là trong trường hợp thứ hai, bạn đang xử lý một chuỗi, không phải một đối tượng.
Nosredna

1
Nói rõ hơn: Điều này không liên quan gì đến các chuỗi trả về một bản sao của chính chúng. Lý do hai đoạn mã khác nhau là ở đoạn thứ hai của Kevin (được giải thích đầy đủ hơn trong câu trả lời của Squeegy).
Chuck

Không quan trọng nếu bạn có một chuỗi hoặc một đối tượng trong biến. Bạn chỉ định một giá trị mới, khác và sau đó biến chứa giá trị mới, khác.
sth

2

đây là phiên bản câu trả lời của tôi:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory

0

Bạn đang đặt a để trỏ đến đối tượng chuỗi mới, trong khi b tiếp tục trỏ đến đối tượng chuỗi cũ.


0

Trong trường hợp đầu tiên bạn thay đổi một số thuộc tính của đối tượng có trong biến, trong trường hợp thứ hai, bạn gán một giá trị mới cho biến. Đó là những điều khác nhau về cơ bản. Các biến abkhông được liên kết kỳ diệu bằng cách nào đó bởi phép gán đầu tiên, chúng chỉ chứa cùng một đối tượng. Đó cũng là trường hợp trong ví dụ thứ hai, cho đến khi bạn gán một giá trị mới cho bbiến.


0

Sự khác biệt là giữa các loại đơn giản và các đối tượng.

Bất cứ thứ gì là một đối tượng (như một mảng hoặc một hàm) đều được truyền bằng tham chiếu.

Bất kỳ thứ gì thuộc loại đơn giản (như chuỗi hoặc số) đều được sao chép.

Tôi luôn có sẵn một hàm copyArray để tôi có thể chắc chắn rằng mình không tạo ra một loạt các bí danh cho cùng một mảng.


Sự khác biệt không đáng chú ý trong nhiều trường hợp, nhưng Javascript không thực sự chuyển hoặc gán bằng tham chiếu. Nó sao chép các giá trị tham chiếu.
Juan Pablo Califano

Những người này đã làm rất tốt việc giải thích nó, vì vậy tôi sẽ chỉ dán liên kết: stackoverflow.com/questions/40480/is-java-pass-by-reference (Tôi đề cập đến Java, nhưng ngữ nghĩa để chuyển và gán các giá trị / tham chiếu giống như trong Javascript)
Juan Pablo Califano

1
Thực ra câu trả lời này không chính xác, mọi thứ đều được chuyển theo giá trị trong JavaScript. Từ MDN, "Các tham số của một lệnh gọi hàm là các đối số của hàm. Các đối số được chuyển cho hàm theo giá trị. Nếu hàm thay đổi giá trị của một đối số, thay đổi này không được phản ánh trên toàn cục hoặc trong hàm đang gọi. Tuy nhiên, các tham chiếu đối tượng là các giá trị cũng vậy, và chúng đặc biệt: nếu hàm thay đổi thuộc tính của đối tượng được giới thiệu, thì thay đổi đó sẽ hiển thị bên ngoài hàm. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
bittersweetryan

Nguyên thủy hoạt động giống như các đối tượng bất biến ( chính xác như chúng ở chế độ nghiêm ngặt). Câu trả lời này không đúng.
Ry-
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.