Property flags và descriptors là gì – Bí ẩn bên trong Javascript Object
Châm ngôn của mình là học để kiếm tiền.
Vì thế mình build các khóa học của mình để giúp anh em tiến bộ nhanh hơn x10 lần , để kiếm được nhiều tiền hơn
- 🏆 React.js Super: Trở thành React.js Developer trong 7 ngày với mức thu nhập 20 triệu/tháng
- 🏆 Node.js Super: Giúp bạn học cách phân tích, thiết kế, deploy 1 API Backend bằng Node.js
- 🏆 Next.js Super: Mình sẽ chia sẻ từ A-Z kiến thức về Next.js, thứ giúp mình kiếm hơn 1 tỉ/năm
Như chúng ta biết thì Object lưu trữ các thuộc tính, các thuộc tính chỉ đơn giản là các cặp “key-value”. Nhưng ngoài ra thì object còn chứa nhiều điều hay ho phía sau.
🥇Property flags – các cờ thuộc tính
Thuộc tính object, bên cạnh một value
còn có một số thuộc tính đặc biệt (gọi là các cờ):
writable
– nếu true, giá trị có thể thay đổi, còn không thì chỉ đọc (read-only )enumerable
– nếu true, thì thuộc tính được liệt kê khi lặp, còn không thì không được liệt kêconfigurable
– nếu true, thuộc tính có thể bị xóa,writable
vàenumerable
có thể bị thay đổi, còn không thì không thể
Một thuộc tính sẽ có đầy đủ 3 cờ, người ta gọi 3 cờ của thuộc tính là bộ mô tả thuộc tính (property descriptor)
Phương thức Object.getOwnPropertyDescriptor
cho phép chúng ta lấy đầy đủ thông tin về thuộc tính
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName)
let user = {
name: 'John'
}
let descriptor = Object.getOwnPropertyDescriptor(user, 'name')
console.log(descriptor)
/* property descriptor:
{
configurable: true,
enumerable: true,
value: "John",
writable: true
}
*/
Để thay đổi cờ, chúng ta có thể sử dụng Object.defineProperty
Object.defineProperty(obj, propertyName, descriptor)
descriptor
: Một object miêu tả các thuộc tính, nếu không cung cấp thì mặc định là false
let user = {}
Object.defineProperty(user, 'name', {
value: 'John'
})
let descriptor = Object.getOwnPropertyDescriptor(user, 'name')
console.log(descriptor)
/*
{
configurable: false,
enumerable: false,
value: "John",
writable: false
}
*/
🥇Non-writable – Không thể thay đổi
Cùng làm user.name
không thể bị assign lại bằng cách thay đổi cờ writable
sang false
let user = {
name: 'John'
}
Object.defineProperty(user, 'name', {
writable: false
})
user.name = 'Pete' // Error: Cannot assign to read only property 'name'
💡 Mẹo:
Lưu ý: Nếu không trong chế độ strict-mode thì sẽ không xảy ra lỗi, nhưng quá trình trên sẽ không thực hiện thành công
Đây là ví dụ thay đổi cờ writable
ngay từ lúc tạo
let user = {}
Object.defineProperty(user, 'name', {
value: 'John',
enumerable: true,
configurable: true
})
console.log(user.name) // John
user.name = 'Pete' // Error
🥇Non-enumerable – Không thể liệt kê
Cùng custom toString
của user
let user = {
name: 'John',
toString() {
return this.name
}
}
// Mặc định thì cả 2 thuộc tính sẽ được liệt kê trong vòng lặp
for (let key in user) console.log(key) // name, toString
Bây giờ chúng ta không thích toString
xuất hiện khi trong vòng lặp, ta có thể set enumerable:false
let user = {
name: 'John',
toString() {
return this.name
}
}
Object.defineProperty(user, 'toString', {
enumerable: false
})
// Bây giờ thì toString biến mất
for (let key in user) console.log(key) // name
Và đó cũng là lý do khi ta log một object thì ta nhận thấy một số thuộc tính bị làm mờ
non-enumerable cũng có hiệu lực trong Object.keys
alert(Object.keys(user)) // name
🥇Non-configurable – Không thể cấu hình
Ý tưởng của configurable: false
là để ngăn chặn thay đổi cờ thuộc tính và xóa thuộc tính, trong khi đó bạn vẫn có thể thay đổi value của thuộc tính Cờ non-configurable (configurable:false
) thỉnh thoảng được set mặc định trong các Object xây dựng sẵn. Ví dụ Math.PI
thì non-writable, non-enumerable, non-configurable
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');
console.log(descriptor)
/*
{
value: 3.141592653589793,
writable: false,
enumerable: false,
configurable: false
}
*
Vì thế chúng ta không thể thay đổi giá trị của Math.PI
hoặc viết lại nó
Math.PI = 3 // Error
// Không thể xóa Math.PI
delete Math.PI
Một khi chúng ta set một thuộc tính là non-configurable (configurable:false
) thì chúng ta không thể thay đổi lại bằng defineProperty
Khi configurable: false
thì chúng ta sẽ bị một số hạn chế trên defineProperty
- Không thể thay đổi cờ
configurable
- Không thể thay đổi cờ
enumerable
- Không thể thay đổi
writable: false
sangtrue
(ngược lại thì ok) - Không thể thay đổi
getter/setter
Đây là user.name
non-configurable, nhưng chúng ta còn có thể thay đổi (vì nó writable)
let user = {
name: 'John'
}
Object.defineProperty(user, 'name', {
configurable: false
})
user.name = 'Pete' // Chạy ok
delete user.name // Error
Và bây giờ chúng ta làm cho user.name
mãi mãi bị “niêm phong”
let user = {
name: 'John'
}
Object.defineProperty(user, 'name', {
writable: false,
configurable: false
})
// Không thể thay đổi user.name và cờ của nó
// Ngay cả việc xóa user.name
user.name = 'Pete'
delete user.name
Object.defineProperty(user, 'name', { value: 'Pete' })
🥇Object.defineProperties
Object.defineProperties(obj, descriptors) cho phép chúng ta định nghĩa nhiều thuộc tính cùng 1 lúc
Cú pháp:
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
})
Ví dụ
Object.defineProperties(user, {
name: { value: 'John', writable: false },
surname: { value: 'Smith', writable: false }
// ...
})
🥇Object.getOwnPropertyDescriptors
Để lấy tất cả property descriptor cùng một lần, chúng ta có thể sử dụng Object.getOwnPropertyDescriptors(obj)
Kết hợp với Object.defineProperties
nó có thể được sử dụng như một cách để clone một object
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj))
Bình thường khi clone một object, chúng ta thường copy thuộc tính như thế này
for (let key in user) {
clone[key] = user[key]
}
Nhưng điều này không copy cờ. Vì thế nếu muốn một cách clone tốt hơn thì Object.defineProperties
là một sự lựa chọn tối ưu. Một sự khác biệt khác là for...in
bỏ qua các thuộc tính tượng trưng, nhưng Object.getOwnPropertyDescriptors
sẽ return về tất cả property descriptors bao gồm loại tượng trưng.
🥇Sealing an object globally – Niêm phong một object toàn bộ
- Object.preventExtensions(obj) Ngăn cấm thêm thuộc tính mới vào object
- Object.seal(obj) Ngăn cấm thêm/xóa các thuộc tính. Set
configurable: false
cho tất cả thuộc tính đang tồn tại - Object.freeze(obj) Ngăn cấm thêm/xóa/thay đổi các thuộc tính. Set
configurable: false, writable: false
cho tất cả các thuộc tính đang tồn tại - Object.isSealed(obj) Return
true
nếu thêm/xóa thuộc tính bị cấm, và tất cả thuộc tính tồn tại cóconfigurable: false
- Object.isFrozen(obj) Return
true
nếu thêm/xóa/sửa thuộc tính bị cấm, và tất cả thuộc tính hiện tại làconfigurable: false, writable: false
Những phương thức này thì hiếm khi được sử dụng trong thực tế
🥇Tham khảo
Bài viết được dịch từ
Kiến thức trong khóa học Next.js này đã giúp mình kiếm hơn 1 tỉ đồng/năm
Phew! Cuối cùng bạn cũng đã đọc xong. Bài viết này có hơi dài một tí vì mình muốn nó đầy đủ nhất có thể 😅
Website bạn đang đọc được viết bằng Next.js TypeScript và tối ưu từng chi tiết nhỏ như SEO, hiệu suất, nội dung để đảm bảo bạn có trải nghiệm tốt nhất.
Với lượt view trung bình là 30k/tháng (dù website rất ít bài viết). Website này đem lại doanh thu 1 năm vừa qua là hơn 1 tỉ đồng
Đó chính là sức mạnh của SEO, sức mạnh của Next.js.
Mình luôn tin rằng kiến thức là chìa khóa giúp chúng ta đi nhanh nhất.
Mình đã dành hơn 6 tháng để phát triển khóa học Next.js Super | Dự án quản lý quán ăn & gọi món bằng QR Code. Trong khóa này các bạn sẽ được học mọi thứ về framework Next.js, các kiến thức từ cơ bản cho đến nâng cao nhất, mục đích của mình là giúp bạn chinh phục mức lương 25 - 30 triêu/tháng
Nếu bạn cảm thấy bài viết này của mình hữu ích, mình nghĩ bạn sẽ thích hợp với phong cách dạy của mình. Không như bài viết này, khóa học là sự kết hợp giữa các bài viết, video, bài tập nhỏ và dự án lớn có thể xin việc được ngay. Học xong mình đảm bảo bạn sẽ lên tay ngay. 💪🏻