對象簡介
- 對象是
js
的基本數(shù)據(jù)類型款咖,對象是一種復(fù)合值何暮,它將很多值聚合在一起,可通過名字訪問這些值铐殃。 - 對象也可看做是屬性的無序集合海洼,每個(gè)屬性都是一個(gè)名/值對,屬性名是字符串富腊,因此我們也可以把對象看成是從字符串到值的映射坏逢。
- 這種基本數(shù)據(jù)結(jié)構(gòu)還有很多叫法,“散列”赘被、“散列表”是整、“字典”、“關(guān)聯(lián)數(shù)組”民假。
-
js
對象還可以從一個(gè)稱為原型的對象繼承屬性(核心特性)浮入。 - 除了字符串、數(shù)字阳欲、
true
舵盈、false
陋率、null
和undefined
之外,js
的值都是對象秽晚。
三類js
對象
-
內(nèi)置對象
(native object)
:數(shù)組瓦糟、函數(shù)、日期赴蝇、正則表達(dá)式 -
宿主對象
(host object)
:js
所運(yùn)行的環(huán)境提供的對象比如:BOM
中的Window
菩浙、DOM
中的document
。 -
自定義對象
(user-defined object)
:代碼創(chuàng)建的對象句伶。
兩類屬性
- 自有屬性:直接在對象中定義的屬性劲蜻。
- 繼承屬性:是在對象的原型對象中定義的屬性。
創(chuàng)建對象
- 對象直接量(創(chuàng)建對象最簡單的方式)
- 關(guān)鍵字
new
-
Object.create()
函數(shù)
1考余、對象直接量
// 沒有任何屬性的對象
var empty = {};
// 復(fù)雜對象
var book = {
x:0, // 正常屬性
"main title":"Js", // 屬性名字有空格先嬉,必須用字符串表示
"sub-title":"js js", // 屬性名字有連字符,必須用字符串表示
"for":"all" // for 是關(guān)鍵字楚堤,必須用引號疫蔓,不建議使用關(guān)鍵字作為屬性名
}
如上:對象直接量是一個(gè)表達(dá)式,這個(gè)表達(dá)式每次運(yùn)算都創(chuàng)建并且初始化一個(gè)新的對象身冬,每次計(jì)算對象直接量的時(shí)候衅胀,也都會(huì)計(jì)算它的每個(gè)屬性的值,所以酥筝,如果在一個(gè)重復(fù)調(diào)用的函數(shù)中的循環(huán)體內(nèi)使用了對象直接量滚躯,它將創(chuàng)建很多新對象,并且每次創(chuàng)建的對象的屬性值也有可能不同嘿歌。
2掸掏、new
一個(gè)對象
new
運(yùn)算符創(chuàng)建并初始化一個(gè)新對象,關(guān)鍵字new
后跟隨一個(gè)函數(shù)調(diào)用搅幅,這里的函數(shù)稱做構(gòu)造函數(shù)(constructor)
阅束,構(gòu)造函數(shù)用以初始化一個(gè)新創(chuàng)建的對象呼胚。
var o = new Object(); // 創(chuàng)建一個(gè)空對象茄唐,和{}一樣
var a = new Array(); // 創(chuàng)建一個(gè)空數(shù)組,和 []一樣
var d = new Date(); // 創(chuàng)建一個(gè)表示當(dāng)前時(shí)間的Date對象
var r = new RegExp('js'); // 創(chuàng)建一個(gè)可以進(jìn)行模式匹配的RegExp對象
3蝇更、Object.create()
Object.create()
是一個(gè)靜態(tài)函數(shù)沪编,而不是提供給某個(gè)對象調(diào)用的方法,使用它的方法只需傳入所需原型對象即可年扩。*·what·? ? ? *
-
Object.create()
只能傳入對象或者null
蚁廓,什么都不傳報(bào)錯(cuò)。
var o1 = Object.create({x:1}); // o1繼承了屬性x
如上代碼所示:o1
繼承了屬性x
厨幻,注意是繼承相嵌,此時(shí)我們打印o1
腿时,輸出{}
,但是打印o1.x
卻能輸出1
饭宾,再看o1.__proto__
輸出{x:1}
批糟,o1
的原型對象是{x:1}
,{x:1}
的原型對象是Object
看铆,這一系列鏈接的原型對象就是所謂的“原型鏈”徽鼎。
- 可以傳入?yún)?shù)
null
來創(chuàng)建一個(gè)沒有原型的新對象,但是通過這種方法創(chuàng)建的對象不會(huì)繼承任何東西弹惦,甚至不包括基本方法否淤,比如toString()
。
var o2 = Object.create(null); // o2不繼承任何屬性和方法棠隐。
- 如果想創(chuàng)建一個(gè)普通的空對象石抡,需要傳入
Object.prototype
。
var o3 = Object.create(Object.prototype); // o3和{}和 new Object() 一樣
繼承
js
對象具有“自有屬性”助泽,也有一些屬性是從原型對象繼承而來的汁雷。
function inherit(p) {
if(p == null){
throw TypeError(); // p 是一個(gè)對象,但是不能是null
}
if(Object.create) {
return Object.create(p); // 如果Object.create()存在就直接使用
}
var t = typeof p;
if(t !== "Object" && t !== "function") {
throw TypeError();
}
function f(){}; // 定義一個(gè)空構(gòu)造函數(shù)
f.prototype = p; // 將其原型屬性設(shè)置為p
return new f(); // 使用f()創(chuàng)建p的繼承對象
}
var o = {}; // o 從 Object.prototype 繼承對象的方法
o.x = 1; // 給o定義一個(gè)屬性x
var p = inherit(o); // p 繼承 o 和 Object.prototype
p.y = 2; // 給p定義一個(gè)屬性y
var q = inherit(p); // q 繼承 p报咳、o侠讯、Object.prototype
q.z = 3; // 給 q 定義一個(gè)屬性 z
var s = q.toString(); // toString 繼承自O(shè)bject.prototype
q.x + q.y; // => 3 x 和 y 分別繼承自 o 和 p
假設(shè)要查詢對象 o
的屬性 x
,如果 o
中不存在 x
暑刃,那么將會(huì)繼續(xù)在 o
原型對重查詢屬性 x
厢漩,如果原型對象中也沒有 x
,但這個(gè)原型對象也有原型岩臣,那么繼續(xù)在這個(gè)原型對象的原型上執(zhí)行查詢溜嗜,直到找到x
或者查找到一個(gè)原型是null
的對象為止,可以看到架谎,對象的原型屬性構(gòu)成一個(gè)“鏈”炸宵,通過這個(gè)“鏈”可以實(shí)現(xiàn)屬性的繼承。
現(xiàn)在假設(shè)給對象o
的屬性x
賦值谷扣,如果o
中已經(jīng)有屬性x
(這個(gè)屬性不是繼承而來的)土全,那么這個(gè)賦值操作只改變這個(gè)已有屬性x
的值,如果o
中不存在屬性x
会涎,那么賦值操作給o
添加一個(gè)新屬性x
裹匙,如果之前o
繼承自屬性x
,那么這個(gè)繼承的屬性就被新創(chuàng)建的同名的屬性覆蓋了末秃。
var unitcircle = {r:1};
var c = inherit(unitcircle);
c.x = 1;
c.y = 1;
c.r = 2;
unitcircle.r; // => 1 原型對象沒有修改
原型
每一個(gè)js
對象(除了null
)都和另一個(gè)對象相關(guān)聯(lián)概页,“另一個(gè)”對象就是我們熟知的原型,每一個(gè)對象都是從原型繼承屬性练慕。
屬性訪問錯(cuò)誤
- 查詢一個(gè)不存在的屬性并不會(huì)報(bào)錯(cuò)惰匙,如果在對象
o
自身的屬性或繼承的屬性中均未找到屬性x
技掏,屬性訪問表達(dá)式o.x
返回undefined
var o = {y:1};
o.x; // => undefined
- 如果對象不存在项鬼,那么查詢這個(gè)不存在的對象的屬性就會(huì)報(bào)錯(cuò)零截,
null
和undefined
值都沒有屬性,因此查詢這些值的屬性戶報(bào)錯(cuò)秃臣。
var o = null;
o.x; // => Uncaught TypeError: Cannot read property 'x' of null
var p = {y:1};
o.x.z; // => Uncaught TypeError: Cannot read property 'z' of undefined
刪除屬性
-
delete
運(yùn)算符可以刪除對象的屬性 -
delete
運(yùn)算符只能刪除自有屬性涧衙,不能刪除繼承屬性(要?jiǎng)h除繼承屬性必須從定義這個(gè)屬性的原型對象上刪除它,而且這會(huì)影響到所有繼承自這個(gè)原型的對象)
檢測屬性
js
對象可以看做屬性的集合奥此,我們經(jīng)常會(huì)檢測集合中成員的所屬關(guān)系弧哎,判定某個(gè)屬性是否存在某個(gè)對象中。
js
提供四種個(gè)方式:
-
in
運(yùn)算符稚虎,會(huì)去查繼承屬性 -
hasOwnPreperty()
撤嫩,只會(huì)查自有屬性,不會(huì)去查繼承的屬性 -
propertyIsEnumerable()
蠢终,只查自有且可枚舉的屬性序攘,不查繼承屬性 - 通過屬性查詢 ,不能區(qū)分值是
undefined
的屬性
1寻拂、in
運(yùn)算符
in
運(yùn)算符的左側(cè)是屬性名(字符串)程奠,右側(cè)是對象,如果對象的自有屬性或繼承屬性中包含這個(gè)屬性則返回true
祭钉。
var o = {x:1};
"x" in o; // => true 自有屬性
"y" in o; // => false 不存在屬性
"toString" in o; //=> true 繼承屬性
2瞄沙、 hasOwnProperty()
hasOwnProperty()
用來檢測給定的名字是否是對象的自有屬性,對于繼承的屬性它將返回false
慌核。
var o = {x:1};
o.hasOwnProperty("x"); // => true 自有屬性
o.hasOwnProperty("y"); // => false 不存在屬性
o.hasOwnProperty("toString"); // => false 距境,繼承屬性
3、propertyIsEnumerable()
propertyIsEnumerable()
是hasOwnProperty()
的增強(qiáng)版垮卓,只有檢測到是自有屬性且這個(gè)屬性可枚舉為true
時(shí)它才返回true
垫桂。
var o = inherit({y:2});
o.x = 1;
o.propertyIsEnumerable("x"); // => true 自有可枚舉屬性
o.propertyIsEnumerable("y"); // => false 繼承屬性
Object.prototype.propertyIsEnumerable("toString"); // => false 不可枚舉屬性
4、使用!==
判定一個(gè)屬性是否是undefined
var o = {x:1,y:undefined};
o.x !== undefined; // => true 自有屬性
o.y !== undefined; // => false 不存在屬性
o.toString !== undefined; // => true 繼承屬性
o.y !== undefined; // => false 屬性存在但是值為undefined
如上代碼:當(dāng)屬性存在粟按,但是值是undefined
诬滩,!==
不能返回希望的結(jié)果。
枚舉屬性
除了檢測對象的屬性是否存在钾怔,我們還會(huì)經(jīng)常的遍歷對象的屬性碱呼。
-
for/in
循環(huán)遍歷 蒙挑,所有可枚舉屬性 -
Object.keys()
宗侦,(ES5
提供)返回由可枚舉的自有屬性的名稱組成的數(shù)組 -
Object.getOwnPropertyNames()
(ES5
提供)只返回自有屬性
1、for/in
for/in
循環(huán)可以在循環(huán)體重遍歷對象中所有可枚舉的屬性(自有和繼承)忆蚀,把屬性名稱賦值給循環(huán)遍歷矾利。
var o = {x:1};
var p = inherit(o);
p.y = 2;
for (var key in p){
console.log(key);
}
// => y 自有屬性
// => x 繼承屬性
// 沒有輸出 toString ?
"toString" in p; // => true 繼承屬性
p.propertyIsEnumerable("toString"); // => false 不可枚舉屬性
如何過濾繼承屬性姑裂?
for (var key in p){
if(!p.hasOwnProperty(key)) continue; // 跳過繼承屬性
console.log(key)
}
// => x 只輸出自有屬性
2、Object.keys()
Object.keys()
枚舉屬性名稱的函數(shù)男旗,它返回一個(gè)數(shù)組舶斧,這個(gè)數(shù)組由對象中可枚舉的自有屬性的名稱組成。
var o = {x:1};
var p = inherit(o);
p.y = 2;
p.z = 3;
Object.keys(p); // => ["y","z"] 未返回繼承屬性x
3察皇、Objcet.getOwnPropertyNames()
只返回對象的自有屬性名稱茴厉,而不僅僅是可枚舉屬性,不可枚舉屬性也會(huì)返回什荣。
var o = {x:1};
var p = inherit(o);
p.y = 2;
p.z = 3;
Object.defineProperty(p,"h",{value:4,enumerable:true}); // 添加可枚舉屬性
Object.defineProperty(p,"w",{value:5,enumerable:false}); // 添加不可枚舉屬性
Object.keys(p); // => ["y","z","h"] 未返回不可枚舉屬性w 和繼承 屬性 x
Object.getOwnPropertyNames(p); // => ["y","z","h","w"] 只是未返回繼承屬性 x
屬性getter和setter
在ES5
中屬性值可以用一個(gè)或兩個(gè)方法代替矾缓,這倆方法就是getter
和setter
。由getter
和setter
定義的屬性稱做“存取器屬性”稻爬。
- 如果屬性同時(shí)具有
getter
和setter
方法嗜闻,那么它是一個(gè)讀/寫屬性。 - 如果它只有一個(gè)
getter
方法桅锄,那么它是一個(gè)只讀屬性琉雳。 - 如果它只有一個(gè)
setter
方法,那么它是一個(gè)只寫屬性友瘤,讀取只寫屬性總是返回undefined
翠肘。
var o = {
x:1,
y:2,
h:undefined,
get product(){
return this.x * this.y;
},
set product(value){
this.x = value;
this.y = value * 2;
},
get sum(){
return this.x + this.y;
},
set z(value){
this.h = value;
}
}
// product 是一個(gè)讀/寫屬性
o.product; // => 2
o.product = 2;
o.product; // => 8
// sum 是一個(gè)只讀屬性
o.sum; // => 3
o.sum = 5;
o.sum; // => 3
// z 是一個(gè)只寫屬性
o.z; // => undefined
o.z = 4;
this.h; // => 4
屬性的特性
除了包含名字和值之外,屬性還包含一些標(biāo)識(shí)它們可寫辫秧、可枚舉锯茄、可配置的特性。ES3
無法設(shè)置這些特性茶没。
- 可以通過這些
API
給原型對象添加方法肌幽,并將它們設(shè)置成不可枚舉,這讓它們看起來更像內(nèi)置方法抓半。 - 可以通過這些
API
給對象定義不能修改或刪除的屬性喂急,借此“鎖定”這個(gè)對象。 -
數(shù)據(jù)屬性的
4
個(gè)特性分別是:
值value
)
可寫性(writable
)
可枚舉性(enumerable
)
可配置性(configurable
) -
存取器屬性 不具有 值 和 可寫性 它們的可寫性由
setter
方法存在與否決定笛求,存取器屬性的4
個(gè)特性是:
讀壤纫啤(get)
寫入(set)
可枚舉
可配置
ES5
定義了一個(gè)名為“屬性描述符”的對象,這個(gè)對象代表那4個(gè)屬性探入。
{
value:數(shù)據(jù)屬性狡孔,表示屬性的值,默認(rèn): undefined
writable: 可寫性蜂嗽,表示能否修改屬性苗膝。默認(rèn)值:true
enumerable:可枚舉性,表示能否通過 for/in 遍歷得到屬性植旧,默認(rèn)true
configurable:可配置性辱揭,如果屬性為false則不可在對屬性描述符進(jìn)行修改离唐,默認(rèn)值為true。
}
通過調(diào)用Object.getOwnPropertyDescriptor()
可以獲得某個(gè)對象特定屬性的屬性描述對象
var o = {x:1};
Object.getOwnPropertyDescriptor(o,"x");
// => {"value":1,"writable":true,"enumerable":true,"configurable":true}
Object.getOwnPropertyDescriptors(o);
// => {"x":{"value":1,"writable":true,"enumerable":true,"configurable":true}}
由上邊代碼可見问窃,這個(gè)方法只能得到自有屬性的描述符亥鬓。
那么如何設(shè)置屬性的特性呢? Object.defineProperty()
- 第一個(gè)參數(shù):要修改的對象
- 第二個(gè)參數(shù):要?jiǎng)?chuàng)建或者修改的屬性名稱
- 第三個(gè)對象:屬性描述符對象
這個(gè)方法要么修改已有屬性要么新建自有屬性域庇,但是不能修改繼承屬性嵌戈。
var o = {x:1};
// 設(shè)置一個(gè)不可枚舉的屬性,其他屬性默認(rèn)true
Object.defineProperty(o,"x",{value:1,enumerable:false});
Object.keys(o); // => []
Object.defineProperties()
如果要同時(shí)修改或者創(chuàng)建多個(gè)屬性:
- 第一個(gè)參數(shù):要修改的對象
- 第二個(gè)參數(shù):一個(gè)映射表听皿,它包含要新建或修改的屬性的名稱以及它們的屬性描述符
var o = {};
// 給對象o添加倆個(gè)屬性咕别,x 和 y 其中 y 不可枚舉不可配置
Object.defineProperties(o,{
x:{"value":1,"writable":true,"enumerable":true,"configurable":true},
y:{"value":2,"writable":true,"enumerable":false,"configurable":false}
});
// y不可配置,如果這個(gè)時(shí)候我們想要修改y的屬性描述符,會(huì)報(bào)出錯(cuò)誤異常
Object.defineProperties(o,{
y:{"value":2,"writable":true,"enumerable":true,"configurable":false}
});
// => Uncaught TypeError: Cannot redefine property: y
對象的三個(gè)屬性
- 原型
- 類
- 可擴(kuò)展性
原型屬性
- 對象的原型屬性是用來繼承屬性的。
- 原型屬性是在實(shí)例對象創(chuàng)建之初就設(shè)置好的仪吧。
-
ES5
中,將對象作為參數(shù)傳入Objcet.getPrototypeOf()
可以查詢它的原型(一個(gè)并不可靠的方法)偿短。 - 要檢測一個(gè)對象是否是另一個(gè)對象的原型,可以使用
isPrototypeOf()
馋没。
var o = {x:1};
var p = Object.create(o);
o.isPrototypeOf(p); // => true p繼承自o
Object.prototype.isPrototypeOf(o); // true o 繼承自O(shè)bject.prototype
類
可擴(kuò)展性
對象的可擴(kuò)展性用以表示是否可以給對象添加新屬性昔逗。
序列化對象
對象序列化是指將對象的狀態(tài)轉(zhuǎn)換為字符串,也可以將字符串還原為對象篷朵。
ES5
提供了內(nèi)置函數(shù)JSON.stringify()
勾怒、JSON.parse()
用來序列化和還原js
對象。
-
NaN
声旺、Infinity
笔链、-Infinity
序列化的結(jié)果是null - 函數(shù)、
RegExp
腮猖、Error
對象和undefined
值不能序列化和還原 -
JSON.stringify()
只能序列化對象可枚舉的自有屬性
JSON.stringify(NaN); // => "null"
JSON.stringify(Infinity); // => "nulll"
JSON.stringify(undefined); // => undefined
instanceof 運(yùn)算符
instanceof
運(yùn)算符希望左操作數(shù)是一個(gè)對象鉴扫,右操作數(shù)標(biāo)識(shí)對象的類,如果左側(cè)對象是右側(cè)類的示例澈缺,則表達(dá)式返回true
坪创,否則返回false
。
js
對象的類是通過初始化它們的構(gòu)造函數(shù)來定義的姐赡,這樣的話instanceof
的右操作數(shù)應(yīng)當(dāng)是一個(gè)函數(shù)
- 所有對象都是
Object
的實(shí)例 - 如果
instanceof
左操作數(shù)不是對象的話莱预,返回false
- 如果右操作數(shù)不是函數(shù),則拋出一個(gè)類型錯(cuò)誤的異常
var d = new Date(); // 通過Date()構(gòu)造函數(shù)來創(chuàng)建一個(gè)新對象
d instanceof Date; // => true d是由Date創(chuàng)建的
d instanceof Object ; // => true 所有的對象都是Object的實(shí)例
d instanceof Number; // => false