[課程筆記]克服JS的奇怪部分-CH4 Javascript 的物件導向與原形繼承
本篇筆記目錄:
U1 瞭解原型
U2 所有東西都是物件(或純值)
U3 Reflection 與 Extend
觀念小叮嚀:古典和原型繼承
Inheritance 繼承
一個物件取用另一個物件的屬性和方法
那古典繼承和原型繼承是什麼呢?
古典繼承
古典繼承在 C#
、Java
裡都有,而且非常熱門。
古典繼承裡面有非常多方法可以用,也很口語化,
他們互相繼承別人的屬性、方法,然後還有很多不同的關鍵字friend
和protected
和private
和interface
之類的,我們需要學習那些是什麼意思並且使用他們,沒有不好,但講者不喜歡。
原型繼承
比古典繼承簡單多了,具備彈性、可擴充性、簡單易懂
古典和原型繼承各自都有他的好壞,所以並沒有一定,所以當有人在講繼承時,就是在講
一個物件取用另一個物件屬性或方法。
U1 瞭解原型
原型(prototype)
JavaScript中的所有物件(包含函數),都有原型屬性,這個屬性會參考到另一個物件:proto

屬性物件proto是它的原型,這是會被obj參考到而且取用屬性和方法的物件,他有些屬性,例如prop2。執行obj.prop2時,點運算子會尋找prop2,在obj找不到,往原型找,然後找到它、回傳他
相同的,原型物件也可以指向另一個物件,每個物件可以有自己的原型,所以這個原型可以有另一個屬性prop3,雖然prop3看起來在我們的主要物件上,但其是是在一個原型鏈(prototype chain)上。
另外,如果我有另一個物件 obj2,他也可以指向同一個原型,假設我呼叫 obj2.prop2,也可以得到一樣的屬性,和 obj.prop2的記憶體位置一樣,雖然共享一個屬性,但不是直接的,而是藉由原型鏈觀念。
下面的會使用__proto__
來直接取用原型,但一般情況不要使用,會大幅減緩程式速度
var person={
firstname:'Default'
lastname:'Default'
getFullName:function(){
return this.firstname+' '+this.lastname;
}
}
var john={
firstname:'John',
lastname:'Doe'
}
//don't do this EVER!for demo purposes only
john.__proto__ =person;
console.log(john.getFullName());
//John Doe
所有物件都有別的物件的參考,我們設定 john 物件的原型到 person 物件,John 繼承自 person,如果我要取用一個John屬性和方法,他不在John上,他會到person上找,如果也不在person上找,他會在鏈上找到。
我們需要注意另外一件重要的事,this
變數會知道我們要用的物件是哪個,this
不會參考到 person
,會參考到互叫他的函數john
。
再看看另一種情況,我們把 Jane 的 proto指向 person:
var person={
firstname:'Default'
lastname:'Default'
getFullName:function(){
return this.firstname+' '+this.lastname;
}
}
var john={
firstname:'John',
lastname:'Doe'
}
var jane={
firstname:'John',
}
john.__proto__=person;
jane.__proto__=person;
console.log(jane.getFullName());
//JaneDefault
john 和 jane 會指向同一個記憶體位置(person物件),firstname 在 jane 中找得到,lastname 在 jane 中找不到,改到 person 去找,所以輸出結果JaneDefault
。
U2 所有東西都是物件(或純值)
JS 的所有東西若不是純值(數值、字串、布林),他們(函數、陣列、一般物件)都有原型,除了基本物件(base object)
所有東西都有原型,範例:
1.物件
var a={};
console.log(a.__proto__)
//Object{}
我們會得到基本物件,在原型鏈上很底層,而這個基本物件有屬性與方法,像是toString
2.函數
var b= function;
//function Empty(){}
得到函數的原型,所有我們建立的函數都有這個原型,當然也有相關的屬性與方法(apply、call、bind)。
3.陣列
var c= [];
//[]
得到原型陣列,一樣有常見的屬性和方法,像是indexOf
、forEach
結論:JavaScript 所有物件、所有陣列及所有函數都有原型
原型的原型是什麼?物件,是內建的核心物件,在原型鏈最下面
a.__proto__proto
//Object
b.__proto__proto
//Object
c.__proto__proto
//Object
U3 Reflection 與 Extend
Reflection
一個物件可以看到自己的東西,然後改變自己的屬性和方法
var person={
firstname:'Default',
lastname:'Default',
getFullName:function(){
return this.firstname+''+this.lastname;
}
}
var john={
firstnmae:'John',
lastname:'Doe'
}
john._proto_=person;
for(var prop in john){
console.log(prop+':'+john[prop]);
}
//firstname:John
//lastname:Doe
//getFullName:function(){
// return this.firstname+''+this.lastname
//}
為什麼會有getfullname呢?
因為 for in 不只是在物件本身取用屬性和方法,還會到原型上找
如果我只是想要知道物件有什麼該怎麼做呢?
我們可以使用基本物件的屬性 hasOwnProperty
...
for(var prop in john){
if(john.hasOwnProperty(prop)){
console.log(prop+':'+john[prop])
}
}
Extend
這個概念也讓我們可以實做一件很有用的事補足原型繼承