1. 基礎
1.1 基本類型
曾經(jīng)聽說Javascript萬物皆對象,沒對這句話有過更多的考慮,總覺得是對的,但是實際上并不一定妥當,對象類型是Javascript的6個基本類型之一。Javascript包含六種基本類型,分別是number
, string
, boolean
, null
, undefined
, object
其中,雖然typeof null === 'object'
,但是null
確實被認為是獨立的基本類型,之所以會有這樣的結果,是因為object
是數(shù)據(jù)根據(jù)二進制轉后前三位來判斷的,前三位是0的會被認為是object
類型,因為null
全部都是0,所以也被認為是object
了
1.2 內置對象
除開基本類型,在object
基礎上,存在9個常用的內置對象Number
, String
, Boolean
, Object
, Function
, Array
, Date
, RegExp
, Error
為什么會存在這些內置對象?因為基本類型是沒有提供任何的方法的,我們在Javascript的字面量或者對象調用的屬性/方法,實際上是來自于內置對象。也就是說對于基本類型string
,并沒有length
,我們在獲取length
屬性的時候,基本類型會轉換為對應的內置對象而使用內置對象的方法獲取相關內容
2. 對象創(chuàng)建
Javascript對象創(chuàng)建通常有兩種方法:字面量和構造方法
通過字面量的方式進行創(chuàng)建是最常見的。
var o = {} // 字面量,使用大括號進行創(chuàng)建
var o = new Object() // 構造方法,使用new的方式創(chuàng)建
var o = Object.create({}) // 也可以使用Object提供的方法也可以創(chuàng)建一個對象
3. 屬性增加
3.1 計算屬性
以字面量的方式為例,我們要給對象增加屬性,一般在創(chuàng)建對象的時候同時進行屬性賦值,在ES6引入了計算屬性,使得屬性名可以通過計算的方式設置,計算屬性使用[]
,將需要計算的內容放到[]
內部。
var pname = 'abc';
var o = {
x: 1,
['a'+'b']: 2, // 計算屬性
[pname]: 3 // 計算屬性,使用變量作為屬性名
}
但是需要注意的一點,對于object而言,所有的屬性名在設置的時候都會調用toString()的方法
var po = {};
var o = {
[po]: 1 // po會調用toString方法
}
// 上面的對象實際上會變?yōu)?var o = {
'[object Object]': 1
}
3.2 屬性描述符
Javascript對象屬性有5個屬性描述符,用于對屬性增加限制,分別是:writable
, enumerable
, configurable
, get
, set
我們可以使用Object.defineProperty()
的給對象定義屬性的時候增加屬性描述
var o = {};
Object.defineProperty(o, 'x', {
value: 1, // 設置屬性的值, 默認undefined
writable: false, // 設置屬性是否可寫,默認false
enumerable: false, //設置屬性是否可枚舉,默認false
configurable: false, // 屬性描述是否可修改,默認false
get: function(){return xxx}, // 屬性get方法,也可以寫作get() {}, 默認undefined
set: function(val){}, // 屬性set方法,也可以寫作set(){}, 默認undefined
})
不過要注意,這里的value
和writable
不能和get()
,set()
同時進行設置
對于屬性描述符configurable
,如果設置為false,那么則不能再對對象的屬性描述進行修改(writable屬性從true
->false
是例外),否則將拋出異常
var o = {};
Object.defineProperty(o, 'x', {
value: 1,
configurable: false
})
Object.defineProperty(o, 'x', {
value: 1,
configurable: true
}) // 拋出異常Uncaught TypeError: Cannot redefine property
3.3 屬性不變性
由于屬性描述符可以對屬性增加限制,所以可以利用這些限制來創(chuàng)建一些特定要求的屬性,使得屬性不能進行修改和重新配置,主要是用Object對象所提供的方法,來對現(xiàn)有對象中的屬性進行處理
3.3.1 阻止對象擴展Object.preventExtensions()
使用該方法可以阻止當前的對象擴展新的屬性
var o = {};
Object.preventExtensions(o) // 阻止對象增加新的屬性
o.a = 1; // 嚴格模式下會拋出異常
console.log(o.a) // undefined
3.3.2 密封Object.seal()
使用該方法,會在Object.preventExtensions()
的基礎上,將對象當前所有屬性的configurable
設置為false
var o = {a: 1};
Object.seal(o); //密封
o.b = 1; //嚴格模式下拋出異常
console.log(o.b) //undefined
Object.defineProperty(o, 'a', {
configurable: true
}) //拋出異常
3.3.3 凍結Object.freeze()
使用該方法,會在Object.seal()
基礎上,將當前對象所有屬性的writable
設置為false
var o = {a: 1};
Object.freeze(o); //凍結
o.a = 2; //嚴格模式下拋出異常
console.log(o.a); // 1
o.b = 1; //嚴格模式下拋出異常
console.log(o.b) //undefined
Object.defineProperty(o, 'a', {
configurable: true
}) //拋出異常
3.4 數(shù)組對象中的屬性
數(shù)組本身是內置對象,所以可以增加屬性,當屬性名為數(shù)字或者可以轉換為數(shù)字時,將對數(shù)組內容進行修改,屬性名為其他內容的時候并不會擴展數(shù)組對象長度
var o = [1, 2, 3];
o[3] = 4;
console.log(o.length); // 4
o['a'] = 4;
console.log(o.length); // 4
3.5 __proto__
屬性
對象中__proto__
屬性是一個特殊屬性名,進行設置時如果屬性值是一個object
或者null
,則可以改變該屬性的結果,否則不能修改該屬性。
var o = {__proto__: 1}
console.log(o.__proto__); // 并不等于1
var o = {__proto__: null}
console.log(o.__proto__); // undefined
3.6 方法屬性
對象中的方法也是一種屬性,只是和普通屬性設置時存在一定的區(qū)別。從某些方面上來說,方法并不屬于某個對象,因為如果方法中使用this
關鍵字,這個this
并不一定就一定指代該對象。
var o = {
foo: function(){},
bar() {}, // ES6新增的聲明方式
['a'+'b']() {} // 使用計算屬性進行聲明
}
3.7 generator和async方法
ES6中新增的generator和async方法,也可以在對象方法屬性中進行設置
var o = {
// generator方法創(chuàng)建方式1 :*在方法名之前
*g() {},
// generator方法創(chuàng)建方式2: *在function之后
ge: function*() {},
// async方法創(chuàng)建方式1: async關鍵字在方法名前
async as() {},
// async方法創(chuàng)建方式2: async關鍵字在function之前
asy: async function() {}
};
其中關于generator和async方法的內容可以參考MDN相關說明
3.8 get和set方法
在屬性設置的時候可以使用get
和set
方法替代直接對屬性進行賦值,使用get
和set
方法替代以后,則可以在數(shù)據(jù)設置和獲取時添加邏輯,進行監(jiān)聽了
var o = {
get x() {return this._x_},
set x(val) { this._x_ = val }
};
o.x = 1;
console.log(o.x); // 1
4. 屬性獲取
對象屬性獲取有兩種常見的方式:使用.
和使用[]
4.1 屬性訪問(使用.
)
如果需要獲取的屬性名不是數(shù)字,不包含空格,那么我們通常都是使用.
的方式來獲取,但是對于如果屬性名為數(shù)字或者包含空格,則沒辦法通過這樣的方式獲取
var o = {a: 1, 1: 'a'};
console.log(o.a); // 1
console.log(o.1); // error
4.2 鍵訪問(使用[]
)
因為使用.
獲取對象屬性的時候存在限制,所以在不能使用.
的場合,我們可以使用[]
來獲取,但是也需要注意,對于使用[]
來獲取的時候,[]
中的內容會調用toString()后進行屬性名的匹配
var pname = {};
var pname2 = {a: 1};
var o = {a: 1, 1: 'a'};
console.log(o['a']); // 1
console.log(o[1]===o['1']); // true
o[pname] = 2; // 會被轉換為o['[object Object]'] = 2;
console.log(o[pname2]); // 2,因為會被轉換為 o['[object Object]']
5. 循環(huán)
5.1 in和for in
使用in關鍵字,可以判斷一個屬性是否存在對象中,但是這里會遍歷對象的原型鏈去查找該屬性
function F() { this.a = 1};
function G() { this.b = 1};
G.prototype = new F;
var o = new G;
console.log('a' in o); // true
console.log('b' in o); // true
使用for in循環(huán),可以循環(huán)對象中所有的enumerable:true
屬性,同樣遍歷時會查找該對象原型鏈上的屬性
function F() { this.a = 1};
function G() { this.b = 1};
G.prototype = new F;
var o = new G;
Object.defineProperty(o, 'c', {
value: 2,
enumerable: true // enumerable默認為false
})
for(let key in o){
console.log(key); // b c a
}
為了判斷屬性是否只在該對象中,可以使用Object.hasOwnProperty()
的方法來處理結果
function F() { this.a = 1};
function G() { this.b = 1};
G.prototype = new F;
var o = new G;
for(let key in o){
if (o.hasOwnProperty(key)) {
console.log(key); // b
}
}
除此之外,使用Object.keys()
和Object.getOwnpropertyNames()
可以獲取對象的屬性名數(shù)組,只是這兩個方法都只能獲取當前對象的屬性,不能獲取原型鏈上的屬性名
function F() { this.a = 1};
function G() { this.b = 1};
G.prototype = new F;
var o = new G;
console.log(Object.keys(o)); // ['b']
console.log(Object.getOwnPropertyNames(o)); // ['b']
5.2 for of
ES6中增加了for of方法來對對象進行循環(huán),對象想要實現(xiàn)for of循環(huán),需要定義[Symbol.iterator]
屬性,通過返回一個next
方法,next
方法返回一個{value:value, done: boolean}
的對象,來定義對象的遍歷邏輯
var o = {
[Symbol.iterator]: function() {
var self = this;
var keys = Object.keys(this);
var idx = 0;
return {
next() {
return {value: self[keys[idx]], done: idx++ >= keys.length } //當done返回true的時候,循環(huán)停止
}
}
}
}
o.a = 1;
o.b = 2;
for(let val of o) {
console.log(val); // 1 2
}
6. 其他
6.1 對象復制
對象復制是一個比較復雜的內容,如果想要實現(xiàn)淺復制可以使用ES6新增加的Object.assign()
的方法來進行,但是深復制因為需要考慮的因素很多(例如如果對象出現(xiàn)自引用這種復制該如何進行?),所以對于深復制并沒有很好的手段,但是對于一般情況來說,可以使用JSON.parse(JSON.stringfy(obj))
的方式來實現(xiàn)一個深復制。
6.2 新特性
對象增加了一些新特性
var o1 = {a: 1};
var o2 = {b: 2};
var o = {o1, o2} // 相當于 var o = {o1: o1 , o2: o2};
var separateO = {...o1, ...o2} // 分離屬性,結果為var separateO = {a: 1, b: 2},但目前支持瀏覽器有限
7. 參考
《你不知道的Javascript(上篇)》
MDN Object initializer
MDN Property accessors
MDN Method definitions
MDN getter
MDN setter
MDN function*