對象

1.1 概念

  • 對象(object)是 JavaScript 語言的核心概念闻坚,也是最重要的數(shù)據(jù)類型压储。

什么是對象?簡單說徘禁,對象就是一組“鍵值對”(key-value)的集合,是一種無序的復(fù)合數(shù)據(jù)集合髓堪。

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

上面代碼中送朱,大括號就定義了一個對象,它被賦值給變量obj干旁,所以變量obj就指向一個對象驶沼。該對象內(nèi)部包含兩個鍵值對(又稱為兩個“成員”),第一個鍵值對是foo: 'Hello'争群,其中foo是“鍵名”(成員的名稱)回怜,字符串Hello是“鍵值”(成員的值)。鍵名與鍵值之間用冒號分隔换薄。第二個鍵值對是bar: 'World'玉雾,bar是鍵名,World是鍵值轻要。兩個鍵值對之間用逗號分隔复旬。

注意對象里也可以直接變量,就可以不用寫成鍵值對的形式冲泥,但實際上還是鍵值對赢底,比如:

var span = '24'
var offset
var obj = {span,offset}
obj.span //'24'
obj.offset //undefined

//等價于
var obj = {span:'24',offset:undfined}

但是如果未定義變量的情況下,不寫成鍵值對就會報錯


1.2 鍵名

對象的所有鍵名都是字符串(ES6 又引入了 Symbol 值也可以作為鍵名)柏蘑,所以加不加引號都可以幸冻。上面的代碼也可以寫成下面這樣。

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

如果鍵名是數(shù)值咳焚,會被自動轉(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

上面代碼中,對象obj的所有鍵名雖然看上去像數(shù)值革半,實際上都被自動轉(zhuǎn)成了字符串碑定。

如果鍵名不符合標識名的條件(比如第一個字符為數(shù)字流码,或者含有空格或運算符),且也不是數(shù)字延刘,則必須加上引號漫试,否則會報錯。

// 報錯
var obj = {
 1p: 'Hello World'
};

// 不報錯
var obj = {
 '1p': 'Hello World',
 'h w': 'Hello World',
 'p+q': 'Hello World'
};

上面對象的三個鍵名碘赖,都不符合標識名的條件驾荣,所以必須加上引號。

對象的每一個鍵名又稱為“屬性”(property)普泡,它的“鍵值”可以是任何數(shù)據(jù)類型播掷。如果一個屬性的值為函數(shù),通常把這個屬性稱為“方法”撼班,它可以像函數(shù)那樣調(diào)用歧匈。

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

obj.p(1) // 2

上面代碼中,對象obj的屬性p砰嘁,就指向一個函數(shù)件炉。

如果屬性的值還是一個對象,就形成了鏈式引用矮湘。

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

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

上面代碼中妻率,對象o1的屬性foo指向?qū)ο髈2,就可以鏈式引用o2的屬性板祝。

對象的屬性之間用逗號分隔宫静,最后一個屬性后面可以加逗號(trailing comma),也可以不加券时。

var obj = {
 p: 123,
 m: function () { ... },
}

上面的代碼中孤里,m屬性后面的那個逗號,有沒有都可以橘洞。

屬性可以動態(tài)創(chuàng)建捌袜,不必在對象聲明時就指定。

var obj = {};
obj.foo = 123;
obj.foo // 123

上面代碼中炸枣,直接對obj對象的foo屬性賦值虏等,結(jié)果就在運行時創(chuàng)建了foo屬性。

1.3 對象的引用

如果不同的變量名指向同一個對象适肠,那么它們都是這個對象的引用霍衫,也就是說指向同一個內(nèi)存地址。修改其中一個變量侯养,會影響到其他所有變量敦跌。

var o1 = {};
var o2 = o1;

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

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

上面代碼中,o1和o2指向同一個對象逛揩,因此為其中任何一個變量添加屬性柠傍,另一個變量都可以讀寫該屬性麸俘。
此時,如果取消某一個變量對于原對象的引用惧笛,不會影響到另一個變量从媚。

var o1 = {};
var o2 = o1;

o1 = 1;
o2 // {}

上面代碼中,o1和o2指向同一個對象患整,然后o1的值變?yōu)?拜效,這時不會對o2產(chǎn)生影響,o2還是指向原來的那個對象并级。

但是,這種引用只局限于對象侮腹,如果兩個變量指向同一個原始類型的值嘲碧。那么,變量這時都是值的拷貝父阻。

var x = 1;
var y = x;

x = 2;
y // 1

上面的代碼中愈涩,當x的值發(fā)生變化后,y的值并不變加矛,這就表示y和x并不是指向同一個內(nèi)存地址履婉。

1.4 表達式還是語句?

對象采用大括號表示斟览,這導(dǎo)致了一個問題:如果行首是一個大括號毁腿,它到底是表達式還是語句?

{ foo: 123 }

JavaScript 引擎讀到上面這行代碼苛茂,會發(fā)現(xiàn)可能有兩種含義已烤。第一種可能是,這是一個表達式妓羊,表示一個包含foo屬性的對象胯究;第二種可能是,這是一個語句躁绸,表示一個代碼區(qū)塊裕循,里面有一個標簽foo,指向表達式123净刮。

為了避免這種歧義剥哑,JavaScript 規(guī)定,如果行首是大括號淹父,一律解釋為語句(即代碼塊)星持。如果要解釋為表達式(即對象),必須在大括號前加上圓括號弹灭。

({ foo: 123})

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

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

上面代碼中揪垄,如果沒有圓括號,eval將其理解為一個代碼塊逻翁;加上圓括號以后饥努,就理解成一個對象。

2 屬性的操作

2.1 讀取屬性

讀取對象的屬性八回,有兩種方法酷愧,一種是使用點運算符,還有一種是使用方括號運算符缠诅。

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

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

上面代碼分別采用點運算符和方括號運算符溶浴,讀取屬性p。

請注意管引,如果使用方括號運算符士败,鍵名必須放在引號里面,否則會被當作變量處理褥伴。

var foo = 'bar';

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

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

上面代碼中谅将,引用對象obj的foo屬性時,如果使用點運算符重慢,foo就是字符串饥臂;如果使用方括號運算符,但是不使用引號似踱,那么foo就是一個變量隅熙,指向字符串bar。

方括號運算符內(nèi)部還可以使用表達式核芽。

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

數(shù)字鍵可以不加引號猛们,因為會自動轉(zhuǎn)成字符串。

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

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

上面代碼中狞洋,對象obj的數(shù)字鍵0.7弯淘,加不加引號都可以,因為會被自動轉(zhuǎn)為字符串吉懊。

注意庐橙,數(shù)值鍵名不能使用點運算符(因為會被當成小數(shù)點),只能使用方括號運算符借嗽。

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

obj.123 // 報錯
obj[123] // "hello world"
2.2 屬性的賦值

點運算符和方括號運算符,不僅可以用來讀取值恶导,還可以用來賦值。

var obj = {};

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

上面代碼中邦泄,分別使用點運算符和方括號運算符,對屬性賦值肌索。
JavaScript 允許屬性的“后綁定”诚亚,也就是說午乓,你可以在任意時刻新增屬性,沒必要在定義對象的時候梢灭,就定義好屬性腕唧。

var obj = { p: 1 };

// 等價于

var obj = {};
obj.p = 1;
2.3 查看所有屬性

Object.keys方法的參數(shù)是一個對象瘾英,返回一個數(shù)組。該數(shù)組的成員都是該對象自身的(而不是繼承的)所有屬性名

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

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

注意只要是DOM對象他自身沒有屬性但惶,使用Object.keys得到的都是一個空的數(shù)組

<canvas id="canvas"></canvas>
var canvas = document.getElementById('canvas');
Object.keys(canvas);
//[]

如果想要得到DOM對象的所有屬性(包括原型上的)就要用for in來遍歷

for(var key in canvas){
   console.log(key)
}
2.4 delete命令

delete命令用于刪除對象的屬性膀曾,刪除成功后返回true阳啥。

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

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

上面代碼中,delete命令刪除對象obj的p屬性斩狱。刪除后所踊,再讀取p屬性就會返回undefined,而且Object.keys方法的返回值也不再包括該屬性秕岛。

注意继薛,刪除一個不存在的屬性,delete不報錯惋增,而且返回true。

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

上面代碼中诈皿,對象obj并沒有p屬性,但是delete命令照樣返回true壶冒。因此胖腾,不能根據(jù)delete命令的結(jié)果瘪松,認定某個屬性是存在的。

只有一種情況记罚,delete命令會返回false壳嚎,那就是該屬性存在,且不得刪除说庭。

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

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

上面代碼之中刊驴,對象obj的p屬性是不能刪除的寡润,所以delete命令返回false。

另外攻礼,需要注意的是栗柒,delete命令只能刪除對象本身的屬性知举,無法刪除繼承的屬性雇锡。

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

上面代碼中僚焦,toString是對象obj繼承的屬性芳悲,雖然delete命令返回true,但該屬性并沒有被刪除谅年,依然存在肮韧。這個例子還說明,即使delete返回true超燃,該屬性依然可能讀取到值意乓。

2.5 in運算符

in運算符用于檢查對象是否包含某個屬性(注意院究,檢查的是鍵名本涕,不是鍵值),如果包含就返回true样漆,否則返回false放祟。

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

in運算符的一個問題是呻右,它不能識別哪些屬性是對象自身的,哪些屬性是繼承的眉撵。

var obj = {};
'toString' in obj // true

上面代碼中纽疟,toString方法不是對象obj自身的屬性,而是繼承的屬性散吵。但是蟆肆,in運算符不能識別炎功,對繼承的屬性也返回true。

2.6 for…in 循環(huán)

for...in循環(huán)用來遍歷一個對象的全部屬性官紫。

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

for (var i in obj) {
 console.log(obj[i]);
}
// 1
// 2
// 3

下面是一個使用for...in循環(huán)束世,提取對象屬性名的例子床玻。

var obj = {
 x: 1,
 y: 2
};
var props = [];
var i = 0;

for (var p in obj) {
 props[i++] = p
}

props // ['x', 'y']

for...in循環(huán)有兩個使用注意點锈死。

  • 它遍歷的是對象所有可遍歷(enumerable)的屬性,會跳過不可遍歷的屬性其屏。
  • 它不僅遍歷對象自身的屬性缨该,還遍歷繼承的屬性贰拿。

舉例來說,對象都繼承了toString屬性妙真,但是for...in循環(huán)不會遍歷到這個屬性荚守。

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

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

上面代碼中癌椿,對象obj繼承了toString屬性菱阵,該屬性不會被for...in循環(huán)遍歷到,因為它默認是“不可遍歷”的晴及。

如果繼承的屬性是可遍歷的,那么就會被for...in循環(huán)遍歷到琳钉。但是歌懒,一般情況下及皂,都是只想遍歷對象自身的屬性且改,所以使用for...in的時候,應(yīng)該結(jié)合使用hasOwnProperty方法碍拆,在循環(huán)內(nèi)部判斷一下感混,某個屬性是否為對象自身的屬性礼烈。

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

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

3 with語句

with語句的格式如下:

with (對象) {
 語句;
}

它的作用是操作同一個對象的多個屬性時济丘,提供一些書寫的方便摹迷。

// 例一
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)部有變量的賦值操作驮审,必須是當前對象已經(jīng)存在的屬性,否則會創(chuàng)造一個當前作用域的全局變量地来。

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

obj.p1 // undefined
p1 // 4

上面代碼中未斑,對象obj并沒有p1屬性,對p1賦值等于創(chuàng)造了一個全局變量p1府阀。正確的寫法應(yīng)該是芽突,先定義對象obj的屬性p1寞蚌,然后在with區(qū)塊內(nèi)操作它挟秤。

這是因為with區(qū)塊沒有改變作用域,它的內(nèi)部依然是當前作用域煞聪。這造成了with語句的一個很大的弊病,就是綁定對象不明確昔脯。

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

單純從上面的代碼塊隧饼,根本無法判斷x到底是全局變量静陈,還是對象obj的一個屬性鲸拥。這非常不利于代碼的除錯和模塊化刑赶,編譯器也無法對這段代碼進行優(yōu)化撞叨,只能留到運行時判斷浊洞,這就拖慢了運行速度法希。因此铁材,建議不要使用with語句著觉,可以考慮用一個臨時變量代替with。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惊暴,一起剝皮案震驚了整個濱河市饼丘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辽话,老刑警劉巖肄鸽,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異油啤,居然都是意外死亡典徘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門益咬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逮诲,“玉大人,你說我怎么就攤上這事幽告∑胨簦” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長廷蓉。 經(jīng)常有香客問我行楞,道長形用,這世上最難降的妖魔是什么解愤? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上健盒,老公的妹妹穿的比我還像新娘。我一直安慰自己搏色,他們只是感情好航邢,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布九火。 她就那樣靜靜地躺著勒极,像睡著了一般键痛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上丁频,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音署穗,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的懂盐。 我是一名探鬼主播俐银,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼榆俺,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起烁涌,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蜓肆,失蹤者是張志新(化名)和其女友劉穎仗扬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霹疫。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡屠阻,死狀恐怖吧恃,靈堂內(nèi)的尸體忽然破棺而出蛉加,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布卢厂,位于F島的核電站死嗦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粒氧。R本人自食惡果不足惜外盯,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酿傍。 院中可真熱鬧,春花似錦巡通、人聲如沸窘行。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罐盔。三九已至但绕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惶看,已是汗流浹背捏顺。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纬黎,地道東北人幅骄。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像本今,于是被迫代替她去往敵國和親拆座。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

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

  • 概述 生成方法 對象(object)是 JavaScript 語言的核心概念冠息,也是最重要的數(shù)據(jù)類型挪凑。 什么是對象?...
    許先生__閱讀 252評論 0 1
  • 1 概述 1.1 生成方法 對象(object)是 JavaScript 語言的核心概念逛艰,也是最重要的數(shù)據(jù)類型躏碳。 ...
    徵羽kid閱讀 287評論 0 0
  • 屬性的簡潔表示法 ES6允許直接寫入變量和函數(shù),作為對象的屬性和方法散怖。 上面代碼表明菇绵,ES6允許在對象之中,直接寫...
    oWSQo閱讀 504評論 0 0
  • 參考文章:對象 1.概述 1.1生成方法 什么是對象镇眷?簡單說咬最,對象就是一組“鍵值對”(key-value)的集合,...
    chrisghb閱讀 791評論 0 0
  • 1.屬性的簡潔表示法 允許直接寫入變量和函數(shù) 上面代碼表明偏灿,ES6 允許在對象之中丹诀,直接寫變量。這時翁垂,屬性名為變量...
    雨飛飛雨閱讀 1,130評論 0 3