Nội dung bài viết
Mình là một dev front-end – một người code Javascript hơn 2 năm nay, mình đã nghĩ rằng mình hiểu những thứ cơ bản như tham trị và tham chiếu của JS cho đến ngày vừa rồi mình gặp một trường hợp khá buồn cười về shallow copy. Lúc này mình nhận ra mình đã hiểu sai về nó, vì thế mình viết bài này nhằm ghi chú lại kiến thức cũng như giúp đỡ anh em đang còn nhầm lẫn về vấn đề này . Vào bài thôi!
Tham trị đối với primitive data type
Có 6 kiểu dữ liệu nguyên thủy (primitive data type): undefined, boolean, number, string, bigint, symbol. Khi ta sao chép giá trị biến này cho biến khác thì giá trị của 2 biến này độc lập và không liên quan với nhau.
Ví dụ nhé:
let a = 1 let b = a b = 2 console.log(a) // 1 console.log(b) // 2
Dù gán b = a
, nhưng khi b
thay đổi thì a
vẫn không thay đổi!
Khi giá trị thuộc kiểu dữ liệu nguyên thủy, biến sẽ chứa giá trị của biến đó.
Mô tả bộ nhớ ở ví dụ trên
Tương tự với ví dụ dưới đây
let name = 'Du Thanh Duoc' const change = (value) => { value = 'XdevClass' } change(name) console.log(name) // 'Du Thanh Duoc'
Tham chiếu đối với Object
Anh em lưu ý là trong Javascript: object, array, function thì đều được coi là object cả nhé.
Khi gán hoặc sao chép dữ liệu thuộc kiểu object thì biến đó chỉ lưu địa chỉ của giá trị đó trên vùng nhớ. Nó không lưu giá trị được gán.
Gán Object
Ở trên ví dụ với object rồi, giờ ví dụ với array nhé
let cars1 = ['BMW', 'Mercedes'] let cars2 = cars1 cars2 = ['Toyota', 'Hyundai'] console.log(cars1) // ['BMW', 'Mercedes'] console.log(cars2) // ['Toyota', 'Hyundai']
Sơ đồ vùng nhớ
add_1(car1)
đại diện cho địa chỉ vùng nhớ của array ['BMW', 'Mercedes']
add_2(car2)
đại diện cho địa chỉ vùng nhớ của array ['Toyota', 'Hyundai']
và add_1(car1)
khác add_2(car2)
Nhìn vào sơ đồ trên ta thấy được ban đầu thì car1
và car2
đều lưu giá trị ô nhớ giống nhau (đều là add_1(car1)
) và chia sẽ cho nhau các thuộc tính bên trong array. car1
và car2
đều có giá trị ô nhớ giống nhau nhưng nó lại ở 2 mắt xích khác nhau, vì thế khi thay đổi car2
bằng 1 giá trị khác thì sẽ không ảnh hưởng đến car1
. Nếu như bạn thay đổi car2[0]
thì car1[0]
cũng sẽ thay đổi theo vì 2 vị trí này chia sẽ với nhau.
Xem ví dụ bên dưới để rõ hơn nhé
Thay đổi thuộc tính Object
Ví dụ:
const person1 = { name: 'Du Thanh Duoc', age: 24 } const person2 = person1 person2.age = 25 console.log(person1) // { name: 'Du Thanh Duoc', age: 25 } console.log(person2) // { name: 'Du Thanh Duoc', age: 25 }
Bạn đang thắc mắc tại sao khai báo bằng const
rồi nhưng lại có thể thay đổi giá trị bên trong object được phải không? Thực ra thì khi bạn khai báo const
kết hợp với object thì bạn sẽ không thể thay đổi object nhưng lại có thể thay đổi thuộc tính của nó.
Như ví dụ thì bạn không thể thay đổi person2
bằng một object mới được, mà chỉ có thể thay đổi person2.age
hoặc person2.name
. Điều này liên quan đến vùng nhớ như mình đã nói ở bên trên, nếu bạn thay đổi nguyên 1 object thì vùng nhớ của person2
sẽ thay đổi và const
không cho phép nên sẽ gây lỗi. Nhưng nếu bạn thay đổi thuộc tính bên trong thì vùng nhớ object bên ngoài vẫn giữ nguyên.
Như sơ đồ trên ta có thể thấy được person1
và person2
chia sẽ với nhau thuộc tính name
và age
, vì thế khi thay đổi age
thì cả 2 sẽ đều bị thay đổi.
Shallow copy object
Ví dụ:
let person1 = { name: "Du Thanh Duoc", skill: { coding: "Javascript" } }; let person2 = { ...person1 }; console.log(person1.skill === person2.skill); //true person2.skill = { coding: "HTML" }; console.log(person1.skill); // { coding: "Javascript" } console.log(person2.skill); // { coding: "HTML" } console.log(person1.skill === person2.skill); // false
Sơ đồ giải thích

Shallow Copy
Nhìn vào sơ đồ trên ta dễ dàng thấy được khi thực hiện shallow copy, Javascript sẽ tạo ra một object mới đến person2
sẽ lưu địa chỉ một ô nhớ mới. Nhưng vùng nhớ person1.skill
sẽ giống person2.skill
. Lưu ý là chúng chỉ giống thôi chứ không chia sẽ, nhìn trên hình ta thấy thì nó sẽ tách ra 2 nhánh khác nhau, chỉ có thuộc tính coding
là chia sẽ giữa 2 object. Vậy nên khi thay đổi person2.skill
thì person1.skill
không bị thay đổi.
Nếu anh em thay đổi person2.skill.coding
thì person1.skill.coding
cũng sẽ bị đổi theo vì 2 thằng này chia sẽ với nhau.
Tóm lại
Thực ra kiến thức này được coi là căn bản thôi, nhưng mình nghĩ rằng nhiều anh em khi code với Javascript cũng sẽ dễ nhầm lẫn (điển hình là mình, ngại quá 😀 ). Hi vọng qua bài này mình đã giúp anh em hiểu rõ hơn về tham trị và tham chiếu trong Javascript một cách dễ dàng nhất.
Ngoài ra mình còn một số bài viết liên quan đến tham trị và tham chiếu nếu anh em muốn clean hơn trong các trường hợp thực tế thì nên xem qua.
Tuyệt vời !! Hóng một bài các thư viện lớn dùng JS và cách hoạt động của các thư viện đó.
Oke nha bạn 😂
sai chính tả: “chia sẻ” chứ không phải “chia sẽ” ạ