第三章 屬性高階
一、屬性描述符
1.獲取對應屬性的描述符
屬性描述符:屬性的特徵;也叫元屬性;修飾屬性的屬性var obj={ name:"wql", age:38, wife:"lss" } var res=Object.getOwnPropertyDescriptor(obj,"wife"); console.log(res)
Object.getOwnPropertyDescriptor(obj,"name"); 第一個引數:對應的物件 第二個引數:對應物件的屬性
//res是一個物件
{
}
2.為物件新增或修改屬性
var obj={ name:"snw" }; Object.defineProperty(obj,"age",{ value:18, writable:true, configurable:true, emumerable:true }); console.log(obj); //Object {name: "snw", age: 18} console.log(obj.age);//18 屬性描述符的預設值都是false
3.writable(可寫的)
writable決定是否可以修改屬性的值
當writable為false時,對應的屬性的值是無法修改的。
// writable為false 時 configurable一般也為false
在預設情況下:
繼續修改的話會靜默失敗
在嚴格模式下:
會報錯
var obj={
name:"Lebron",
age:33,
team:"湖人"
}
//obj.team="騎士"; 位置在這兒能被修改
Object.defineProperty(obj,"team",{
writable:false
})
obj.team="熱火";
console.log(obj);
4.configurable(可配置的)
configurable來決定屬性是否可以配置var obj={ name:"zyy", age:20, hobby:"eat" } Object.defineProperty(obj,"hobby",{ //value:"play", writable:true, configurable:false, enumerable:false })
可配置:屬效能否重新定義 屬效能否刪除 當configurable為false時,對應的屬性是無法重新定義的,無法刪除的。但該物件是還可以繼續新增屬性的。 預設情況下
進行重新定義會報錯
進行刪除會靜默失敗 注意一個小小的例外: 當configurable為false時,writable可以進行重新定義,但只滿足由writable:true ==> writable:false
delete obj.hobby; console.log(obj);
5.enumerable
//enumerable可列舉:控制了屬性的可列舉許可權 //可列舉:能否出現在物件的for in 迴圈中 var obj={ name:"zyy", age:26, behavior:"play basketball" } Object.defineProperty(obj,"behavior2",{ value:"eat", enumerable:false }) for(var item in obj){ console.log(item); }
6.嚴格模式
<!--
嚴格模式的語法比較嚴謹
沒有通過var定義的變數 在嚴格模式底下就會報錯
this的指向
普通呼叫時 如果是嚴格模式 那this指向undefined
嚴格模式的管理範圍:作用域
在屬性描述符writable為flase的情況下 對屬性值進行修改 就會報錯不會靜默失敗
在屬性描述符configurable為flase的情況下 對屬性進行刪除 就會報錯不會靜默失敗
--> <script> function test() { "use strict" inner(); function inner() { console.log(this) } } (function () { "use strict" test() })()
7.屬性的指定方式
/*屬性的指定方式: 1. 字面量初始化的時候直接通過 鍵值對的形式指定 屬性描述符的預設值基本都是true 2. 通過物件點的形式來指定 屬性描述符的預設值基本都是true 3. 原始的指定形式的方式 ` 屬性描述符的預設值基本都是false value:undefined 4. var a = "a"; 給window新增的屬性 configurable: false enumerable: true writable: true 5. b="b";給window新增的屬性 屬性描述符的預設值基本都是true */ var a = "a"; b="b"; console.log(Object.getOwnPropertyDescriptor(window,"a")) console.log(Object.getOwnPropertyDescriptor(window,"b")) delete a; delete b; console.log(a); console.log(b);
8.存在性檢查
var obj={ a:undefined, wife:{ name:"xxx" } }; console.log(obj.a); //in關鍵字 會影響物件的直接屬性 也訪問原型鏈 可是不訪問物件的深層屬性 console.log("a" in obj) console.log("b" in obj) console.log("toString" in obj) console.log("name" in obj,"------") //在js中所有的方法都是 淺不變形 的;只會影響物件的直接屬性 不訪問原型鏈 也不訪問物件的深層屬性 console.log(obj.hasOwnProperty("a")); console.log(obj.hasOwnProperty("b")); console.log(obj.hasOwnProperty("toString")); console.log(obj.hasOwnProperty("name")); var res = Object.getOwnPropertyNames(obj); console.log(res);
9.訪問描述符(get&&set)
// 屬性描述符 : 屬性的特徵 屬性的屬性 // 資料描述符 : 具有writable 和 value屬性描述符 的屬性! // 訪問描述符 : 具有set 和 get屬性描述符 的屬性!
var obj={
get age(){
return obj.__age__;
},
set age(val){
if (val > 150){
val =150;
}else if(val < 0){
val =0;
}
obj.__age__=val;
}
}
obj.age=33;
console.log(obj.age);
二、物件不變性
1.物件常量屬性
將屬性的writable和configurable設定為false
var MathCopy = {};
Object.defineProperty(MathCopy,"PI",{
value:3.141592654,
writable:false,
configurable:false,
enumerable:true
})
MathCopy.PI=1;
console.log(MathCopy.PI)
2.禁止物件擴充套件
由於屬性描述符是對屬性的管理
所以想禁止物件擴充套件不能使用屬性描述符來控制,而是需要呼叫Object.preventExtensions(obj);
引數:要求禁止擴充套件的物件
預設情況下
為物件新增新的屬性會靜默失敗
嚴格模式底下
報錯
注意:禁止物件擴充套件只是禁止了物件去擴充套件屬性,
而之前的屬性是可以進行重新定義或者刪除的,
屬性值也是可以修改的
3.密封物件
在禁止物件擴充套件(Object.preventExtensions(obj);的基礎上把現有屬性的configurable都調整為false
呼叫Object.seal(obj)密封一個物件
密封之後禁止了物件去擴充套件屬性,原有的屬性不可以進行重新定義或者刪除,但屬性值是可以修改的
var obj = {
a:"a",
b:'b'
};
Object.seal(obj);
delete obj.a;
obj.c="c";
obj.a="aa";
console.log(obj);
輸出:{a: "aa", b: "b"} 沒有被刪除,也沒有新增c
4.凍結物件(淺)
在密封物件(Object.seal(obj))的基礎上把現有屬性的writable都調整為false
呼叫Object.freeze(obj)密封一個物件
凍結之後禁止了物件去擴充套件屬性,原有的屬性不可以進行重新定義或者刪除,屬性值不可以進行修改
var obj = { a:"a", b:'b' }; Object.freeze(obj); delete obj.a; obj.c="c"; obj.a="aa"; console.log(obj);
5.凍結物件(深)
<script> var obj = { name: "zyy", age: 20, hobby: { No1: "playball", No2: "eat", No3: "sleep" } } function deep(obj) { var keys = Object.getOwnPropertyNames(obj); keys.forEach(function (key) { var val = obj[key] if (Object.prototype.toString.call(val) === "[object Object]") { deep(val) } }); return Object.freeze(obj) } deep(obj); obj.hobby.No1="watch"; console.log(obj) </script>
三、屬性查詢和設定
1.基本規則
/* 資料描述符 configurable: true enumerable: true writable: true */ // 查詢: 先在物件的直接屬性中找 找到了就返回 找不到上原型鏈 整體原型鏈都沒有 返回 undefined // 設定: 隻影響物件的直接屬性
2.完整的規則(查詢)
// [[Get]]:代表的屬性查詢的演算法 // [[Get]]: // 1.在物件中查詢是否具有相同名稱的屬性,如果找到,就會返回這個屬性的值。 // 2.如果沒有找到,則遍歷原型鏈 // 3.無論如何都沒找到,返回undefined // 4.訪問描述符具體看get方法的邏輯
3.完整的規則(設定)
/* [[Put]]:代表的屬性設定的演算法 obj.a="a"; [[put]]: 1. 如果屬性直接存在於物件中 不在原型鏈上 找到直接存在於物件中的屬性 -資料描述符(沒有setter/getter) 直接修改物件中的屬性(注意writbale的值) -訪問描述符 直接呼叫set方法 4. 如果屬性直接存在於物件中 也在原型鏈上 找到直接存在於物件中的屬性 -資料描述符(沒有setter/getter) 直接修改物件中的屬性(注意writbale的值) -訪問描述符 直接呼叫set方法 2. 如果屬性不直接存在於物件中也不在原型鏈上 在物件的直接屬性中新增一個屬性(資料描述符) value:"a" writable:true configurable:true enumerable:true 3. 如果屬性不直接存在於物件中 在原型鏈上 ①.該屬性是資料描述符(沒有setter/getter) -writbale為true 直接在物件中新增一個屬性,我們稱之為遮蔽屬性 (√)(√)(√)(√)-writbale為false 報錯,不會生成遮蔽屬性 (√)(√)(√)(√) ②.該屬性是訪問描述符 呼叫set,不會生成遮蔽屬性 */ // Object.prototype.a="a"; Object.defineProperty(Object.prototype,"a",{ set:function(val){ } }) var obj={}; obj.a="aaaa"; console.log(obj,Object.prototype) </script>
object
靜態方法
Object.create()
Object.defineProperty()
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyNames()
Object.preventExtensions()
Object.seal()
Object.freeze()
Object.keys()
例項方法
Object.prototype.toString()
Object.prototype.valueOf()
Object.prototype.propertyIsEnumerable()