今天要學習的是物件(Object),事不宜遲,開始學習吧!
在物件中,每個特性都有一組對應的名稱(key)與值(value),如下式程式碼的 name 與 Bill。
要注意的是物件中的特性並不是用;
的方式區隔,而會以,
的隔開每個特性。
// name 為特性(key),而 Bill 為值(value)。
var obj = {
name: 'Bill',
habbit: 'play computer'
};
對於物件特性的取值方式則有兩種:
.
的特性存取
[" "]
的鍵值存取
Ex: 透過 .
的方式取值
var obj = {
name: 'Bill',
habbit: 'play computer'
};
console.log(obj.name);
Ex2: 透過 [" "]
的方式取值
var obj = {
name: 'Bill',
habbit: 'play computer'
};
console.log(obj["habbit"]);
而存值的方式也不困難,如下:
Ex: 透過 .
的方式存值
var obj = {};
obj.name = 'Bill';
console.log(obj);
Ex2: 透過 [" "]
的方式存值
var obj = {};
obj["habbit"] = 'play computer';
console.log(obj);
用比較簡單的方式來講,就是在物件中使用函式(function),就稱為使用物件的方法。
Ex: 調用 obj
物件的方法 sayHi()
var obj = {
name: 'Bill',
sayHi: function(){
console.log(`Hello! ${this.name}`);
}
}
obj.sayHi();
這個部分要學習關於物件比較深入的部份:原型
Object.prototype.__proto__
先來看看 ECMAScript 2015(第6版,ECMA-262)怎麼解釋它:
Object.prototype.proto is an accessor property with attributes { [[Enumerable]]: false, [[Configurable]]: true }. The [[Get]] and [[Set]] attributes are defined as follows
從規範中可以知道, __proto__
是一個訪問器屬性(一個 getter 函數及一個 setter 函數),且為不可列舉(Enumerable)、可配置(Configurable)的。
而 MDN 對於 Object.prototype.__proto__
則有寫到:
The proto property of Object.prototype is an accessor property (a getter function and a setter function) that exposes the internal [[Prototype]] (either an object or null) of the object through which it is accessed.
前半段與 ECMAScript 2015(第6版,ECMA-262)所提相同,但後半段則提到可以透過 Object.prototype.__proto__
來訪問內部的 [[Prototype]],而這個內部的 [[Prototype]] 可能是一個物件(Object),也可能為空(null)。
來看看幾個測試的例子:
var obj = {age: 2};
console.log(obj.__proto__);
console.log(Object.prototype);
使用 Object.prototype
語法可以得到一個原型物件,從結果可以看到 obj.__proto__
可以取得與 Object.prototype
相同的結果。
而且與剛剛MDN所提及的可以透過 Object.prototype.__proto__
來訪問內部的 [[Prototype]] 相呼應。
但是,雖然這個語法可以達成這個目的,使用它卻是有爭議的。
使用 Object.prototype.__proto__
改變物件屬性,這種行為在每一個JavaScript引擎和瀏覽器中都是一個非常慢且影響性能的操作。
然而為了確保瀏覽器的兼容性(許多瀏覽器都有實作該功能),Object.prototype.__proto__
於 ES6 中被標準化。
但即使如此依然不推薦使用。
因此,如果有操作原型物件的需求,應該使用 Object.getPrototypeOf
和Object.setPrototypeOf
語法。
Object.getPrototypeOf
和Object.setPrototypeOf
語法等同於 Object.prototype.__proto__
的 getter 函數 與 setter 函數。
var obj = {age: 2};
console.log(obj.__proto__);
console.log(Object.getPrototypeOf(obj));
object that provides shared properties for other objects
ECMAScript 2015(第6版,ECMA-262)
原型(prototype)可以分享自己的特性給其他物件使用。
而所有的物件都有原型(prototype)
來看看某個物件的原型物件
const obj = {name: "Bill"};
console.log(obj);
可以看到 obj
物件的原型物件,而其中諸如 isPrototypeOf
等這些特性,都是這個原型物件分享給 obj
物件使用的特性。
再看看另外一個例子:
const numberArray = [1,2];
console.log(numberArray);
numberArray
的底層有一個陣列原型物件,而 numberArray
這個陣列之所以可以使用陣列的方法(諸如 reduce
、filter
、map
等),就是因為從陣列原型物件中繼承了這些方法。
再來讓我們透過例子理解原型鏈的概念:
這邊要注意的是這樣的寫法只適用於測試,實務運用會導致瀏覽器效能降低。
Ex. 透過 __proto__
將 obj
物件做為原型物件 assign(指定給) obj2
物件
const obj = {
sayHi: function() {
console.log('Hello!');
}
}
const obj2 = {};
obj2.__proto__ = obj;
obj2.sayHi();
在 obj
原型鏈搜尋sayHi()方法的過程如下:
obj2
物件在自己的特性找不到 sayHi
這個方法時,會往原型物件中尋找該方法sayHi
這個方法(因為剛剛已經將 obj
物件當作原型物件傳給 obj2
)obj2
使用 sayHi
這個方法並回傳 Hello
之所以能夠不斷往原型物件尋找特性,就是因為原型鏈(prtotype chain)的概念。
來看看 MDN 對於原型鏈(prtotype chain)的解釋:
每個物件都有一個連著其他原型(prototype)的私有屬性(private property)物件。原型物件也有著自己的原型,於是原型物件就這樣鏈結,直到撞見 null 為止:null 在定義裡沒有原型、也是原型鏈(prototype chain)的最後一個鏈結。
那如果當原型物件與物件自身都有同樣的屬性時,會是得到怎麼樣的結果呢?
以這個例子來說明:
const obj = {
name: "Bill",
sayHi: function(){
console.log('Hello!');
}
}
const obj2 = {
name: "John"
};
console.log(obj2.name);
可以發現獲得的值是 obj2
特性 name
的值 John
。所以可以知道:
物件會由原型鏈最上方開始尋找,當符合條件時就停止,所以會找到 John
的名字而不是 Bill
這個名字
關於物件(Object)今天先告一段落,明天繼續介紹物件(Object)的其他部分。
明天見~