語法
創建一個對象有兩種形式:聲明(文字)形式
和構造形式
。
例如:
// 聲明形式
var myObject = {
name: 'cherry',
age: 20,
};
// 構造形式
var myObject = new Object();
myObject.name = 'cherry';
myObject.age = 20;
一般我們在開發的時候,在創建一個對象的時更喜歡用聲明(文字)形式,這種形式可以簡化創建包含有大量屬性的對象的過程。
內置對象
Object
是JavaScript的基礎,它也是JavaScript語言類型中的一種。JavaScript中的語言類型包含:
- string
- number
- boolean
- null
- undefined
- Object
- symbol (ES6新增)
除去object
之外,其他的類型都被稱爲簡單基礎類型
,在使用typeof
來測試null
的時候,會返回‘object’
,但是null
並不是object
類型。造成這種情況是由於不同對象在底層會被表示爲二進制,當二進制的前三位都是0
的時候會被認爲是object類型,而null的二進制表示爲全0,因此,前三位自然是0,所以在使用typeof
的時候,會返回’object‘
。Object類型有多衍生的子類型,函數就是Object的一個子類型,數組也是。這些子類型通常被稱爲JavaScript的內置對象
。有些內置對象的名字看起來和簡單基礎類型一樣。
- String
- Number
- Boolean
- Array
- Function
- Date
- Error
- RegExp
- Object
這些內置對象在JavaScript中實際上是內置函數
,這些內置函數可以作爲構造函數
來使用,從而可以構建一個對應子類型的新對象。
例如:
var str = 'I am a string';
typeof str ; // string
str instanceof String; // false
var strObj = new String('I am a string');
typeof strObj ; // object
strObj instanceof String; // true
原始值’I am a string’並不是一個對象,它只是一個字面量,並且是一個不可改變的值。當我們需要在字符串上執行一些操作的時候,比如:獲取具體的對應位置的字符,獲取字符串的長度…,需要將其轉換爲String對象,幸好,在必要的時候語言會自動將字符串字面量轉換爲String對象。也就是數,你不需要自己顯式的創建一個對象。
var str = 'I am a string';
console.log(str.length); // 13
console.log(str.charAt(3)); // 'm'
對象中的內容
對象中的內容是由一些存儲在特定命名位置的(任何類型)的值組成。雖然這麼說,但是對象並不是擁有這些內容,在引擎內部,這些值的存儲方式各種各樣,一般並不會存儲在對象容器中,存儲在對象容器中的是這些屬性的名字,這些名字就像指針一樣,指向這些值真正的存儲位置。
訪問對象中的內容
訪問對象中的內容有兩種方式:.操作符(屬性訪問)
和[]操作符(鍵訪問)
。
例如:
var myObj = {
a: 2,
};
myObj.a ; // 2
myObj['a']; // 2
這兩種訪問方式訪問的都是同一個位置,拿到的也是同一個值,在日常開發中,我們最常用的是屬性訪問。這兩種訪問方式的區別在於,.操作符
要求屬性名必須符合標識符的命名規範,而['...']操作符
則可以接受任意的UTF-8/Unicode字符串。例如,如果訪問名稱爲’super-name’的屬性,就不能使用.操作符
,只能使用['super-name']
來訪問。
屬性描述符
從ES5開始,所有的屬性都具有屬描述符,屬性描述符用來描述該屬性是否只讀,是否可配置,以及是否可遍歷。所以屬性描述符是針對一個屬性而言的,並不是針對整個對象。
- writable
- configurable
- enumerable
以上就是JavaScript中的屬性描述符。writable決定了是否可以修改屬性的值,configurable決定了是否可以配置屬性的屬性描述符,enumerable決定了屬性是否可遍歷。在我們創建一個屬性的時候,這三個屬性描述符都是默認值。
var myObj = {
a: 2,
};
Object.getOwnPropertyDescription(myObj, 'a');
// {
// value: 2,
// writable: true,
// configurable: true,
// enumerable: true,
// }
我們可以使用Object.defineProperty(…)來添加一個新屬性或修改一個已有屬性,並對屬性操作符進行設置。
var myObj = {};
Object,defineProperty(myObj, 'a', {
value: 2,
wirtable: true,
configurable: true,
enumerable: true,
});
一般來說,你並不會使用這種方式來爲對象添加屬性,除非你想改變屬性的屬性描述符。
getter和setter
ES5中的getter
和setter
用來改寫默認操作,但是隻能應用於單個屬性,不能應用於整個對象上。getter
是一個隱藏函數,在調用對象屬性的時候調用,setter
也是一個隱藏函數,在給對象屬性賦值的時候調用。
通常來說getter
和setter
是成對出現的,當你給一個屬性定義了getter
和setter
時,這個屬性會被定義爲‘訪問描述符’
,對於訪問描述符,JavaScript會忽略它的value
和writable
屬性,取而代之的是隻關心它的get
和set
以及configurable
和enumerable
特性。
var myObj = {
get a() {
return this.a;
}
set a(val) {
this.a = val * 2;
}
}
myObj.a = 2;
myObj.a ; // 4
存在性
之前我們提到過,如果引用一個對象中不存在的屬性時,會返回undefined,但是undefined有可能是對象中的屬性存儲的值,那麼如何來區分者兩種情況?
var myObj = {
a: undefined,
}
myObj.a; // undefined
myObj.b; // undefined;
在JavaScript中用來判定一個屬性是否存在的方法有兩個:
- in操作符
- hasOwnProperty(…)
繼續上面的例子:
('a' in myObj); // true
('b' in myObj); // false
myObj.hasOwnProperty('a'); // true
myObj.hasOwnProperty('b'); // false
這兩種方法的區別是。in操作符
不但會在當前對象中進行查找,也會查找該對象的原型鏈,而hasOwnProperty(...)
只會在當前對象中進行查找,不會查找該對象的原型鏈。
遍歷
之前介紹的enumerable描述符用來描述一個屬性是否可枚舉,而可枚舉就相當於一個屬性是否會出現在對象屬性的遍歷中。例如:
var myObject = {
a: 2,
};
Object.definePropertyDescription(myObject, 'b', {
value: 4,
enumerbale: false, // 讓屬性b不可枚舉
});
myObject.b; // 3
("b" in myObject); // true
myObject.hasOwnProperty( "b" ); // true
for (var k in myObject) {
console.log( k, myObject[k] );
}
// "a" 2
可以看到的是,'b'
確實可以訪問,並且存在於myObject當中,但是使用for...in
進行遍歷的時候,確不會出現在循環中,原因就是屬性b是一個不可枚舉屬性
。
我們還可以使用其他的方式進行檢測一個屬性是否可枚舉,接着上面的例子:
myObjec.propertyIsEnumerable('a'); true
myObjec.propertyIsEnumerable('b'); false
Object.keys(myObject); // ['a']
Object.getOwnPropertyNames( myObject ); ['a', 'b']
propertyIsEnumerable(...)
方法可以檢測一個屬性是否可以枚舉,Object.keys(..)
返回的是一個數組,包含一個對象中所有可枚舉的屬性名,Object.getOwnPropertyNames(...)
返回的也是一個數組,包含的是一個對象所有的屬性名,無論它們是否可枚舉。且這兩個方法都只會返回這個對象包含的直接屬性,並不會涉及到對象的原型鏈。