JavaScript對象屬性分為數據屬性和訪問器屬性。1. 數據屬性包含實際值,并有configurable、enumerable、writable和value特性;2. 訪問器屬性通過getter和setter函數控制讀寫,具有configurable、enumerable、get和set特性。Object.getownpropertydescriptor()可用于獲取屬性特性。定義屬性可用直接賦值或object.defineproperty(),后者可精細控制屬性行為。getter和setter可用于實現計算屬性。for…in循環遍歷自身及原型鏈上的可枚舉屬性,而object.keys()僅返回自身的可枚舉屬性。為防止屬性被修改,可用object.preventextensions()阻止擴展、object.seal()密封對象、或object.freeze()凍結對象,三者保護級別遞增且均不作用于嵌套對象。
JavaScript對象(Object)的屬性是構成對象的核心部分,它們定義了對象的狀態和行為。理解對象屬性對于編寫高效、可維護的JavaScript代碼至關重要。
JavaScript對象屬性的全面介紹:
屬性的類型和特性
屬性從類型上可以分為兩種:數據屬性和訪問器屬性。數據屬性包含一個保存數據值的位置,而訪問器屬性不直接保存數據值,而是通過getter和setter函數來讀寫數據。
每個屬性還具有一些特性,這些特性描述了屬性的行為。這些特性包括:
- [[Configurable]]: 表示屬性是否可以通過delete刪除并重新定義,是否可以修改它的特性,以及是否可以修改為訪問器屬性。默認為true。
- [[Enumerable]]: 表示屬性是否可以通過for-in循環返回。默認為true。
- [[Writable]]: 表示屬性的值是否可以修改。默認為true。
- [[Value]]: 包含屬性實際的值。這是數據屬性特有的。
- [[Get]]: 在讀取屬性時調用的函數。這是訪問器屬性特有的。
- [[Set]]: 在寫入屬性時調用的函數。這是訪問器屬性特有的。
可以通過Object.getOwnPropertyDescriptor()方法來獲取屬性的特性。
let person = { name: "Alice" }; let descriptor = Object.getOwnPropertyDescriptor(person, "name"); console.log(descriptor); // 輸出: // { // value: 'Alice', // writable: true, // enumerable: true, // configurable: true // }
如何定義屬性?直接賦值,還是用Object.defineProperty?
直接賦值是最簡單的方式,但Object.defineProperty提供了更精細的控制。直接賦值創建的屬性,其configurable、enumerable和writable特性都默認為true。而使用Object.defineProperty,可以顯式地設置這些特性。
let person = {}; // 直接賦值 person.age = 30; // 使用Object.defineProperty Object.defineProperty(person, "gender", { value: "female", writable: false, enumerable: true, configurable: false }); console.log(person.age); // 30 console.log(person.gender); // female person.gender = "male"; // 嘗試修改,嚴格模式下會報錯,非嚴格模式下靜默失敗 console.log(person.gender); // female (值未改變) delete person.gender; // 嘗試刪除,無效 console.log(person.gender); // female (屬性仍然存在)
副標題1
如何利用getter和setter實現計算屬性?
Getter和setter允許你定義屬性的讀取和寫入行為。這在需要根據其他屬性計算屬性值,或者需要在設置屬性值時執行一些額外的邏輯時非常有用。
let rectangle = { width: 10, height: 5, get area() { return this.width * this.height; }, set area(newArea) { // 這里可以添加一些驗證邏輯 let newWidth = Math.sqrt(newArea * 2); // 假設保持長寬比不變 this.width = newWidth; this.height = newArea / newWidth; } }; console.log(rectangle.area); // 50 rectangle.area = 100; console.log(rectangle.width); // 14.142135623730951 console.log(rectangle.height); // 7.0710678118654755
副標題2
for…in循環和Object.keys()有什么區別?何時使用哪個?
for…in循環會遍歷對象自身及其原型鏈上所有可枚舉的屬性。而Object.keys()只會返回對象自身的可枚舉屬性的數組。
let animal = { name: "Generic Animal" }; let dog = Object.create(animal); dog.breed = "Labrador"; console.log("for...in loop:"); for (let key in dog) { console.log(key); // 輸出: breed, name } console.log("Object.keys():"); console.log(Object.keys(dog)); // 輸出: [ 'breed' ]
副標題3
如何防止對象屬性被意外修改?Object.freeze()、Object.seal() 和 Object.preventExtensions() 的區別是什么?
JavaScript提供了幾種方法來防止對象屬性被意外修改:
- Object.freeze(): 凍結對象。不能添加、刪除或修改對象的屬性。configurable和writable特性都會變為false。這是一個最高級別的保護。
- Object.seal(): 密封對象。不能添加或刪除對象的屬性,但可以修改現有屬性的值。configurable特性會變為false。
- Object.preventExtensions(): 阻止對象擴展。不能向對象添加新屬性,但可以刪除和修改現有屬性。
let obj = { prop1: "value1", prop2: "value2" }; // Object.preventExtensions() Object.preventExtensions(obj); obj.prop3 = "value3"; // 嘗試添加新屬性,嚴格模式下會報錯,非嚴格模式下靜默失敗 console.log(obj.prop3); // undefined // Object.seal() Object.seal(obj); delete obj.prop1; // 嘗試刪除屬性,嚴格模式下會報錯,非嚴格模式下靜默失敗 obj.prop2 = "new value2"; // 可以修改現有屬性 console.log(obj); // 輸出: { prop1: 'value1', prop2: 'new value2' } // Object.freeze() Object.freeze(obj); obj.prop2 = "another value"; // 嘗試修改屬性,嚴格模式下會報錯,非嚴格模式下靜默失敗 console.log(obj); // 輸出: { prop1: 'value1', prop2: 'new value2' }
理解這些方法及其區別,可以根據不同的安全需求選擇合適的保護級別。需要注意的是,這些方法都是淺層的,只對對象自身的屬性有效,對于屬性值為對象的屬性,仍然可以修改其內部屬性。