js對象基礎知識

1、什麼是對象?

對象就是一組“鍵值對”(key-value)的集合,是一種無序的複合數據集合。
如下我們定義一個對象:

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

釋:上面代碼中,大括號就定義了一個對象,它被賦值給變量obj,所以obj就指向一個對象。該對象內部包含2個鍵值對(又可稱2個成員),第一個鍵值對是foo:‘Hello’,其中foo是鍵名,'Hello’是鍵值,成員之間用逗號分隔。

2、鍵名

注:對象的所有鍵名都是字符串,所以加不加引號都可以,上面的代碼可以這樣寫:

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

注:如果鍵名是數字,會自動被轉換成字符串。

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

輸出obj結果如下:
在這裏插入圖片描述
比如獲取鍵名爲100的鍵值:

obj['100']

結果爲
在這裏插入圖片描述
上面代碼中,對象obj的所有鍵名雖然看上去像數值,實際上都被自動轉成了字符串。
注:如果鍵名不符合標識名的條件(比如第一個字符爲數字,或者含有空格或運算符),且也不是數字,則必須加上引號,否則會報錯。

3、對象的屬性

對象的每一個鍵名也可稱之爲對象的屬性,它的屬性值可以是任何數據類型(比如:數組,字符串,數字,布爾,函數…,)如果它的屬性值是一個函數數,那麼我們就把這個屬性稱之爲”方法“。
例如,如下有一個對象obj,它有一個屬性p,但是p是一個函數,那麼p就是obj的一個方法

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

調用對象obj的p方法

obj.p(1) // 2

例如,如果屬性值還是一個對象,就形成了鏈式引用:

var o1 = {};//定義一個o1對象,它是一個空對象
var o2 = { bar: 'hello' };//定義一個o2對象,它有一個屬性bar
o1.foo = o2;//給o1添加一個屬性foo,foo的值是對象o2
o1.foo.bar;o1調用它的foo屬性,foo屬性值又是一個對象,又可以繼續調用它裏面的屬性,就形成了鏈式引用
// "hello"

4、爲對象添加屬性

對象在開始定義的時候可以是一個空對象,可以後面再給它加上屬性。
舉個例子:

var obj={};//定義一個空對象obj
obj.name="小紅";//爲對象obj創建一個屬性name,並且爲它賦值“小紅”
console.log(obj);//輸出obj
//{name:"小紅"}
console.log(obj.name);//輸出obj的name屬性的值

5、對象的引用

如果不同的變量名指向同一個對象,那麼他們都是這個對象的引用,也就是說指向同一個內存地址,修改其中一個變量,會影響到其他所有變量。
例如:

var o1={};//定義一個空對象o1
var o2=o1;//定義一個變量o2,把o1賦值給o2
o1.name=1;//爲對象o1創建一個屬性name,併爲它賦值1
o2.name;//輸出對象o2的name屬性,結果爲1
o2.name=2;//修改o2的name屬性的值爲2
o1.name;//輸出結果2,發現o1的屬性name的值也變成了2

上面例子中,其實o1和o2變量都指向了同一個對象,都引用了這個對象,在內存中都指向了同一個地址,通過o1去修改這個對象,o2也會隨之變化。任何一個變量添加屬性,另外一個變量都可以讀寫該屬性。

注:如果取消某一個變量對於原對象的引用,不會影響到另一個變量。

var o1 = {};//定義一個變量o1,然後給它賦值一個空對象
var o2 = o1;//把o1賦值給o2,此刻o2也指向那個空對象,注意o2內存中的地址是那個空對象,並不是o1
o1 = 1;//此刻我們修改o1,也就是此刻o1所指向的內存地址變量
o2 // {},我們發現o1變了,但是o2不會變,它仍然指向那個空對象。

同理,我們舉個簡單的例子:

var x=1;//定義一個變量x,它的值是1,內存中的地址就是1
var y=x;//把x賦值給y,此刻y也指向1
x=2;//修改x的值
y;//1,我們發現y的值並不會變化,它仍然指向1

上面表示當x的值發生變化後,y的值並沒有改變,這說明y和x並不是指向同一個內存地址。

6、表達式還是語句

對象採用大括號表示,這導致了一個問題:如果行首是一個大括號,它到底是表達式還是語句?

{ foo: 123 }

JavaScript 引擎讀到上面這行代碼,會發現可能有兩種含義。第一種可能是,這是一個表達式,表示一個包含foo屬性的對象;第二種可能是,這是一個語句,表示一個代碼區塊,裏面有一個標籤foo,指向表達式123。

爲了避免這種歧義,JavaScript 引擎的做法是,如果遇到這種情況,無法確定是對象還是代碼塊,一律解釋爲代碼塊。

{ console.log(123) } // 123

上面的語句是一個代碼塊,而且只有解釋爲代碼塊,才能執行。

如果要解釋爲對象,最好在大括號前加上圓括號。因爲圓括號的裏面,只能是表達式,所以確保大括號只能解釋爲對象。

({ foo: 123 })

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

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

上面代碼中,如果沒有圓括號,eval將其理解爲一個代碼塊;加上圓括號以後,就理解成一個對象。

7、屬性的讀取

讀取對象的屬性,有兩種方法,一種是使用點運算符,還有一種是使用方括號運算符。
例如:

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。
方括號運算符內部還可以使用表達式。

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

數字鍵可以不加引號,因爲會自動轉成字符串。
例如:

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

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

上面代碼中,對象obj的數字鍵0.7,加不加引號都可以,因爲會被自動轉爲字符串。
注意,數值鍵名不能使用點運算符(因爲會被當成小數點),只能使用方括號運算符。
例如:

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

obj.123 // 報錯
obj[123] // "hello world"

上面代碼的第一個表達式,對數值鍵名123使用點運算符,結果報錯。第二個表達式使用方括號運算符,結果就是正確的。

8、屬性的賦值

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

var obj = {};

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

一個對象你可以再任意時候新增屬性。
JavaScript 允許屬性的“後綁定”,也就是說,你可以在任意時刻新增屬性,沒必要在定義對象的時候,就定義好屬性。

var obj = { p: 1 };
// 等價於
var obj = {};
obj.p = 1;

9、屬性的查看

查看一個對象本身的所有屬性,可以使用Object.keys方法。

var obj = {
  key1: 1,
  key2: 2
};
Object.keys(obj);
// ['key1', 'key2']

上述例子中,你想查看對象obj所包含的所有屬性,可以使用Object.keys()

10、屬性的刪除: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。因此,不能根據delete命令的結果,認定某個屬性是存在的。

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

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

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

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

重點:

  • 1、delete命令只能刪除對象本身的屬性,無法刪除繼承的屬性

例如:

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

上面代碼中,toString是對象obj繼承的屬性,雖然delete命令返回true,但該屬性並沒有被刪除,依然存在。這個例子還說明,即使delete返回true,該屬性依然可能讀取到值。

11、屬性是否存在:in 運算符

in運算符用於檢查對象是否包含某個屬性(注意,檢查的是鍵名,不是鍵值),如果包含就返回true,否則返回false。它的左邊是一個字符串,表示屬性名,右邊是一個對象。

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

in運算符的一個問題是,它不能識別哪些屬性是對象自身的,哪些屬性是繼承的。就像上面代碼中,對象obj本身並沒有toString屬性,但是in運算符會返回true,因爲這個屬性是繼承的。
這時,可以使用對象的hasOwnProperty方法判斷一下,是否爲對象自身的屬性。

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

12、屬性的遍歷:for…in 循環

for…in循環用來遍歷一個對象的全部屬性。

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循環有兩個使用注意點。

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

舉例來說,對象都繼承了toString屬性,但是for…in循環不會遍歷到這個屬性。

var obj = {};

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

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

上面代碼中,對象obj繼承了toString屬性,該屬性不會被for…in循環遍歷到,因爲它默認是“不可遍歷”的。

如果繼承的屬性是可遍歷的,那麼就會被for…in循環遍歷到。但是,一般情況下,都是隻想遍歷對象自身的屬性,所以使用for…in的時候,應該結合使用hasOwnProperty方法,在循環內部判斷一下,某個屬性是否爲對象自身的屬性。

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

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章