JavaScript: Giá trị nguyên thủy và giá trị tham chiếu trong Javascript
Tổng quan
Trong hướng dẫn này, bạn sẽ tìm hiểu về hai loại giá trị khác nhau trong JavaScript bao gồm giá trị nguyên thủy và giá trị tham chiếu.
JavaScript có hai loại giá trị khác nhau:
- Giá trị nguyên thủy (Primitive values)
- Các giá trị tham chiếu (Reference Values)
Giá trị nguyên thủy là các đơn vị dữ liệu nguyên tử trong khi giá trị tham chiếu là các đối tượng có thể bao gồm nhiều giá trị.
Bộ nhớ stack và heap
Khi bạn khai báo các biến , JavaScript engine sẽ phân bổ bộ nhớ cho chúng trên hai vị trí bộ nhớ: stack và heap.
Dữ liệu tĩnh là dữ liệu có kích thước cố định tại thời điểm biên dịch. Dữ liệu tĩnh bao gồm:
- Các giá trị nguyên thủy ( null , undefined , boolean , number, string, symbol và BigInt )
- Các giá trị tham chiếu đề cập đến các đối tượng.
Vì dữ liệu tĩnh có kích thước không thay đổi nên JavaScript engine sẽ phân bổ một lượng không gian bộ nhớ cố định cho dữ liệu tĩnh và lưu trữ nó trên stack.
Ví dụ: phần sau đây khai báo hai biến và khởi tạo giá trị của chúng thành một chuỗi ký tự và một số:
let name = 'John'; let age = 25;
Vì name
và age
là các giá trị nguyên thủy nên JavaScript engine lưu trữ các biến này trên stack như trong hình sau:
Lưu ý rằng chuỗi là đối tượng (object) trong nhiều ngôn ngữ lập trình, bao gồm Java và C#. Tuy nhiên, chuỗi là giá trị nguyên thủy trong JavaScript.
Không giống như stack, JavaScript lưu trữ các đối tượng (và hàm) trên heap. JavaScript engine không phân bổ lượng bộ nhớ cố định cho các đối tượng này. Thay vào đó, nó sẽ phân bổ nhiều không gian hơn khi cần thiết.
Ví dụ sau đây định nghĩa các biến name
, age
, và person
:
let name = 'John'; let age = 25; let person = { name: 'John', age: 25, };
Bên trong, JavaScript engine phân bổ bộ nhớ như trong hình sau:
Thuộc tính động
Giá trị tham chiếu cho phép bạn thêm, thay đổi hoặc xóa thuộc tính bất kỳ lúc nào. Ví dụ:
let person = { name: 'John', age: 25, }; // add the ssn property person.ssn = '123-45-6789'; // change the name person.name = 'John Doe'; // delete the age property delete person.age; console.log(person);
Output:
{ name: 'John Doe', ssn: '123-45-6789' }
Không giống như giá trị tham chiếu, giá trị nguyên thủy không thể có thuộc tính. Điều này có nghĩa là bạn không thể thêm thuộc tính vào giá trị nguyên thủy.
JavaScript cho phép bạn thêm thuộc tính vào giá trị nguyên thủy. Tuy nhiên, nó sẽ không có tác dụng gì. Ví dụ:
let name = 'John'; name.alias = 'Knight'; console.log(name.alias); // undefined
Trong ví dụ này, chúng tôi thêm thuộc tính alias
vào name
là biến với giá trị nguyên thủy. Nhưng khi chúng ta truy cập thuộc tính alias
thông qua name
, nó sẽ trả về undefined
.
Sao chép giá trị
Khi bạn gán một giá trị nguyên thủy từ biến này sang biến khác, JavaScript engine sẽ tạo một bản sao của giá trị đó và gán nó cho biến đó. Ví dụ:
let age = 25; let newAge = age;
Trong ví dụ này:
- Đầu tiên, khai báo một biến
age
và khởi tạo giá trị của nó bằng25
. - Thứ hai, khai báo một biến khác
newAge
và gán giá trị biếnage
chonewAge
.
Thực tế, JavaScript engine tạo một bản sao của giá trị nguyên thủy 25
và gán nó cho biến newAge.
Hình ảnh sau đây minh họa bộ nhớ ngăn xếp sau khi gán:
Trên bộ nhớ stack, newAge
và age
là các biến riêng biệt. Nếu bạn thay đổi giá trị của một biến, nó sẽ không ảnh hưởng đến biến kia.
let age = 25; let newAge = age; newAge = newAge + 1; console.log(age, newAge); // 25 26
Khi bạn gán giá trị tham chiếu từ biến này sang biến khác, JavaScript engine sẽ tạo tham chiếu để cả hai biến đều tham chiếu đến cùng một đối tượng trên bộ nhớ heap. Điều này có nghĩa là nếu bạn thay đổi một biến, nó sẽ ảnh hưởng đến biến kia.
let person = { name: 'John', age: 25, }; let member = person; member.age = 26; console.log(person); console.log(member);
Giải thích
Đầu tiên, khai báo một biến person và khởi tạo giá trị của nó bằng một đối tượng có hai thuộc tính name
và age
.
Thứ hai, gán biến person cho biến member. Trong bộ nhớ, cả hai biến đều tham chiếu đến cùng một đối tượng, như trong hình sau:
Thứ ba, thay đổi thuộc tính age của đối tượng thông qua biến member:
Vì cả person
và member
đều tham chiếu đến cùng một đối tượng nên việc thay đổi đối tượng thông qua biến member cũng được phản ánh trong biến person.
Tổng kết
- Javascript có hai loại giá trị: giá trị nguyên thủy và giá trị tham chiếu.
- Bạn có thể thêm, thay đổi hoặc xóa các thuộc tính của giá trị tham chiếu, trong khi bạn không thể làm điều đó với giá trị nguyên thủy.
- Sao chép một giá trị nguyên thủy từ biến này sang biến khác sẽ tạo ra một bản sao giá trị riêng. Điều đó có nghĩa là việc thay đổi giá trị của một biến sẽ không ảnh hưởng đến biến kia.
- Sao chép giá trị tham chiếu từ biến này sang biến khác sẽ tạo tham chiếu sao cho hai biến tham chiếu đến cùng một đối tượng. Điều này có nghĩa là việc thay đổi đối tượng thông qua một biến sẽ phản ánh ở một biến khác.