你們都知道循環一個對象,能夠用for ..in..
來作,好比:javascript
var myObject = {foo: 'bar'};
for(var name in myObject) {
// 這裏能獲得你想要的屬性名
// 也能夠獲得對應屬性值: myObject[propertyName]
}
複製代碼
這是顯而易見的,可是若是有人在這個對象的原型上添加了一些屬性或方法呢?vue
Object.prototype.baz = 'quux';
for (var name in myObject) {
alert(name); //foo baz
}
複製代碼
myObject
也會跟着改,那麼別人改動原型,對本身的代碼形成很大的影響,結果確定是不能接受的。java
既然對象存在繼承,那麼咱們必須考慮哪些是我本身寫的(私有的),哪些是從原型上繼承的,單純的for ..in..
循環已經不能知足需求 了。數組
hasOwnProperty
就是用來判斷是不是私有屬性,而不是繼承過來的ui
myObject.hasOwnProperty('foo'); // true
myObject.hasOwnProperty('baz'); // false
複製代碼
若是僅僅想要私有的屬性或方法,咱們就能夠這麼寫了spa
for(var name in myObject) {
if(myObject.hasOwnProperty(name)){
alert(name);// 'foo'
}
}
複製代碼
for...in..
循環,中間還要作判斷,是否是很麻煩,不過咱們有專門的方法來解決這些麻煩事。prototype
Object.keys(myObject);//['foo'] 返回一個對象,是一個私有屬性的集合
複製代碼
說到這裏,確定還有一些人會疑惑,既然for...in
能遍歷出對象自身的屬性或方法,也能遍歷出繼承過來的屬性或方法,那麼Object
類上還有不少的屬性和方法的,好比說toString
,這是被myObject
繼承的,可是沒有被for...in
遍歷到啊。code
'toString' in myObject; //true
myObject.toString();//"[object Object]"
複製代碼
這裏引入一個概念「可枚舉」enumerable
,這個概念的最初目的,就是讓某些屬性能夠規避掉for...in
操做,否則全部內部屬性和方法都被遍歷到,就能夠放肆的更改了。cdn
寫到這裏,你應該會明白了,上面提到的toString
都是不可枚舉的。你會不會有這樣的問題?若是我在對象裏面寫了一些屬性,可是不想被遍歷出來,是否是也能夠把這些屬性變成不可枚舉的呢?固然是能夠的了。對象
Object.defineProperty(myObject, 'name', {
value : 'cover',
enumerable: false//不可枚舉
})
Object.prototype.baz = 'quux';
// myObject:{foo: "bar", name: "cover"}
for (var name in myObject) {
alert(name); //foo baz
}
複製代碼
經過這種方法定義出來的屬性或方法就能夠變成不可枚舉。
新的知識點:Object.defineProperty
,顧名思義,就是爲對象定義屬性。 定義:直接在一個對象上定義一個新的屬性,或者是修改已存在的屬性。最終這個方法會返回該對象。
參數
object
必需。 要在其上添加或修改屬性的對象。 這多是一個本機JavaScript
對象(即用戶定義的對象或內置對象)或DOM
對象。propertyname
必需。 一個包含屬性名稱的字符串。descriptor
必需。 屬性描述符。 它能夠針對數據屬性或訪問器屬性。其中descriptor
的參數值是咱們須要重點關注的,該屬性可設置的值有:
value
] 屬性的值,默認爲undefinedwritable
] 該屬性是否可寫,若是設置成 false,則任何對該屬性改寫的操做都無效(但不會報錯),對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true
。Object.defineProperty(myObject, 'age', {
value : 18,
writable:false
});
myObject.age =19;
console.log(myObject.age);//18;
複製代碼
configurable
]若是爲false
,則任未嘗試刪除目標屬性或修改屬性的行爲將被無效化,對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true
。Object.defineProperty(myObject, "name", {
value:"gogo" ,
configurable: false
});
delete myObject.name;
console.log(myObject.name);// 輸出 gogo
myObject.name = "gg";
console.log(myObject.name); // 輸出gogo
複製代碼
enumerable
] 是否能在for-in循環中遍歷出來或在Object.keys中列舉出來。對於像前面例子中直接在對象上定義的屬性,這個屬性該特性默認值爲爲 true。在調用
Object.defineProperty()
方法時,若是不指定,configurable
,enumerable
,writable
特性的默認值都是false
,這跟以前所 說的對於像前面例子中直接在對象上定義的屬性,這個特性默認值爲爲true
。並不衝突,以下代碼所示:
//調用Object.defineProperty()方法時,若是不指定
var someOne = { };
someOne.name = 'coverguo';
console.log(Object.getOwnPropertyDescriptor(someOne, 'name'));
//輸出 Object {value: "coverguo", writable: true, enumerable: true, configurable: true}
//直接在對象上定義的屬性,這個特性默認值爲爲 true
var otherOne = {};
Object.defineProperty(otherOne, "name", {
value:"coverguo"
});
console.log(Object.getOwnPropertyDescriptor(otherOne, 'name'));
//輸出 Object {value: "coverguo", writable: false, enumerable: false, configurable: false}
複製代碼
Object.getOwnPropertyDescriptor
方法會返回某個對象屬性的描述對象(descriptor
)。ES2017 引入了Object.getOwnPropertyDescriptors
方法,返回指定對象全部自身屬性(非繼承屬性)的描述對象。
set
] 修改器,一旦目標對象訪問該屬性,就會調用這個方法, 並返回結果。默認爲undefined
。get
] 獲取器,一旦目標對象設置該屬性,就對調用這個方法。默認爲undefined
其實,在vue
中就用到了 Object.defineProperty
方法,主要用於雙向數據綁定。扯得有點遠了,咱們再回來看,如何判斷對象上的屬性是否可枚舉?
obj.propertyIsEnumerable(prop)
Object.defineProperty(myObject, "name", {
value:"gogo" ,
configurable: false ,
enumerable:false,
});
myObject.age =18
myObject.propertyIsEnumerable('name'); //false
myObject.propertyIsEnumerable('age');//true
複製代碼
另外介紹一個方法:
Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames
返回一個數組,包含對象自身的全部屬性(不含 Symbol 屬性,可是包括不可枚舉屬性)的鍵名。是否是和Object.keys(obj)
有點像呢?
最後,咱們再來總結一下,有四個操做會忽略enumerable
爲false
的屬性。
for...in
循環:只遍歷對象自身的和繼承的可枚舉的屬性。Object.keys()
:返回對象自身的全部可枚舉的屬性的鍵名。JSON.stringify()
:只串行化對象自身的可枚舉的屬性。Object.assign()
: 忽略enumerable
爲false
的屬性,只拷貝對象自身的可枚舉的屬性。最後加一個題外的問題:
關於instanceof 的大概實現原理:
function myInstanceof(left,right){
let rightVal = right.prototype;
let letfVal = left.__proto__;
while(true){
if(leftVal === null)return false;
else if(letfVal === rightVal) return true;
letfVal = letfVal.__proto__;
}
}
複製代碼
在加上一張著名的圖:
這張圖不太好理解的地方在於 Object.__proto__
的值是 Function.prototype
,暫時先記住吧。