目錄
創建對象
Object.create()
原型
原型操作
原型污染
對象屬性
屬性特征
?枚舉屬性
Object.keys()
Object.getOwnPropertyNames()
?Object.getOwnPropertyDescriptor()
Object.definePeoperty()
Object.definePeoperties()
訪問器屬性
getter和setter屬性
屬性訪問錯誤
檢測屬性
新增方法
Object.is
Object.assign
創建對象
Object.create()
使用Object.create()方法創建一個新對象
// obj1繼承了屬性x和y。
let obj1 = Object.create({ x: 1, y: 2 });
//obj2是一個普通的空對象。
let obj2 = Object.create(Object.prototype);
//obj3不繼承任何屬性和方法。
let obj3 = Object.create(null);
Object.create(新對象的原型,新對象的屬性描述符)
const animal = {eat() {console.log("Eating...");},
};
const dog = Object.create(animal, {name: { value: "Doggie" },bark: {value: function () {console.log("Barking...");},},
});
dog.eat();
dog.bark();
//輸出
//Eating...
//Barking...
原型
每一個JavaScript對象都和原型對象相關聯。(每一個對象都從原型繼承屬性。)
所有通過對象字面量創建的對象都具有同一個原型對象。
通過new和構造函數創建的對象的原型就是構造函數的prototype屬性引用的對象。
new Array()對象的原型就是Array.prototype,又繼承Object.prototype。
let obj = { value: 100 };
Object.prototype.flag = "head";
console.log(obj.flag);
let arr = [1, 2, 3, 4];
console.log(arr.flag);
//輸出
//head
//head
原型操作
Object.getPrototypeOf(對象):查詢對象的原型
Object.setPrototypeOf(對象,新的原型對象):為對象重新指定原型對象
obj1.isPrototypeOf(obj2):檢測對象obj2是否是對象obj1的原型(obj1是否在obj2的原型鏈上)
原型污染
修改JavaScript對象的原型鏈,從而影響所有基于該原型的對象行為。
防御措施:
使用Object.create(null)
創建無原型對象;
避免使用__proto__
,改用Object.getPrototypeOf()
和Object.setPrototypeOf()。
對象屬性
數據屬性:對象屬性可以是任意JavaScript值。
屬性特征
每個數據屬性之間還有一些與之相關的值。
- 可寫:是否可以設置屬性值?
- 可枚舉:是否可以通過for/in結構返回該屬性
- 可配置:是否可以刪除或修改該屬性
?枚舉屬性
for/in循環可以在循環體中遍歷對象中所有可枚舉的屬性(包括自身屬性和繼承屬性)
let o = Object.create({ m: 10, n: 20 });
o.x = 1;
o.y = 2;
o.z = 3;
for (let p in o) {console.log(p, o[p]);
}
//輸出
//x 1
//y 2
//z 3
//m 10
//n 20
Object.keys()
返回一個數組。這個數組由對象中可枚舉的自有屬性的名稱組成。
let o = Object.create({ m: 10, n: 20 });
o.x = 1; o.y = 2; o.z = 3;
console.log(Object.keys(o));
//輸出
//[ 'x', 'y', 'z' ]
Object.getOwnPropertyNames()
返回對象的所有自有屬性的名稱
let o = Object.create({ m: 10, n: 20 });
o.x = 1;
o.y = 2;
o.z = 3;
console.log(Object.getOwnPropertyNames(o));
//輸出
//[ 'x', 'y', 'z' ]
?Object.getOwnPropertyDescriptor()
可以獲得某個對象特定屬性的屬性描述符。
console.log(Object.getOwnPropertyDescriptor(circle, "r"));
//{value: 9.549296585513721,writable: true,enumerable: true,configurable: true}
Object.definePeoperty()
傳入要修改的對象、要床架或修改的屬性的名稱以及屬性描述符對象。
- 第一個參數:傳入要修改的對象
- 第二個參數:要創建或修改的屬性的名稱
- 第三個參數:屬性描述符對象
let o = {};
Object.defineProperty(o, "x", {value: 10,writable: true,enumerable: false,configurable: false,
});
console.log(o.x, Object.keys(o));//10 []
value(屬性值),writable(可寫性),enumerable(可枚舉性),configurable(可配置性)
傳入Object.defineProperty()的屬性描述符對象不必包含所有4個特性。
Object.definePeoperties()
同時修改或創建多個屬性。
- 第一個參數:要修改的對象
- 第二個參數:映射表(包含要新建或者修改的屬性名稱,以及屬性的描述)
let p = Object.defineProperties({},{x: { value: 1, writable: true, enumerable: true, configurable: true },y: { value: 1, writable: true, enumerable: true, configurable: true },r: {get: function () {return Math.hypot(this.x, this.y);},enumerable: true,configurable: true,},}
);
console.log(p);//{ x: 1, y: 1, r: [Getter] }
訪問器屬性
屬性可以是一個getter或setter函數。
可寫性是由setter方法是否存在決定的。
存取器屬性的4個特性是讀取(get)、寫入(set)、可枚舉性和可配置性。
getter和setter屬性
getter的返回值就是屬性存取表達式的值。
setter負責“設置”屬性值。
let circle = {r: 10,get round() {return 2 * this.r * Math.PI;},set round(v) {this.r = v / 2 / Math.PI;},get area() {return Math.PI * this.r ** 2;},
};
console.log(circle.round, circle.area);//62.83185307179586 314.1592653589793
circle.round = 60;
console.log(circle.r, circle.area);//9.549296585513721 286.47889756541167let circle1 = Object.create(circle);
circle1.r = 20;
console.log(circle1.round);//125.66370614359172
circle1.round = 500;
console.log(circle1.r, circle1.area);//79.57747154594767 19894.367886486918
和數據屬性一樣,存取器屬性是可以繼承的。
屬性訪問錯誤
查詢一個不存在的屬性并不會報錯,返回undefined。
如果對象不存在,查詢這個不存在的對象 的屬性就會報錯。
let one = {};
// let one = { two: { three: 3 } };
console.log(one.two.three);//報錯
if (one) {
if (one.two) {
if (one.two.three) console.log(one.two.three);
}
}
console.log(one && one.two && one.two.three);
console.log(one?.two?.three); // Null傳導運算符
檢測屬性
檢查屬性的方法:
- in字符串
- hasOwnPreperty()
- properlylsEnumerable()
let o = { x: 1 };
console.log("x" in o);//true
console.log("y" in o);//false
console.log("toString" in o);//true
console.log(o.hasOwnProperty("x"));//true
console.log(o.hasOwnProperty("y"));//false
console.log(o.hasOwnProperty("toString"));//false
console.log(o.propertyIsEnumerable("x"));//true
console.log(o.propertyIsEnumerable("y"));//false
console.log(o.propertyIsEnumerable("toString"));//false
console.log(Object.prototype.propertyIsEnumerable("toString"));//false
Object.prototype 包含一個 toString 方法,所以即使對象 o 自身沒有定義 toString,in 操作符也會沿著原型鏈查找到 Object.prototype.toString。
新增方法
Object.is
比較兩個值是否相等,返回一個布爾值。
console.log(Object.is(1, 2)); // false
console.log(Object.is(1, "1"), 1 == "1", 1 === "1"); // false true false
console.log(Object.is(NaN, NaN), NaN == NaN, NaN === NaN); // false true false
console.log(Object.is(0, -0), 0 == -0, 0 === -0); // false true true
console.log(Object.is(+0, -0)); // false
console.log(Object.is(+0, 0)); // true
Object.assign
將所有可枚舉的自身屬性從一個或多個源對象復制到目標對象,返回目標對象。(對象的深層復制)
const symbolTag = Symbol("is object");
const defaults = {color: "red",size: "10px",[symbolTag]: "configuration",
};
const options = {color: "blue",opacity: 0.5,
};
const merged = Object.assign({}, defaults, options);
console.log(defaults);
//{ color: 'red', size: '10px', [Symbol(is object)]: 'configuration' }
console.log(options);
//{ color: 'blue', opacity: 0.5 }
console.log(merged);
//{ color: 'blue', size: '10px', opacity: 0.5, [Symbol(is object)]: 'configuration' }
Object.assign()方法不會直接遞歸對象,如果對象的屬性值是一個對象,則會直接復制該對象的引用,而不會復制該對象的屬性。
const symbolTag = Symbol("is object");
const defaults = {color: "red",size: "10px",[symbolTag]: "configuration",
};
const options = {color: "blue",opacity: 0.5,
};
const merged = Object.assign({}, defaults, options);
const options2 = {color: "blue",opacity: 0.5,position: { x: 10, y: 20 },
};
const merged2 = Object.assign({}, defaults, options2);
console.log(merged2);
//{
// color: 'blue',
// size: '10px',
// opacity: 0.5,
// position: { x: 10, y: 20 },
// [Symbol(is object)]: 'configuration'
//}
console.log(merged2.position === options2.position);
//true