今天要介紹在JavaScript中會看到的「兩種原型」,這兩種原型都叫做prototype,但他們卻有完全不同的意思。
第一種的prototype為「原型關係」,一個物件為是另一個物件的原型,如以下例子:
//宣告一個物件
var objA = {
name:'A',
};
//讓objB繼承他
var objB = Object.create(objA,{name:{value:'B'}});
//objB的原型為objA
Object.getPrototypeOf(objB) === objA; //會回傳true
//使用isPrototypeOf檢查
objA.isPrototypeOf(objB); //會回傳true
此時objB的內容會是:
他的__photo__屬性會有繼承而來的特性,但如果我們將這個屬性指定成其他物件:
//宣告另一個物件
var objC = {
name:'C',
};
//把objB的__proto__指向objC
objB.__proto__ = objC;
//再去檢查會發現objB的prototype已經由objA變成objC了
Object.getPrototypeOf(objB) === objC; //會回傳true
//使用isPrototypeOf檢查
objC.isPrototypeOf(objB); //會回傳true
此時objB的內容會是如下:
第二種為特性的prototype值,每個建構器都會有一個prototype的特性,他的值是一個物件,而透過這個建構器產生的實體原型都會指向這個建構器的prototype,如以下例子:
//建立一個空function
function func(){};
//把func當做建構器來創造一個實體物件
var obj = new func();
//obj這個實體物件的原型會指向建構器的prototype特性
Object.getPrototypeOf(obj) === func.prototype; //會回傳true
//接著使用isPrototypeOf檢查
func.prototype.isPrototypeOf(obj); //會回傳true
//然後藉由建構器產生的實體物件會有constructor屬性,所以接著來判斷
obj.constructor == func; //回傳true
obj instanceof func; //回傳true
來看看obj的內容如何:
他在__proto__中的constructor指向建構器本身,上一篇我們知道constructor屬性可以修改,這一次我們來修改__proto__看看:
//新增一個新建構器
function func1(){};
//把obj的__proto__屬性指向新建構器的prototype屬性
obj.__proto__ = func1.prototype;
//這時候obj的原型就會從func完全變成func1了
obj.constructor == func; //就不等於原本的建構器了,會回傳false
obj.constructor == func1; //會回傳true
//接著使用instanceof、isPrototypeOf及Object.getPrototypeOf檢查
obj instanceof func1; //回傳true
func1.prototype.isPrototypeOf(obj); //會回傳true
Object.getPrototypeOf(obj) === func1.prototype; //會回傳true
這些例子真的越試越搞混,不過可以看到不論試用何種方式去做原型的繼承或指定,最後都會存在物件中的__proto__屬性中,所以只要改變了該屬性就算是建構器的實體constructor特性也會跟著改變!!
那既然使用__proto__就可以更改原型,那他又和prototype有什麼關係呢?我們可以把他理解成prototype是屬於父母身上的基因,而他在做繼承這個動作的時候會把prototype中的基因放進孩子中的__proto__裡面,讓他也擁有相同的基因存在,不過JS的物件繼承真的是一個很難釐清的部分,說不定以後更了解了會再回來更新這篇文章,如果有說明錯誤或不明白的地方,麻煩再留言告知我,我會盡速改正!!
原來 prototype 還有兩種,
看完這篇更清楚了這段程式的用意 obj.__proto__ = func1.prototype;
,
也明白了建構器 prototype 屬性的用途,
我覺得你形容 prototype 是父母身上的基因還蠻貼切的,哈哈