JS 基礎 #3 | Object & Prototype Chain;Function Constructor 構建函數

Prototype Chain

每一個 Javascript 物件都必定會有一個 Prototype 屬性。因此 Javascript 才有辦法繼承。
可以宣告一個 constructor 構建函數,然後再宣告一個 instance 物件來繼承它。
例如上圖是 John 這個 instance 繼承了 Person 這個 constructor 構建函數。同時 Person 這個 instance 也繼承了 Object 這個 constructor 構建函數。



建立物件 — Function Constructor 構建函數

原本的建立物件方法如下:

var john = {
    name: 'John',
    yearOfBirth: 1990,
    job: 'teacher'
};

改為函式建構式的寫法建立一個物件(實例化 instantiation):

var Person = function(name, yearOfBirth, job) {
    this.name = name;
    this.yearOfBirth = yearOfBirth;
    this.job = job;
};


var john = new Person('John', 1990, 'teacher');
// 1. 會先建立一個空物件
// 2. 接著執行 function
// 3. this 指向這個空物件(而非 Windows,並且指派值)
// 4. 這個空物件指派給 john

開始試著繼承,原本的寫法如下:

var Person = function(name, yearOfBirth, job) {
	this.name = name;
	this.yearOfBirth = yearOfBirth;
	this.job = job;
  
	this.calculateAge = function() {
	    console.log(2016 - this.yearOfBirth);
  	}
	this.lastName = 'Smith';
}

var john = new Person('John', 1990, 'teacher');
var jane = new Person('Jane', 1969, 'designer');

john.calculateAge();
jane.calculateAge();

但這樣的寫法等於全部的實例 john、jane 裡都要有自己的 calculateAge function,而內容是完全一樣的。所以單純用 new 建立出來的 instance 會有無法共用屬性與資源浪費的狀況。因此可以改寫成繼承的方式,如此一來只要繼承 Person 來用就可以了:

原型鏈的引入

var Person = function(name, yearOfBirth, job) {
	this.name = name;
	this.yearOfBirth = yearOfBirth;
	this.job = job;
}

Person.prototype.calculateAge = function() {
  console.log(2016 - this.yearOfBirth);
}
Person.prototype.lastName = 'Smith';

var john = new Person('John', 1990, 'teacher');
var jane = new Person('Jane', 1969, 'designer');

john.calculateAge();
jane.calculateAge();



Prototype Chain 的概念

可以看到 john 是一個 Person 物件,裡面有 job、name、yearOfBirth 這三個屬性以及值。

原型鏈:prototype & __proto__

以上面的 var john = new Person(‘John’, 1990, ‘teacher’); 為例,呼叫 john.calculateAge(); 時,javascript到底是怎樣找到那個calculateAge(); function的呢?

john這個 instance 本身是沒有calculateAge這個function的,依照 javascript 的機制,john屬於Person的一個實例,所以如果在john找不到,那我們會去Person.prototype那找。那中間到底是怎樣讓personA連結到Person.prototype呢?

答案是, __proto__ (REF: MDN)

經過上方的練習後,試著在 console 裡打開來:

上圖下方可以看到 john 裡第一層的 proto ,而這個其實就是 Person 物件的 prototype 屬性。

john.__proto__ === Person.prototype //true

john__proto__會指到Person.prototype,所以在john沒找到 calculateAge();  function,javascript 就會透過__proto__Person.prototype去找。

那假如Person.prototype裡也還是沒有calculateAge function呢?那 javascript 會依照同樣邏輯,往Person.prototype.__proto__去找,依此類推,直到某一次的__proto__指到null為止。

而上面這一條透過__proto__不斷串起來的鍊,就叫做原型鍊。透過這一條原型鍊,就可以達成類似繼承的功能,可以呼叫自己 parent 的 method。

hasOwnProperty

如果想知道一個屬性是存在於 instance 本身,還是存在於原型鏈當中,可以使用 hasOwnProperty 這個function。

john.hasOwnProperty('job') // true

instanceof

另外可以用這個功能來查看目前的實例是繼承於誰。

john instanceof Person //true


建立物件 — Object.create

透過 Object.create 方法,也可以直接新增 john 實例,且繼承 personProto 。

var personProto = {
	calculateAge: function() {
		console.log(2019 - this.birthYear);
	}
}

var john = Object.create(personProto);
john.name = 'John';
john.yearOfBirth = 1990;
john.job = 'teacher';

Object.create 也可以同時宣告新增實例並直接指定實例的各種屬性。

var jane = Object.create(personProto,
{
	name: { value: 'Jane' },
	yearOfBirth: { value: 1969 },
	job: { value: 'designer' }
})

兩種寫法會得到一樣的結果:


Primitives & Objects 的差異

Primitive types are manipulated by value and object types are manipulated by reference. — 原始型別處理值,物件型別處理參考

以下代表了 primitives 本身是擁有值 by value 的。

// Primitives
var a = 23;
var b = a;
a = 46;
console.log(a);  //46
console.log(b);  //23

而 object 只是指向記憶體中的某個參考 by reference。下列可以看到 obj2 指向了和 obj1 的同一個記憶體位址。obj2 本身沒有再建立一個新的物件。

// Objects
var obj1 = {
    name: 'John',
    age: 26
};
var obj2 = obj1;
obj1.age = 30;
console.log(obj1.age);  //30
console.log(obj2.age);  //30

以下透過 function 來看看二者之間實際的差異:

// Functions
var age = 27;
var obj = {
    name: 'Jonas',
    city: 'Lisbon'
};

function change(a, b) {
    a = 30;
    b.city = 'San Francisco';
}

change(age, obj);

console.log(age);      //27
console.log(obj.city); //San Francisco

primitive age 永遠不會被 a 改變。但 object b 因為是參考,所以它本身的 city 內容被改變了。

Zeen is a next generation WordPress theme. It’s powerful, beautifully designed and comes with everything you need to engage your visitors and increase conversions.