JavaScript 對象
對象 指包含數(shù)據(jù)和用于處理數(shù)據(jù)的指令的數(shù)據(jù)結構. 對象有時也指現(xiàn)實世界中的一些事, 例如在賽車游戲當中一輛車或者一幅地圖都可以是一個對象。—— MDN
JavaScript 中的對象由屬性
和方法
組成。
屬性可以是任意 JavaScript 中的數(shù)據(jù)類型,方法則是一個函數(shù)。
1. 創(chuàng)建對象
對象的格式如下:
{
prop1: 'value1',
prop2: 666,
prop3: {},
method1: function() {
},
}
其中 prop1
、prop2
、prop3
都是屬性,method1
是方法,屬性是任意的數(shù)據(jù)類型,方法則是一個函數(shù)。
可以看到一個屬性或者方法,在其名字與值中間采用冒號 :
分隔,屬性和方法之間采用逗號 ,
分隔。
通常屬性和方法的名字會被稱為屬性名
與方法名
,屬性的值稱為屬性值
,方法的值則直接叫做 方法
。
對象符合 key/value
的結構,一個 key
對應一個 value
,這種結構也被稱為鍵值對,屬性名和方法名都是 key
,他們的值都是 value
。
注意:在 JavaScript 的對象中,方法和屬性的表示其實是一樣的。
一個屬性的值如果是一個函數(shù),則就把這個屬性稱之為方法,只是稱呼上的不同。
對象最簡單的創(chuàng)建方式就是使用對象字面量
。
var person = {};
以上創(chuàng)建了一個最簡單的空對象。
對象在 JavaScript 中的應用范圍非常廣。
可以試想一下,如果需要用變量描述一個人的信息,包括名字、性別、年齡、雙親信息,同時就要表示這個人的一些行為,如說話。
顯然數(shù)值、字符串、布爾類型這些數(shù)據(jù)類型是不太適合描述這樣一個人的。
這個時候就可以考慮使用對象,也就是說當需要描述的事物變得復合(無法使用單一的數(shù)據(jù)類型描述的時候),就可以考慮使用對象存儲數(shù)據(jù)。
var person = {
'name': '小明',
'age': 17,
isAdult: false,
sex: 'man',
hobby: ['eat', 'sleep', 'play doudou'],
parents: {
mather: {
name: '大紅',
},
father: {
name: '大明',
},
},
say: function() {
console.log('我叫' + this.name + ',我今年' + this.age + '歲了。');
},
};
console.log(person); // 在控制臺可以觀察 person 對象
上面這個 person
變量就是一個對象,用于描述一個人,這個人具有 name
、age
等屬性與方法。
在控制臺輸出對象后,可以對其展開,觀察他的內(nèi)容。
其中部分屬性在聲明的時候,屬性名上加上了引號,這在 JavaScript 中是被允許的,但一般情況下不會加上引號,原因之一是沒有必要加,不應該與字符串混淆,另外一點就是可以消除初學者對 JSON 和 JavaScript 對象在理解上的歧義。
2. 操作對象
創(chuàng)建對象主要是為了設置、訪問對象的屬性值,調用對象的方法等。
2.1 訪問對象的屬性值
訪問屬性有 2 種方式:
對象.屬性名
對象['屬性名']
var obj = {
key: 'value',
say: function() {
console.log('never 996');
},
};
console.log(obj.key); // 輸出:"value"
console.log(obj['key']); // 輸出:"value"
obj.say(); // 輸出:"never 996"
obj['say'](); // 輸出:"never 996"
這兩種方式都很常用。第二種通常會應用在需要用變量確定屬性名的時候去使用。
var person = {
age: 27,
name: '鴿手',
};
Object.keys(person).forEach(function(key) {
console.log(person[key]);
});
以上就是使用第二種方式的場景之一,使用 Object.keys()
獲取到 person 的所有屬性名組成的數(shù)組,對數(shù)組做遍歷拿到每一個屬性名并放在一個變量中,再通過變量訪問到對應的屬性值。
當試圖訪問一個不存在的屬性的時候,則會返回 undefined
。
var obj = {};
console.log(obj.value); // 輸出:undefined
2.2 設置對象的屬性值
設置屬性值也有 2 種方式:
對象.屬性名 = 屬性值
對象['屬性名'] = 屬性值
設置屬性值的方式與訪問值很相似,只是多了一個賦值操作。
設置屬性值按照如下規(guī)則進行:
- 如果對象中不存在這個屬性,則創(chuàng)建這個屬性并賦值
- 如果對象中存在這個屬性,則直接賦值
var person = {
age: 22,
};
person.name = '阿花';
person['hobby'] = ['eat', 'play doudou'];
console.log(person);
person.age = 33;
console.log(person);
3. 使用特殊的屬性名
對象的屬性名是可以用任意字符串表示的。
上面有提到,聲明屬性的時候可以帶上引號。
如果不帶引號,那屬性名必須要符合變量命名的規(guī)則,使用引號包裹屬性名,則可以使用任意字符串作為屬性名。
var obj = {
--^$@@@age: 16,
};
上面這樣寫是會報錯的,如果非要使用這樣的屬性名,就可以加上一對引號,可以雙引號,也可以是單引號,使用引號的規(guī)則和字符串一致。
var obj = {
'--^$@@@age': 16,
};
這種特殊的屬性名無法通過 對象.屬性名
的形式訪問。
var obj = {
'--^$@@@age': 16,
};
var val = obj.--^$@@@age;
JavaScript 無法解析這種特殊的語法,所以要使用 對象['屬性名']
的形式訪問。
var obj = {
'--^$@@@age': 16,
};
var val = obj['--^$@@@age'];
console.log(val); // 輸出:16
特殊的屬性名場景比較少,如統(tǒng)計字符串的場景。
var counter = {};
var strs = [
'#@T%TGFDSgfdsgsf',
'#@T%TGFDSgfdsgsf',
'123fdafeafewa',
'123fdafeafewa',
'#@T%TGFDSgfdsgsf',
];
strs.forEach(function(item) {
if (item in counter) {
counter[item]++;
} else {
counter[item] = 0;
}
});
console.log(counter);
strs 是由字符串組成的數(shù)組,即需要統(tǒng)計的一組數(shù)據(jù)。
利用對象的特性來對字符串分類計數(shù)。
4. 其他創(chuàng)建對象的方法
除了字面量的方式,還有許多創(chuàng)建對象的方式。
4.1 使用 Object 對象
使用 new Object()
或者 Object()
的方式也可以創(chuàng)建一個對象
var obj1 = new Object();
var obj2 = new Object; // 如果沒有參數(shù) 可以不帶括號
var obj3 = Object();
上面的方式都可以創(chuàng)建一個空對象。
比較有趣的是可以給 Object
傳遞一個對象字面量作為參數(shù),返回的對象的屬性與傳入的對象字面量的屬性一致。
var obj1 = new Object({
age: 11,
name: '長睫毛',
});
var obj2 = Object({
age: 12,
name: '小酒窩',
});
console.log(obj1, obj2);
4.2 構造函數(shù)
使用構造函數(shù),也可以創(chuàng)建對象。
function Car(color, maxSpeed) {
this.color = color;
this.maxSpeed = maxSpeed;
}
Car.prototype.bibi = function() {
console.log('嗶嗶!');
};
var car = new Car('red', 9999999);
console.log(car);
以上例子使用構造函數(shù)創(chuàng)建了一個速度超級快的車
對象。
4.3 Object.create
使用 Object.create
也可以創(chuàng)建一個新對象,但是必須傳遞一個對象作為參數(shù)。
var parent = {
walk: function() {
console.log('走路');
},
};
var son = Object.create(parent);
console.log(parent === son);
son.walk();
Object.create
會根據(jù)傳遞過去的對象生成一個新的對象,作為參數(shù)傳遞的對象會作為新對象的原型。
5. 遍歷對象
5.1 for … in
var me = {height: 180, weight: 70};
var i;
for (i in me) {
console.log(i);
console.log(me[i]);
}
使用 for ... in
可以遍歷對象的所有 key 值,也就是屬性名,取到所有的屬性就可以訪問到所有的屬性值。
需要注意的是 for ... in
循環(huán)只遍歷可枚舉屬性,同時對象原型上的也會被訪問到。
var me = {height: 180, weight: 70};
var you = Object.create(me);
you.age = 11;
var i;
for (i in you) {
console.log(i);
}
上面這個例子就把 me
和 you
中的所有屬性都遍歷出來了。
可以使用 Object.prototype.hasOwnProperty
來判斷一個屬性是否只處于其本身而不在原型上。
var me = {height: 180, weight: 70};
var you = Object.create(me);
you.age = 11;
var i;
for (i in you) {
if (you.hasOwnProperty(i)) {
console.log(i);
}
}
這樣就只會輸出 age
了。
5.2 Object.keys
Object.keys
會返回對象上的所有可枚舉屬性組成的數(shù)組。
var gugugu = {
name: '?王',
hobby: '放鴿子',
};
var keys = Object.keys(gugugu);
console.log(keys);
keys.forEach(function(key) {
console.log(gugugu[key])
});
通過遍歷屬性組成的數(shù)組來遍歷對象。
5.3 Object.getOwnPropertyNames
使用 Object.getOwnPropertyNames
也可以獲取到由屬性組成的數(shù)組,但數(shù)組會包括不可枚舉的屬性。
var gugugu = {
name: '?王',
hobby: '放鴿子',
};
var desc = Object.create(null);
desc.enumerable = false; // 是否可枚舉 默認就是false
desc.value = '最強鴿手';
Object.defineProperty(gugugu, 'nickname', desc);
console.log(Object.keys(gugugu));
console.log(Object.getOwnPropertyNames(gugugu));
使用 getOwnPropertyNames
比使用 keys
多出一個不可枚舉的 nickname
屬性。
注意:ES6 還提供了
Object.values
、Object.entries
、for ... of
、Reflect.ownKeys
等特性,結合這些特性也能遍歷一個對象。
6. 創(chuàng)建絕對純凈的對象
純凈對象僅為本篇中的稱呼方式,這種特殊的對象沒有特定的稱呼。
純凈對象即原型為 null 的對象。
使用 Object.create(null)
來創(chuàng)建純凈對象。
var obj1 = Object.create(null);
console.log(obj1);
var obj2 = {};
console.log(obj2);
可以嘗試在控制臺中對比這兩個對象,純凈對象是沒有原型的,無法調用 toString
、hasOwnProperty
、valueOf
這些原型上的方法。
大部分使用純凈對象的場景是使用 Object.defineProperty
為對象創(chuàng)建屬性的時候,屬性的描述需要一個絕對干凈的對象,防止特殊的屬性對描述造成影響。
另外的使用場景就是當作一個字典使用,防止原型上的內(nèi)容對字典產(chǎn)生干擾。
7. 小結
對象最常用的兩種創(chuàng)建方式,就是使用字面量和構造函數(shù)。
創(chuàng)建對象的時候應合理規(guī)劃屬性名和方法名,根據(jù)業(yè)務來確定如何使用對象。