JavaScript 02-2 對(duì)象

對(duì)象就是一組“鍵值對(duì)”(key-value)的集合,是一種無序的復(fù)合數(shù)據(jù)集合酣胀。鍵名都是字符串,加不加引號(hào)都可以娶聘。

var obj = {
  foo: 'Hello',
  bar: 'World'
};
// 或者
var obj = {
  'foo': 'Hello',
  'bar': 'World'
};

如何鍵名是數(shù)值闻镶,會(huì)被自動(dòng)轉(zhuǎn)為字符串。

var obj = {
  1: 'a',
  3.2: 'b',
  1e2: true,
  1e-2: true,
  .234: true,
  0xFF: true
};

obj
// Object {
//   1: "a",
//   3.2: "b",
//   100: true,
//   0.01: true,
//   0.234: true,
//   255: true
// }

obj['100'] // true

上面代碼中丸升,對(duì)象obj的所有鍵名雖然看上去像數(shù)值儒溉,實(shí)際上都被自動(dòng)轉(zhuǎn)成了字符串。

如果鍵名不符合標(biāo)識(shí)名的條件(比如第一個(gè)字符為數(shù)字发钝,或者含有空格或運(yùn)算符)顿涣,且也不是數(shù)字剂邮,則必須加上引號(hào)唁桩,否則會(huì)報(bào)錯(cuò)。

// 報(bào)錯(cuò)
var obj = {
  1p: 'Hello World'
};

// 不報(bào)錯(cuò)
var obj = {
  '1p': 'Hello World',
  'h w': 'Hello World',
  'p+q': 'Hello World'
};

上面對(duì)象的三個(gè)鍵名凸克,都不符合標(biāo)識(shí)名的條件孵淘,所以必須加上引號(hào)蒲障。

對(duì)象的每一個(gè)鍵名又稱為“屬性”(property),它的“鍵值”可以是任何數(shù)據(jù)類型瘫证。如果一個(gè)屬性的值為函數(shù)揉阎,通常把這個(gè)屬性稱為“方法”,它可以像函數(shù)那樣調(diào)用背捌。

var obj = {
  p: function (x) {
    return 2 * x;
  }
};

obj.p(1) // 2

上面代碼中毙籽,對(duì)象obj的屬性p,就指向一個(gè)函數(shù)毡庆。

屬性可以動(dòng)態(tài)創(chuàng)建坑赡,不必在對(duì)象聲明時(shí)就指定烙如。如果屬性的值還是一個(gè)對(duì)象,就形成了鏈?zhǔn)揭谩?/p>

var o1 = {};
var o2 = { bar: 'hello' };

o1.foo = o2;
o1.foo.bar // "hello"

上面代碼中毅否,對(duì)象o1的屬性foo指向?qū)ο?code>o2亚铁,就可以鏈?zhǔn)揭?code>o2的屬性。

對(duì)象的引用

如果不同的變量名指向同一個(gè)對(duì)象螟加,那么它們都是這個(gè)對(duì)象的引用徘溢,也就是說指向同一個(gè)內(nèi)存地址。修改其中一個(gè)變量捆探,會(huì)影響到其他所有變量然爆。

var o1 = {};
var o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

上面代碼中,o1o2指向同一個(gè)對(duì)象徐许,因此為其中任何一個(gè)變量添加屬性,另一個(gè)變量都可以讀寫該屬性卒蘸。

此時(shí)雌隅,如果取消某一個(gè)變量對(duì)于原對(duì)象的引用,不會(huì)影響到另一個(gè)變量缸沃。

var o1 = {};
var o2 = o1;

o1 = 1;
o2 // {}

上面代碼中恰起,o1o2指向同一個(gè)對(duì)象,然后o1的值變?yōu)?code>1趾牧,這時(shí)不會(huì)對(duì)o2產(chǎn)生影響检盼,o2還是指向原來的那個(gè)對(duì)象。

但是翘单,這種引用只局限于對(duì)象吨枉,如果兩個(gè)變量指向同一個(gè)原始類型的值。那么哄芜,變量這時(shí)都是值的拷貝貌亭。

var x = 1;
var y = x;

x = 2;
y // 1

上面的代碼中,當(dāng)x的值發(fā)生變化后认臊,y的值并不變圃庭,這就表示yx并不是指向同一個(gè)內(nèi)存地址。

表達(dá)式 or 語句

對(duì)象采用大括號(hào)表示失晴,這導(dǎo)致了一個(gè)問題:如果行首是一個(gè)大括號(hào)剧腻,它到底是表達(dá)式還是語句?

{ foo: 123 }

JavaScript 引擎讀到上面這行代碼涂屁,會(huì)發(fā)現(xiàn)可能有兩種含義书在。第一種可能是,這是一個(gè)表達(dá)式拆又,表示一個(gè)包含foo屬性的對(duì)象蕊温;第二種可能是袱箱,這是一個(gè)語句,表示一個(gè)代碼區(qū)塊义矛,里面有一個(gè)標(biāo)簽foo发笔,指向表達(dá)式123

為了避免這種歧義凉翻,JavaScript 引擎的做法是了讨,如果遇到這種情況,無法確定是對(duì)象還是代碼塊制轰,一律解釋為代碼塊前计。

{ console.log(123) } // 123

上面的語句是一個(gè)代碼塊,而且只有解釋為代碼塊垃杖,才能執(zhí)行男杈。

如果要解釋為對(duì)象,最好在大括號(hào)前加上圓括號(hào)调俘。因?yàn)閳A括號(hào)的里面伶棒,只能是表達(dá)式,所以確保大括號(hào)只能解釋為對(duì)象彩库。

({ foo: 123 }) // 正確
({ console.log(123) }) // 報(bào)錯(cuò)

這種差異在eval語句(作用是對(duì)字符串求值)中反映得最明顯肤无。

eval('{foo: 123}') // 123
eval('({foo: 123})') // {foo: 123}

上面代碼中,如果沒有圓括號(hào)骇钦,eval將其理解為一個(gè)代碼塊宛渐;加上圓括號(hào)以后,就理解成一個(gè)對(duì)象眯搭。

屬性的操作

屬性的讀取

讀取對(duì)象的屬性窥翩,有兩種方法,一種是使用點(diǎn)運(yùn)算符鳞仙,還有一種是使用方括號(hào)運(yùn)算符鳍烁。

var obj = {
  p: 'Hello World'
};

obj.p // "Hello World"
obj['p'] // "Hello World"

上面代碼分別采用點(diǎn)運(yùn)算符和方括號(hào)運(yùn)算符,讀取屬性p繁扎。

請(qǐng)注意幔荒,如果使用方括號(hào)運(yùn)算符,鍵名必須放在引號(hào)里面梳玫,否則會(huì)被當(dāng)作變量處理爹梁。

var foo = 'bar';

var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2

上面代碼中,引用對(duì)象objfoo屬性時(shí)提澎,如果使用點(diǎn)運(yùn)算符姚垃,foo就是字符串;如果使用方括號(hào)運(yùn)算符盼忌,但是不使用引號(hào)积糯,那么foo就是一個(gè)變量掂墓,指向字符串bar

方括號(hào)運(yùn)算符內(nèi)部還可以使用表達(dá)式看成。

obj['hello' + ' world']
obj[3 + 3]

數(shù)字鍵可以不加引號(hào)君编,因?yàn)闀?huì)自動(dòng)轉(zhuǎn)成字符串。

var obj = {
  0.7: 'Hello World'
};

obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"

上面代碼中川慌,對(duì)象obj的數(shù)字鍵0.7吃嘿,加不加引號(hào)都可以,因?yàn)闀?huì)被自動(dòng)轉(zhuǎn)為字符串梦重。

注意兑燥,數(shù)值鍵名不能使用點(diǎn)運(yùn)算符(因?yàn)闀?huì)被當(dāng)成小數(shù)點(diǎn)),只能使用方括號(hào)運(yùn)算符琴拧。

var obj = {
  123: 'hello world'
};

obj.123 // 報(bào)錯(cuò)
obj[123] // "hello world"

上面代碼的第一個(gè)表達(dá)式降瞳,對(duì)數(shù)值鍵名123使用點(diǎn)運(yùn)算符,結(jié)果報(bào)錯(cuò)蚓胸。第二個(gè)表達(dá)式使用方括號(hào)運(yùn)算符挣饥,結(jié)果就是正確的。

屬性的賦值

點(diǎn)運(yùn)算符和方括號(hào)運(yùn)算符赢织,不僅可以用來讀取值亮靴,還可以用來賦值馍盟。

var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';

上面代碼中于置,分別使用點(diǎn)運(yùn)算符和方括號(hào)運(yùn)算符,對(duì)屬性賦值贞岭。

JavaScript 允許屬性的“后綁定”八毯,也就是說,你可以在任意時(shí)刻新增屬性瞄桨,沒必要在定義對(duì)象的時(shí)候话速,就定義好屬性。

var obj = { p: 1 };

// 等價(jià)于

var obj = {};
obj.p = 1;

屬性的查看

查看一個(gè)對(duì)象本身的所有屬性芯侥,可以使用Object.keys方法泊交。

var obj = {
  key1: 1,
  key2: 2
};

Object.keys(obj);
// ['key1', 'key2']

屬性的刪除:delete 命令

delete命令用于刪除對(duì)象的屬性,刪除成功后返回true柱查。

var obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []

上面代碼中廓俭,delete命令刪除對(duì)象objp屬性。刪除后唉工,再讀取p屬性就會(huì)返回undefined研乒,而且Object.keys方法的返回值也不再包括該屬性。

注意淋硝,刪除一個(gè)不存在的屬性雹熬,delete不報(bào)錯(cuò)宽菜,而且返回true

var obj = {};
delete obj.p // true

上面代碼中竿报,對(duì)象obj并沒有p屬性铅乡,但是delete命令照樣返回true。因此仰楚,不能根據(jù)delete命令的結(jié)果隆判,認(rèn)定某個(gè)屬性是存在的。

只有一種情況僧界,delete命令會(huì)返回false侨嘀,那就是該屬性存在,且不得刪除捂襟。

var obj = Object.defineProperty({}, 'p', {
  value: 123,
  configurable: false
});

obj.p // 123
delete obj.p // false

上面代碼之中咬腕,對(duì)象objp屬性是不能刪除的,所以delete命令返回false(關(guān)于Object.defineProperty方法的介紹葬荷,請(qǐng)看《標(biāo)準(zhǔn)庫》的 Object對(duì)象一章)涨共。

另外,需要注意的是宠漩,delete命令只能刪除對(duì)象本身的屬性举反,無法刪除繼承的屬性(關(guān)于繼承參見《面向?qū)ο缶幊獭氛鹿?jié))。

var obj = {};
delete obj.toString // true
obj.toString // function toString() { [native code] }

上面代碼中扒吁,toString是對(duì)象obj繼承的屬性火鼻,雖然delete命令返回true,但該屬性并沒有被刪除雕崩,依然存在魁索。這個(gè)例子還說明,即使delete返回true盼铁,該屬性依然可能讀取到值粗蔚。

屬性是否存在:in 運(yùn)算符

in運(yùn)算符用于檢查對(duì)象是否包含某個(gè)屬性(注意,檢查的是鍵名饶火,不是鍵值)鹏控,如果包含就返回true,否則返回false肤寝。它的左邊是一個(gè)字符串当辐,表示屬性名,右邊是一個(gè)對(duì)象醒陆。

var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true

in運(yùn)算符的一個(gè)問題是瀑构,它不能識(shí)別哪些屬性是對(duì)象自身的,哪些屬性是繼承的。就像上面代碼中寺晌,對(duì)象obj本身并沒有toString屬性世吨,但是in運(yùn)算符會(huì)返回true,因?yàn)檫@個(gè)屬性是繼承的呻征。
這時(shí)耘婚,可以使用對(duì)象的hasOwnProperty方法判斷一下,是否為對(duì)象自身的屬性陆赋。

var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false
}

屬性的遍歷:for...in 循環(huán)

for...in循環(huán)用來遍歷一個(gè)對(duì)象的全部屬性沐祷。

var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log('鍵名:', i);
  console.log('鍵值:', obj[i]);
}
// 鍵名: a
// 鍵值: 1
// 鍵名: b
// 鍵值: 2
// 鍵名: c
// 鍵值: 3

for...in循環(huán)有兩個(gè)使用注意點(diǎn)。

  • 它遍歷的是對(duì)象所有可遍歷(enumerable)的屬性攒岛,會(huì)跳過不可遍歷的屬性赖临。
  • 它不僅遍歷對(duì)象自身的屬性,還遍歷繼承的屬性灾锯。
    舉例來說兢榨,對(duì)象都繼承了toString屬性,但是for...in循環(huán)不會(huì)遍歷到這個(gè)屬性顺饮。
var obj = {};

// toString 屬性是存在的
obj.toString // toString() { [native code] }

for (var p in obj) {
  console.log(p);
} // 沒有任何輸出

上面代碼中吵聪,對(duì)象obj繼承了toString屬性,該屬性不會(huì)被for...in循環(huán)遍歷到兼雄,因?yàn)樗J(rèn)是“不可遍歷”的吟逝。關(guān)于對(duì)象屬性的可遍歷性,參見《標(biāo)準(zhǔn)庫》章節(jié)中 Object 一章的介紹赦肋。

如果繼承的屬性是可遍歷的块攒,那么就會(huì)被for...in循環(huán)遍歷到。但是金砍,一般情況下局蚀,都是只想遍歷對(duì)象自身的屬性麦锯,所以使用for...in的時(shí)候恕稠,應(yīng)該結(jié)合使用hasOwnProperty方法,在循環(huán)內(nèi)部判斷一下扶欣,某個(gè)屬性是否為對(duì)象自身的屬性鹅巍。

var person = { name: '老張' };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name

with 語句

with語句的格式如下:

with (對(duì)象) {
  語句;
}

它的作用是操作同一個(gè)對(duì)象的多個(gè)屬性時(shí),提供一些書寫的方便料祠。

// 例一
var obj = {
  p1: 1,
  p2: 2,
};
with (obj) {
  p1 = 4;
  p2 = 5;
}
// 等同于
obj.p1 = 4;
obj.p2 = 5;

// 例二
with (document.links[0]){
  console.log(href);
  console.log(title);
  console.log(style);
}
// 等同于
console.log(document.links[0].href);
console.log(document.links[0].title);
console.log(document.links[0].style);

注意骆捧,如果with區(qū)塊內(nèi)部有變量的賦值操作,必須是當(dāng)前對(duì)象已經(jīng)存在的屬性髓绽,否則會(huì)創(chuàng)造一個(gè)當(dāng)前作用域的全局變量敛苇。

var obj = {};
with (obj) {
  p1 = 4;
  p2 = 5;
}

obj.p1 // undefined
p1 // 4

上面代碼中,對(duì)象obj并沒有p1屬性顺呕,對(duì)p1賦值等于創(chuàng)造了一個(gè)全局變量p1枫攀。正確的寫法應(yīng)該是括饶,先定義對(duì)象obj的屬性p1,然后在with區(qū)塊內(nèi)操作它。

這是因?yàn)?code>with區(qū)塊沒有改變作用域来涨,它的內(nèi)部依然是當(dāng)前作用域图焰。這造成了with語句的一個(gè)很大的弊病,就是綁定對(duì)象不明確蹦掐。

with (obj) {
  console.log(x);
}

單純從上面的代碼塊技羔,根本無法判斷x到底是全局變量,還是對(duì)象obj的一個(gè)屬性卧抗。這非常不利于代碼的除錯(cuò)和模塊化藤滥,編譯器也無法對(duì)這段代碼進(jìn)行優(yōu)化,只能留到運(yùn)行時(shí)判斷社裆,這就拖慢了運(yùn)行速度超陆。因此,建議不要使用with語句浦马,可以考慮用一個(gè)臨時(shí)變量代替with时呀。

with(obj1.obj2.obj3) {
  console.log(p1 + p2);
}

// 可以寫成
var temp = obj1.obj2.obj3;
console.log(temp.p1 + temp.p2);
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市晶默,隨后出現(xiàn)的幾起案子谨娜,更是在濱河造成了極大的恐慌,老刑警劉巖磺陡,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趴梢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡币他,警方通過查閱死者的電腦和手機(jī)坞靶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝴悉,“玉大人彰阴,你說我怎么就攤上這事∨墓冢” “怎么了尿这?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庆杜。 經(jīng)常有香客問我射众,道長,這世上最難降的妖魔是什么晃财? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任叨橱,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘罗洗。我一直安慰自己嘉裤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布栖博。 她就那樣靜靜地躺著屑宠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪仇让。 梳的紋絲不亂的頭發(fā)上典奉,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音丧叽,去河邊找鬼卫玖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛踊淳,可吹牛的內(nèi)容都是我干的假瞬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼迂尝,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼脱茉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起垄开,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤琴许,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后溉躲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榜田,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年锻梳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了箭券。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疑枯,死狀恐怖辩块,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情神汹,我是刑警寧澤庆捺,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布古今,位于F島的核電站屁魏,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏捉腥。R本人自食惡果不足惜氓拼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桃漾,春花似錦坏匪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至恋追,卻和暖如春凭迹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背苦囱。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國打工嗅绸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撕彤。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓鱼鸠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親羹铅。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚀狰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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

  • 數(shù)值 判斷NaN更可靠的方法是,利用NaN為唯一不等于自身的值的這個(gè)特點(diǎn)职员,進(jìn)行判斷造锅。 isFinite方法返回一個(gè)...
    guyigg閱讀 1,153評(píng)論 0 2
  • 函數(shù)和對(duì)象 1、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門語言來說都是核心的概念廉邑。通過函數(shù)可以封裝任意多條語句哥蔚,而且...
    道無虛閱讀 4,550評(píng)論 0 5
  • 屬性的簡潔表示法 ES6允許直接寫入變量和函數(shù),作為對(duì)象的屬性和方法蛛蒙。 上面代碼表明糙箍,ES6允許在對(duì)象之中,直接寫...
    oWSQo閱讀 506評(píng)論 0 0
  • 概述 生成方法 對(duì)象(object)是 JavaScript 語言的核心概念牵祟,也是最重要的數(shù)據(jù)類型深夯。 什么是對(duì)象?...
    許先生__閱讀 255評(píng)論 0 1
  • 1 概述 1.1 生成方法 對(duì)象(object)是 JavaScript 語言的核心概念诺苹,也是最重要的數(shù)據(jù)類型咕晋。 ...
    徵羽kid閱讀 292評(píng)論 0 0