JavaScript對象

對象是一種復(fù)合值:它將很多值(原始值或其他對象)聚合在一起窄做,可通過名字訪問這些值。
對象也可看做是屬性的無序集合河泳,每個屬性都是一個名/值對伶授。

除了包含屬性之外断序,每個對象還擁有3個特性(object attribute):

  • 對象的原型(prototype):指向另外一個原型對象,本對象的屬性繼承自它的原型對象糜烹。
  • 對象的類(class):一個標(biāo)識對象類型的字符串违诗。
  • 對象的擴展標(biāo)記(extensible flag):指明是否可以向該對象添加新屬性。

JavaScript中包含了3類對象和2類屬性:

  • 內(nèi)置對象(native object):由ECMAScript規(guī)范定義的對象或類疮蹦。如诸迟,Array, function, Date, RegExp等。
  • 宿主對象(host object): 由JavaScript解釋器所嵌入的宿主環(huán)境(如Wbd瀏覽器)定義的愕乎。如阵苇,表示網(wǎng)頁結(jié)構(gòu)的HTMLElement對象。
  • 自定義對象(user-defined object):由運行中的JavaScript代碼創(chuàng)建的對象感论。
  • 繼承屬性(inherited property):在對象的原型中定義的屬性绅项。
  • 自有屬性(own property):直接在對象中定義的屬性。

對象的創(chuàng)建

直接量創(chuàng)建對象

對象直接量是同若干名/值對組成的映射表比肄,名/值對中間用冒號分隔快耿,名/值對之間用逗號分隔,整個映射表用花括號括起來芳绩。

var empty = {};                             // 沒有任何屬性的對象
var point  = { x:0, y:0 };                  // 包含2個屬性的對象 
var point2 = { x:point.x, y:point.y+1 }     // 使用對象的值來賦值屬性的值

var book   = {
    "main title":"JavaScript",              // 屬性名字里有空格掀亥,必須用引號
    "sub-title":"The Definitive Guide",     // 屬性名字里有連字符,必須用引號
    "for":"all audiences",                  // "for"是保留字示括,必須使用引號
    
    author:{                                // 對象屬性   
        firstname:"David",
        surname:"Flanagan"
    }
}

對象直接量是一個表達(dá)式,這個表達(dá)式的每次運算都創(chuàng)建并初始化一個新的對象痢畜。

通過new創(chuàng)建對象

new運算符創(chuàng)建并初始化一個新對象垛膝,關(guān)鍵字new后面跟隨一個構(gòu)造函數(shù)(constructor)調(diào)用。

var o = new Object();       // 創(chuàng)建一個空對象丁稀,和{}一樣(只包含Object.prototype)
var a = new Array();        // 創(chuàng)建一個空數(shù)組吼拥,和[]一樣
var d = new Date();         // 創(chuàng)建一個表示當(dāng)前時間的Date對象
var r = new RegExp("js");   // 創(chuàng)建一個可以模式匹配的RegExp對象

通過Object.creat()創(chuàng)建對象

Object.creat()是一個靜態(tài)函數(shù),其中第一個參數(shù)是待創(chuàng)建對象的原型线衫,第二個參數(shù)可選凿可,用以對對象的屬性進(jìn)行進(jìn)一步描述。

var o1 = Object.create(Object.property);    // 創(chuàng)建一個空對象,同{}枯跑、new Object()一樣

var o2 = Object.create({x:1, y:2});         // 創(chuàng)建一個新對象惨驶,并繼承了屬性x和y

var o3 = Object.creat(null);                // 創(chuàng)建一個對象,但不繼承任何屬性及方法

可以通過任意原型創(chuàng)建新對象敛助,如:

// inherit()返回一個繼承自原型對象p屬性的新對象
function inherit(p) {

    // p是一個對象粗卜,但不能是null
    if(p == null) throw TypeError();    
    
    // 如果Object.create()存在,則直接使用它來創(chuàng)建對象
    if(Object.create)                   
        return Object.create(p)
    
    var t = typeof p;
    if(t !== "object" && t !== "function") throw TypeError();
    
    // 通過prototype屬性創(chuàng)建對象
    function f() {}
    f.prototype = p;
    return new f();
}

inherit()并不能代替Object.create()纳击,它不能傳入null原型來創(chuàng)建對象续扔,也不能接收第二個參數(shù)。

屬性相關(guān)操作

屬性繼承

通過繼承創(chuàng)建的新對象焕数,對新對象的操作并不會影響到原型對象纱昧。

var a = { r:1 };
var b = inherit(a);
b.x = 1; b.y = 1;   // 新對象增加2個屬性
b.r = 2;            // 修改新對象的繼承屬性
a.r;                // => 1,原型對象的屬性不會受影響

假設(shè)要查詢對象o的屬性x堡赔,如果o中不存在x识脆,那么將會繼續(xù)在o的原型對象中查詢,直接找到x或者找到一個原型是null的對象為止加匈。<br/

屬性訪問錯誤

  • 查詢一個不存在的屬性并不會報錯存璃。
book.subtitle;          // => undefined:屬性不存在,但不會報錯
  • 但是雕拼,如果對象不存在纵东,那么試圖查詢這個不存在對象的屬性就會報錯。
book.subtitle.length;   // undefined沒有l(wèi)ength屬性啥寇,拋出一個類型錯誤異常
  • 有一此屬性是只讀的偎球,不能重新賦值,但是設(shè)置屬性的失敗操作不會報錯辑甜。
Object.prototype = 0;   // => Object原型是只讀的衰絮,賦值失敗,但不會報錯

下面提供兩種避免出錯的方法:

// 一種冗余但很易懂的方法
var len = undefined;
if(book) {
    if(book.subtitle)
        len = book.subtitle.length;
}

// 一種更簡練的方法磷醋,len被賦值為undefined或length
var len = book && book.subtitle && book.subtitle.length;

刪除屬性

  • delete只是斷開屬性和宿主對象的聯(lián)系猫牡,而不會去操作屬性中的屬性
a = { p:{x:1} };
b = a.p;
delete a.p;

b.x;            // => 1,已經(jīng)刪除的屬性引用(b)依然存在
  • delete 只能刪除自有屬性邓线,不能刪除繼承屬性(要刪除繼承屬性必須從定義這個屬性的原型對象上刪除它淌友,而且這會影響所有繼承的對象)
  • delete 不能刪除那么可配置性為false的屬性
var x = 1;          // 使用var定義的全局變量
delete this.x;      // 不能刪除,可配置性為false

this.y = 1;         // 不使用var定義的全局變量
delete y;           // 可刪除骇陈,可配置性為true

檢測屬性

可通過in運算符震庭、hasOwnProperty()和propertyIsEnumerable()來判斷某個屬性是否存在于某個對象中。
其中propertyIsEnumerable()是hasOwnProperty()的增強版你雌,保有檢測到是自有屬性并且這個屬性是可枚舉時才返回true器联。

var o = { x: 1 };
"x" in o;                       // true,x是o的屬性
"y" in o;                       // false,y不是o的屬性
"toString" in o;                // true拨拓,【繼承屬性】(方法)

o.hasOwnProperty("x");          // true肴颊,x是【自有屬性】
o.hasOwnProperty("y");          // false
o.hasOwnProperty("toString");   // false,toString是【繼承屬性】

除了使用in運算符之外千元,還有更簡便的方法(!== undefined):

var o = { x: 1 };
o.x !== undefined;          // true
o.y !== undefined;          // false
o.toStirng !== undefined;   // true

然而有一種場景這種方法是不可行的:屬性值被顯式地定義為undefined苫昌。

var o = { x: undefined };
o.x !== undefined;          // false,屬性實際上是存在的P液!K钌怼!

注:還有一種情況是物独,這個屬性只有setter方法袜硫,而沒有g(shù)etter方法。

枚舉屬性

for/in循環(huán)可遍歷對象中所有可枚舉的屬性(包括自有屬性和繼承屬性)挡篓,對象的內(nèi)置方法是不可枚舉的婉陷,但在代碼中給對象添加的屬性是可枚舉的。

// 把p中的可枚舉屬性復(fù)制到o中官研,并返回o
// 如果o和p有同名屬性秽澳,則會覆蓋o中的屬性
// 這個函數(shù)不處理getter和setter以及復(fù)制屬性
function extend(o, p) {
    // 遍歷p中所有可枚舉屬性
    for(prop in p) {
        o[prop] = p[prop];
    }
    
    return o;
}

存取器屬性(getter & setter)

在ECMAScript 5中,屬性值可以用一個或兩個方法替代戏羽,這兩個方法就是getter和setter担神。

  • 和數(shù)據(jù)屬性不同,存取器屬性不具有可寫性始花。如果屬性同時具有g(shù)etter和setter方法角钩,那么它是一個讀/寫屬性郁副;如果只有g(shù)etter方法,則是一個只讀屬性嘲叔;如果只有setter方法恭朗,則是一個只寫屬性怔锌,讀取時返回undefined氯材。
  • 和數(shù)據(jù)屬性一樣兆沙,存取器屬性是可以繼承的

定義存取器屬性(使用直接量語法)的語法如下:

var p = {
    // 普通的數(shù)據(jù)屬性
    x: 1.0,
    y: 1.0,
    
    // 存取器屬性
    get r() { return Match.sqrt(this.x*this.x + this.y*this.y); } ,        // 不需要function聲明男韧,并且在最后添加“,”
    set r(newvalue) {
        this.x = newvalue;
        this.y = newvalue;
    } ,     
    
    get theta() { return Match.atan2(this.y, this.x); }
}
代碼中定義了可讀寫屬性r和只讀屬性theta朴摊。

屬性的特性

除了包含名字和值之外,屬性還包含可寫煌抒、可枚舉可配置的特性仍劈。

屬性類型 屬性特性
數(shù)據(jù)屬性 值(value)厕倍、可寫性(writable)寡壮、可枚舉性(enumerable)、可配置性(configurable)。
存取器屬性 存取(get)况既、寫入(set)这溅、可枚舉性和可配置性。

可通過Object.getOwnPropertyDescriptor()獲取屬性描述符:

// 返回{value: 1, writable:true, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor({x:1}, "x");

var random = {
    get octet() { return 1; }
}
// 返回{get:/*func*/, set:undefined, enumerable:true, configurable:true}
Object.getOwnPropertyDescriptor(random, "octet");

// 對于繼承屬性和不存在的屬性棒仍,返回undefined
Object.getOwnPropertyDescriptor({}, "x");           // undefined悲靴,沒有這個屬性
Object.getOwnPropertyDescriptor({}, "toStirng");    // undefined,繼承屬性

Object.defineProperty()用于設(shè)置屬性的特性:

var o = {}; // 創(chuàng)建一個空對象
// 添加一個屬性x莫其,并賦值為1
Object.defineProperty(o, "x", { value: 1,
                                writable: false,
                                enumerable: false,
                                configurable: true } );
                                      
o.x = 2;    // 操作失敗癞尚,屬性不可寫

// 可通過重新設(shè)置屬性特性來改變屬性的值
Object.defineProperty(o, "x", {value: 2});
o.x;        // => 2

// 將數(shù)據(jù)屬性修改存取器屬性
Object.defineProperty(o, "x", { get: function() { return 3; });
o.x;        // => 3
前提 特性
對象不可擴展 可以編輯已有屬性,但不能添加新屬性
屬性不可配置 不能修改它的可配置性和可枚舉性
存取器屬性不可配置 不能修改其getter和setter方法乱陡,也不能將它轉(zhuǎn)換為數(shù)據(jù)屬性
數(shù)據(jù)屬性不可配置 則不能將它轉(zhuǎn)換為存取器屬性浇揩;不能修改它的可寫性false=>true,但可以修改true=>false
數(shù)據(jù)屬性不可配置且不可寫 不能修改屬性的值
數(shù)據(jù)屬性可配置不可寫 可以修改屬性的值(實際上先修改屬性為可寫憨颠,然后修改值)

一個更完善的屬性擴展屬性函數(shù)extend():

Object.defineProperty(Object.prototype,
    "extend",
    {
        writable: true,
        enumerable: false,
        configurable: true,
        // 擴展值是一個函數(shù)
        value: fucntion(o) {
            // 得到所有的自有屬性胳徽,包括不可枚舉屬性
            var names = Object.getOwnPropertyNames(o);
            // 將屬性逐個擴展給Object.prototype
            for(var i=0; i < names.length; i++) {
                // 如果屬性已經(jīng)存在,則跳過
                if(names[i] in this) continue;
                var desc = Object.getOwnPropertyDescriptor(o, names[i]);
                Object.defineProperty(this, names[i], desc);
            }
        }
    }
);

對象的三個屬性

原型屬性

每一個JavaScript對象(null除外)都和另外一個對象相關(guān)聯(lián)爽彤,這個對象就是原型對象养盗,每一個對象都從原型繼承屬性
沒有原型的對象為數(shù)不多适篙,Object.prototype就是其中之一往核。它不繼承任何屬性。

對象 原型
通過對象直接量創(chuàng)建的對象 Object.prototype匙瘪。
通過new創(chuàng)建的對象 構(gòu)造函數(shù)的prototype屬性铆铆。
通過Object.creat()創(chuàng)建的對象 第一個參數(shù)(也可以是null)作為它們的原型。

使用isPrototypeOf()進(jìn)行原型判斷丹喻,判斷在原型鏈(prototype chain)上進(jìn)行薄货,如:

var p = { x: 1 };
var o = Object.create(p);
p.isPrototypeOf(o);                 // true,o繼承自p
Object.prototype.isPrototypeOf(o);  // true碍论,p繼承自O(shè)bject.prototype

類屬性

對象的類屬性(class attribute)是一個字符串谅猾,用以表示對象的類型信息。
默認(rèn)的toString()方法(繼承自O(shè)bject.prototype)返回了如下這種格式的字符串:

[object class]

很多對象繼承的toString()方法重寫了鳍悠,為了能調(diào)用正確的toString()方法税娜,必須間接地調(diào)用Function.call()方法

function classof(o) {
    if(o === null)      return "Null";
    if(o == undefined)  return "Undefined";
    return Object.prototype.toString.call(o).slice(8, -1);  // 使用Function.call方法
}

通過對象直接量藏研、new構(gòu)造函數(shù)或者Object.create()這3種方式創(chuàng)建的對象敬矩,類屬性都是"Object"。

classof(null);      // "Null"
classof(1);         // "Number"
classof("");        // "String"
classof(false);     // "Boolean"
classof({});        // "Object"
classof([]);        // "Array"
classof(/./);       // "RegExp"
classof(new Date());// "Date"
classof(window);    // "Window"
function f() {}
classof(f);         // "Function",注意與下面的區(qū)別
classof(new f());   // "Object"

可擴展性

對象的可擴展性表示是否可以給對象添加新屬性蠢挡。
所有的內(nèi)置對象和自定義對象都是顯式可擴展的弧岳,宿主對象的可擴展性由JavaScript引擎定義的凳忙。

方法 作用
Object.isExtensible() 判斷對象是否可擴展。
Object.preventExtensions() 將對象設(shè)置為不可擴展禽炬。注意:一旦設(shè)置為不可擴展涧卵,將無法再轉(zhuǎn)換回可擴展。
Object.seal() 將對象設(shè)置為不可擴展腹尖,同時所有的自有屬性不可配置柳恐。注意:對于已經(jīng)封閉(sealed)的對象不能解封。
Object.freeze() 除了完成seal()的功能外热幔,同時還將所有的數(shù)據(jù)屬性設(shè)置為只讀(如果對象的存取器屬性具有settter方法乐设,存取器屬性將不受影響,仍可以通過給屬性賦值調(diào)用它們)绎巨。

序列化對象(serialization)

對象序列化(JSON.stringify())是指對象的狀態(tài)轉(zhuǎn)換為字符串伤提,也可將字符串還原(JSON.parse())為對象。
JSON.stringify()只能序列化對象可枚舉的自有屬性认烁。

o = { x:1, y:{z:[false, null, ""]} };
s = JSON.stringify(o);                  // s是"{ x:1, y:{z:[false, null, ""]} }"  
p = JSON.parse(s);                      // p是o的深拷貝
  • Number肿男、String、Boolean却嗡、null舶沛、對象、數(shù)組都可以序列化和還原窗价。NaN如庭、Infinity和-Infinity序列化結(jié)果是null。
  • Date對象序列化的結(jié)果是ISO格式的日期字符串(參照Date.toJSON()函數(shù))撼港,但JSON.parse()依然保留它們的字符串形態(tài)坪它。
  • 函數(shù)、RegExp帝牡、Error對象和undefined值不能序列化和還原(沒有定義toJSON()方法)往毡。
var obj = new Date();
var s = JSON.stringify(obj);
var d = JSON.parse(s);
alert(obj);                     // Sat May 14 2016 17:33:04 GMT+0800
alert(s);                       // "2016-05-14T09:33:04.874Z" (stringify返回時包含2層"")
alert(d);                       // 2016-05-14T09:33:04.874Z
alert(typeof d);                // string類型,無法還原為Date類型

對象方法

toStirng()

由于默認(rèn)的toString()方法并不會輸出很多有用的信息([object Object])靶溜,因此很多類都帶有自定義的toString()开瞭。
例如,數(shù)組轉(zhuǎn)換為字符串時罩息,結(jié)果是一個數(shù)組元素列表嗤详;當(dāng)函數(shù)轉(zhuǎn)換為字符串時,得到函數(shù)的源代碼瓷炮。

toLocaleString()

這個方法返回本地化字符串葱色。Object默認(rèn)的toLocaleString()并不做任何本地化自身的操作,它僅調(diào)用toString()方法并返回對應(yīng)值娘香。
Date和Number類對toLocaleString()方法做了定制苍狰,可以用它對數(shù)字恐锣、日期和時間做本地化的轉(zhuǎn)換。

toJSON()

Object.property實際上沒有定義toJSON()方法舞痰,但對于需要序列化的對象來說,JSON.stringify()方法會調(diào)用toJSON()方法诀姚。
如果在待序列化的對象中存在這個方法响牛,則調(diào)用它,返回值即是序列化的結(jié)果赫段,而不是原始的對象呀打。

valueOf()

當(dāng)JavaScript需要將對象轉(zhuǎn)換為某種原始值時調(diào)用,尤其是轉(zhuǎn)換為數(shù)字的時候糯笙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贬丛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子给涕,更是在濱河造成了極大的恐慌豺憔,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件够庙,死亡現(xiàn)場離奇詭異恭应,居然都是意外死亡,警方通過查閱死者的電腦和手機耘眨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門昼榛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人剔难,你說我怎么就攤上這事胆屿。” “怎么了偶宫?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵非迹,是天一觀的道長。 經(jīng)常有香客問我纯趋,道長彻秆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任结闸,我火速辦了婚禮唇兑,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘桦锄。我一直安慰自己扎附,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布结耀。 她就那樣靜靜地躺著留夜,像睡著了一般匙铡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碍粥,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天鳖眼,我揣著相機與錄音,去河邊找鬼嚼摩。 笑死钦讳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的枕面。 我是一名探鬼主播愿卒,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼潮秘!你這毒婦竟也來了琼开?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤枕荞,失蹤者是張志新(化名)和其女友劉穎柜候,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躏精,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡改橘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了玉控。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飞主。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖高诺,靈堂內(nèi)的尸體忽然破棺而出碌识,到底是詐尸還是另有隱情,我是刑警寧澤虱而,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布筏餐,位于F島的核電站,受9級特大地震影響牡拇,放射性物質(zhì)發(fā)生泄漏魁瞪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一惠呼、第九天 我趴在偏房一處隱蔽的房頂上張望导俘。 院中可真熱鬧,春花似錦剔蹋、人聲如沸旅薄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽少梁。三九已至洛口,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凯沪,已是汗流浹背第焰。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留妨马,地道東北人挺举。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像身笤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子葵陵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容