iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 16
0
Modern Web

JavaScript 忍者的修練--從下忍進階到中忍系列 第 16

Day 16: Constructor function 的原型機制

  • 分享至 

  • xImage
  •  

函式原型與物件原型

除了使用 object literal 建立物件,前面我們也用過 constructor function 先定義物件的預設狀態,再用new關鍵字建立新的物件 instance。JavaScript 函式也是物件,當然也會有它的原型規則。讓我們來看一看在 constructor 作用時的原型規則。

當建立一個函式的時候,同時也會建立一個原型物件,如果我們使用這個函式當做 constructor 建立新物件的話,新物件的原型就會是函式的原型。

// 定義一個 Cat constructor function
// 同時會自動建立函式原型 Cat.prototype
function Cat() {}

// 我們可以更改函式原型
Cat.prototype.meow = function() {
	return true;
};

// 使用 constructor function 建立新物件
var kitty = new Cat();

// 新物件可取用函式原型裡的方法
console.log(kitty.meow()); // true

物件和函式是怎麼記住它們的原型是誰呢?前一篇文章提過,物件有個內在屬性[[prototype]]記錄它的原型對象,每當搜尋不到某個屬性時,就會把搜尋的工作交給原型物件來做,物件之間透過這條鏈結串在一起,叫做 prototype chain。

建立函式時,除了前面提過一些初始化的程序(如 excution context, lexical environment, arguments, this)之外,還會自動產生一個prototype屬性,並且指向一個同樣也是自動產生的原型物件。JavaScript 讓我們能用這個屬性變更函式原型內容。

這個函式原型會有一個屬性constructor,指向本來的函式。函式和函式原型之間就是靠著prototypeconstructor這二個屬性在連結彼此的關係。

當我們把這個函式當成 constructor 建立的新物件的時候,新物件的原型便會指向函式的原型。

以上就是 constructor function 原型的運作模式。

這樣做有什麼好處呢?JavaScript 沒有真正類別(class)的設計,constructor function 是最接近的方法,JavaScript 用它來模擬類別的效果。因為由 constructor function 所建立的新物件,物件原型都是函式原型,當我們在函式原型裡增加方法,所有的物件都可以使用,如此一來就做到了同一類別的物件共用程式碼的效果。

function Cat() {
	this.meow = function() {
		return true;
	}
}

// 建立一個 kitty 物件,擁有 meow 方法
var kitty = new Cat();
console.log(kitty.meow()); // true

// 在函式原型新增一個 purr 方法
Cat.prototype.purr = function() {
	return true;
};

// 建立一個 doraemon 物件,同時擁有 purr 和 meow 方法
var doraemon = new Cat();
console.log(doraemon.purr()); // true
console.log(doraemon.meow()); // true

// 透過物件原型,kitty 也共用 purr 方法
console.log(kitty.purr()); // true

Instance property

由 constructor 建立的新物件在術語裡叫做 instance,電腦對 instance 的具體作法是撥出記憶體的空間存放它們,一個 instance 有一個記憶體空間,二個 instances 有二個記憶體空間,等等。所以 instance 之間才能各自獨立運作。而 constructor function 裡this關鍵字便是代表 instance,所以凡是this下面的屬性,在每一個 instance 裡都會複製一份,是屬於 instance 自己的屬性(instance property)。

如果同時又在函式原型裡建立了相同名稱的屬性,依照 prototype chain 原則,在 instance 上就找到了,不需要到原型去搜尋。

function Cat() {
	this.meow = function() {
		return false;
	}
}

// 在原型建立一個和 constructor function 裡一樣名稱的方法
Cat.prototype.meow = function() {
	return true;
};

// 在 instance 裡的方法會優先於在原型裡的同名方法
var kitty = new Cat();
console.log(kitty.meow()); // false

Instance 裡的屬性愈多,需要的記憶體空間也就愈大,在建立大量物件時如果方法的行為都是一樣,可以不用建在 constructor fuction 裡,因為會複製到每一個物件,浪費記憶體。讓方法建在函式原型裡,效果是一樣的。

修改原型

JavaScript 除了讓我們能新增原型屬性外,也可以刪除、修改它們,不論是函式原型.prototype或是物件原型[[prototype]](使用Object.setPrototypeOf)。

function Cat() {
	this.meow = function() {
		return true;
	}
}
var kitty = new Cat();
Cat.prototype.purr = function() {
	return true;
};

// 用新物件及方法替換舊的原型
Cat.prototype = {
	hiss: function() {
		return true;
	}
}

// 新的 instance 擁有對新原型的參照,但無法取得舊原型方法
var doraemon = new Cat();
console.log(doraemon.meow()); // true, instance 屬性依然存在
console.log(doraemon.hiss()); // true
console.log(doraemon.purr); // undefined, 找不到舊原型屬性,因為已經替換了

// 原有的 instance 保有對舊原型的參照,但無法取用新原型方法
console.log(kitty.meow()); // true, instance 屬性依然存在
console.log(kitty.purr()); // true, kitty 仍然存有舊原型的參照
console.log(kitty.hiss); // undefined, 新原型上的屬性,不在 kitty 的原型鏈上

在範例中我們用新的物件和方法換掉舊有的Cat.prototypemeow因為是物件屬性不會受到原型影響。

doraemon.hiss()可以用因為它是替換原型後才建立的物件,具有對新原型的參照。但是doraemone卻無法參照到已經不在的舊原型方法purr

然而舊的函式原型並沒有消失,因為舊的物件kitty仍然保有對它的參照,kitty.purr()仍然可以使用。

constructor 屬性

要檢查一個物件是否由某個 constructor function 所建立的,我們可以用instanceof

function Cat() {};
var kitty = new Cat();
console.log(kitty instance of Cat); // true

前面提過,建立函式時同時會建立一個函式物件,並且函式的屬性prototype會指向這個物件。在函式物件裡也會有constructor屬性指回函式,這個屬性在 instance 裡也可以存取得到,因此我們就能檢查物件是由哪一個 constructor 所建立的。

既然這個屬性指向的是一個 constructor function,我們當然可以用它來建立新的物件。換句話說即使我們無法存取原本的 constructor function,只要可以找得到參照,就可以拿來用,效果一模一樣。這就是 JavaScript 自由的地方。

function Cat() {}
var kitty = new Cat();
var doraemon = new kitty.constructor();

console.log(kitty instanceof Cat); // true
console.log(doraemon instanceof Cat); // true

上一篇
Day 15: 物件原型
下一篇
Day 17: 物件繼承
系列文
JavaScript 忍者的修練--從下忍進階到中忍30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言