[課程筆記]克服JS的奇怪部分-CH4 Javascript 的物件導向與原形繼承

Udemy課程連結
課程總目錄

本篇筆記目錄:

觀念小叮嚀:古典和原型繼承
U1 瞭解原型
U2 所有東西都是物件(或純值)
U3 Reflection 與 Extend

觀念小叮嚀:古典和原型繼承

Inheritance 繼承

一個物件取用另一個物件的屬性和方法

那古典繼承和原型繼承是什麼呢?

古典繼承

古典繼承在 C#Java 裡都有,而且非常熱門。

古典繼承裡面有非常多方法可以用,也很口語化,
他們互相繼承別人的屬性、方法,然後還有很多不同的關鍵字friendprotectedprivateinterface之類的,我們需要學習那些是什麼意思並且使用他們,沒有不好,但講者不喜歡。

原型繼承

比古典繼承簡單多了,具備彈性、可擴充性、簡單易懂

古典和原型繼承各自都有他的好壞,所以並沒有一定,所以當有人在講繼承時,就是在講
一個物件取用另一個物件屬性或方法。


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= [];
//[]

得到原型陣列,一樣有常見的屬性和方法,像是indexOfforEach

結論: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

這個概念也讓我們可以實做一件很有用的事補足原型繼承