# The Weird Part Of Javascript - part 4
###### tags: `Javascript`
# Section 5 - Object-Oriented Javascript And Prototypal Inheritance
# Conceptual Aside: Classical vs Prototypal Inheritance
## Inheritance
> 一個物件可以取得 屬性以及方法 從另一個物件上面
## Classical Inheritance
是一種分享物件 屬性以及方法的一種方式
* 缺點是非常攏長
Verbose
* 其中的交錯非常複雜難以得知其中的物件如何影響其他的物件
作者舉的例子
就像是 你在裝潢家裡可是當你更換燈泡時,你的馬桶卻沖水了
* 有許多 API 需要使用
friend
protected
private
interface
## Prototypal Inheritance
作者介紹的優點:
Simple
flexible
extensible
easy to understand
# Understanding The Prototype
> 在 JS 裡面每一個物件都有 prototype 屬性
## Prototype Chain(原型鍊)

在 JS 裡面每一個物件都有 prototype 屬性,簡單來說就是一個其他物件的參考點也就是 proto
圖片的範例中 obj 想要使用 prop2 因為在 obj 內找不到,這時候就會透過 proto 這個參考點去尋找其他物件的屬性,直到找到 prop2, prop3 以此類推,**而這個一直往下找的過程就是 Prototype Chain**
所以可以理解到的是我們在尋找的 prop1 2 3 ,都不是在 obj 內找到的,而是透過 prototype 的那個 proto 物件,並且返回尋找的內容
然而我們如果要得到 prop3 的值,我們不需要寫成
`obj.proto.proto.prop3`
而是直接寫成
`obj.prop3`
JS 引擎會在背後幫我們搜尋 原型鍊
## 其他物件也想取得同樣的屬性以及方法
其他的物件想要取得 prop2 可以辦得到嗎?
可以,只要使用一樣的 proto 即可以取得一樣的物件屬性以及方法

## 程式碼範例
這邊為了展示 prototype 的繼承,直接使用了 __proto__ ,現實中因應效能問題請勿使用
```javascript=
var person = {
firstname:'Default',
lastname:'Default',
getFullName:function(){
return this.firstname + ' ' + this.lastname;
}
}
var john = {
firstname:'John',
lastname:'Doe'
}
// 別使用這邊的方法,只限 DEMO 使用
john.__proto__ = person;
// 這時候 john 就會繼承 person,如果這時候我想要使用 john本身沒有的屬性或是方法時,JS 引擎就會往 person 去找
console.log(john.getFullName());
// 就會引用 person 的 function 印出 John Doe
```
接下來有個問題
為何這邊 firstname 印出來是 John ,很簡單因為在 John 本身的物件他就找到此屬性了,因此就直接返回而不會再往下搜尋原型鍊
```javascript=
john.__proto__ = person;
console.log(john.firstname); // John
```
那個這樣的狀態呢?
因為在 jane 物件中找不到 lastname 這個屬性所以透過原型鍊的幫助抓到 person 的 Default
```javascript=
var jane = {
firstname = 'Jane'
}
jane.__proto__ = person;
console.log(jane.lastname); // Default
```
# Everything is an Object (or a Primitive)
> 這章主要利用 `__proto__` 來找到原型鍊的最底層, 會發現原來大家都一樣
```javascript=
var a = {};
var b = function () {};
var c = [];
```
a 為空的物件
b 為 function
c 為 array
* a 已經為最底層的物件
* b 使用 `__proto__` 兩次達到底層
* c 使用 `__proto__` 兩次達到底層
* 發現得到可以用的函式都是一樣的

> 從這裡可以證明所有的 物件 函式 陣列 都是物件,其他就是純值(primitive)
# Reflection and Extend
## Reflection
> 物件可以看著它自己並且可以監聽以及改變自己的屬性以及方法
```javascript=
var person = {
firstname:'Default',
lastname:'Default',
getFullName:function(){
return this.firstname + ' ' + this.lastname;
}
}
var john = {
firstname:'John',
lastname:'Doe'
}
john.__proto__ = person;
for(var prop in john){
console.log(prop+':'+john[prop]);
}
```
這邊印出的結果會是所有 john 內部目前的內容也就包含了 john 沒有的方法也就是 getFullName,不過它因為 `__proto__` 的關係也會從 person 去拿

但如果我只想要方法或是屬性只存在我要物件中呢?
這邊可以使用 hasOwnProperty 是個 object 內建的方法,可以辨別是否物件有此屬性有的話返回 true 沒有則 false 來執行這個判斷式
```javascript=
for (var prop in john) {
if (john.hasOwnProperty(prop)) {
console.log(prop + ': ' + john[prop]);
}
}
```
印出結果
可以看出就篩選掉了不是 john 的方法瞜

寫到這邊只會 `console.log()` 出真正有出現在 john 內的屬性或是方法 也就是 firstname, lastname ,這兩個屬性是真正存在 john 物件內部的
運用這樣的方式可以 reflect john 這個物件本身觀察它自己的屬性,以及是否是真的存在於 john 本身(像是 getFullName 就是不是),並且可以改變那些屬性甚至操作他們。
但是這樣的方法並不是 build-in 在 JS 內,所以很多的框架或是函式庫會寫它們自己的 reflection ,這邊會用 underscore.js 函式庫來作範例
## underscore 範例
在使用 underscore 的範例中,可以看出 john 直接繼承了 jane, jim 的內容,並且有延續 person 的 prototype
```javascript=
var john = {
firstname:'John',
lastname:'Doe'
}
var jane ={
address:'111 main street',
getFullName: function (){
return this.firstname+' '+ this.lastname
}
}
var jim ={
getFirstName: function (){
return this.firstName;
}
}
_.extend(john,jane,jim)
console.log(john);
// Object {
// firstname:'john',
// lastname: 'Doe',
// address:'111 main street',
// getFirstName: function,
// getFullName: function
// }
```
在 underscore 的 source code 只要是由這個 createAssigner 函式來處理
var length 的部分是 argument 的長度以上面的例子來看的話
1. `if (defaults) obj = Object(obj);` 這部分則是代表如果參數數量小於2 或是 obj 為空則直接返回物件本身,簡單來說就是沒有放入要被繼承的參數因此直接返回原本的物件
1. 進入第一個迴圈這邊印出除了 john 以外的其他參數(因為它index 使用從 1 開始)
1. 並在其中的第二個環圈印出其他參數的屬性使用 keysFunc
1. l = key.length 則幫助第二個迴圈印出所有的屬性
1. 並且第二個迴圈會判斷是否 屬性為空 如果不為空則把其他屬性指派給 john
1. 最後返回物件
```javascript=
function createAssigner(keysFunc, defaults) {
return function(obj) {
var length = arguments.length;
if (defaults) obj = Object(obj);
if (length < 2 || obj == null) return obj;
for (var index = 1; index < length; index++) {
var source = arguments[index],
keys = keysFunc(source),
l = keys.length;
for (var i = 0; i < l; i++) {
var key = keys[i];
if (!defaults || obj[key] === void 0) obj[key] = source[key];
}
}
return obj;
};
}
```
# Section 6: Building Objects
## new key word 以及 function constructor
雖然有更新的方式可以創造物件,但這邊我們要先了解這兩個創造物件的方法,畢竟他們也流傳很廣,並且使用正確的方式設定 prototype
```javascript=
function Person() {
console.log(this);
this.firstname = 'john';
this.lastname = 'doe';
console.log('This function is invoked');
}
var john = new Person();
console.log(john);
// Person{firstname:'john', lastname:'doe'}
```
然而,其實這個 new key word 是個 operator

當 new 出現的時候,一個空的物件出現並且他後面呼叫了 Person 函式,並且藉著 Person 函式把 firstname/lastname 加進去那個空物件中(this 指向空物件),並且 Person 函式並沒有 return value ,因此就返回了空的物件(但現在有被加進內容了)
並且要證明這個函式有被呼叫的方法很簡單就是 log 出內容即可

那我們印出最開始的 this 是什麼(未操作前)會發現其實就是空的物件

那如果本來的 Person function 有返回值則擋住擋住上面 this 指派給 new 空物件的內容直接替換成返回值的內容
```javascript=
function Person() {
console.log(this);
this.firstname = 'john';
this.lastname = 'doe';
console.log('This function is invoked');
return { greeting :'i got in the way'};
}
var john = new Person();
console.log(john);
// Person{firstname:'john', lastname:'doe'}
```
可以看到 return 內容直接取代上面 this 的操作

如果我們在創建一個 jane 會生什麼事呢?
```javascript=
var jane = new Person();
```
它就會在產出一次一樣的內容

所以如果我想要透過創建人物的時候可以操作其姓名怎麼處理呢?
帶入參數
```javascript=
function Person(firstname,lastname) {
console.log(this);
this.firstname = firstname;
this.lastname = lastname;
console.log('This function is invoked');
return { greeting :'i got in the way'};
}
var john = new Person('john', 'doe')
console.log(john)
var jane = new Person('jane', 'doe')
console.log(jane)
```
印出結果
就是我們帶進去的參數摟

## Big word alert: Function Constructors
> 就是一個普通的 function 不過他的功能是用來創建物件的內容為其加入屬性或是方法
也就是我們剛剛看到的 `Person()` 這個就是 function constructor ,會來搭建前面 new 出來的空物件的內容藉由當 excution context 的 creation phase 時都會創造的 this 變數來達成
```javascript=
var john = new Person()
```
並且因為那個 function constructor 並沒有返回值所以會返回那個被設置好的空物件,當 function constructor 執行結束時
# Function Constructors and '.prototype'
當你使用 function constructors 時,它就會自動設定好 prototype 給你
每個 function 都有 prototype 這個屬性可是只有當它是使用 new operator 的這個 function constructors 創造時才會啟用它(會以一個空物件的形式被創建)
然而 `__proto__` 這個屬性則是每個物件都有,他們兩個是不相同的!

所以我使用它的方式如下:
直接對 Person 使用 .prototype 並且添加上去需要的方法或是屬性,就可以直接套用到 john 或是 jane 上面非常的方便,此時 Person.prototype 就是他們的 prototype
```javascript=
function Person(firstname,lastname) {
console.log(this);
this.firstname = firstname;
this.lastname = lastname;
console.log('This function is invoked');
return { greeting :'i got in the way'};
}
Person.prototype.getFullName = functoin(){
return this.firstname+ ' ' + this.lastname;
}
var john = new Person('john', 'doe')
console.log(john)
// 印出的 john 會包含 getFullname 方法
var jane = new Person('jane', 'doe')
console.log(jane)
// 印出的 jane 會包含 getFullname 方法
```
甚至可以在創造好 john, jane 之後再添加方法或是屬性上去都是可以的!
```javascript=
Person.prototype.getFormalFullName =function() {
return this.lastname + ', ' + this.firstname;
}
console.log(john.getFormalFullName());
```
這邊就會直接印出剛剛新增上去的方法
Doe, John
從這邊可以理解到所有我使用 function constructors 創造的物件,我都可以在創造之後的任意時間點新增方法上去藉著使用 .prototype 這個屬性
所以如果我創造了 1000 個人,如果我把方法寫在 Person 裏面,那麼那行重複的程式碼就會重複被創造 1000 遍一樣的程式碼非常佔空間
因此可以藉由 .prototype 給他們增加方法只要使用這行程式碼即可,可以節省很多記憶體空間
這就是你使用 function constructors 來使用 .portotype 的方式
# Dangerous Aside: 'new' and functions
危險的地方是如果在使用 function constructor 時忘記加上 new 關鍵字
因為 new 關鍵字是 Person function 返回的值被拿走了,所以只會得出 undefined
```javascript=
var john = Person('john', 'doe')
console.log(john) // undefined
Person.prototype.getFormalFullName =function() {
return this.lastname + ', ' + this.firstname;
}
console.log(john.getFormalFullName());// 會直接報錯
```
所以我們用來使用當作 function constructor 的 function 會以大寫字母開頭命名比方說 `Person()` 當我們看到報錯時並且有大寫的函式名稱可能就是忘記加上 new 關鍵字摟
雖然有新的創造物件的方法出現,不過要讀懂前人的扣這部分還是必須理解
# Conceptual Aside: Built-In Function Constructors
這邊會介紹一些內建在 JS 引擎內部的 function constructor
```javascript=
var a = new Number(3);
console.log(a)
```
我們操作在瀏覽器裡的 console 會得到 a 不是 number 而是物件,因為 function constructor 創造的是物件!

既然創造出來的 a 是由 function constructor 創造的物件那麼它自然就會有 prototype 屬性可以使用

並且可以發現有很多已經被寫好可以使用的方法在裡面
所以我可以操作我剛剛設置好的 a 使用 tofixed 方法得出他的小數點後兩位
```javascript=
a.toFixed(2) // "3.00"
```
---
這邊我們來試試看不同的方法
```javascript=
var a = new String('john')
console.log(a)
```
這邊一樣會 log 出來的內容是物件

並且 a 也有 prototype 設置好的方法可以使用(因為它指向 String ),這些方法是附加在 String 這個 function constructor 內

---
有時候 JS 引擎可以直接理解字串的輸入(儘管不是使用 function constructor)
把它包進去 String 物件裡面因此它可以正常使用 prototype 屬性的方法
```javascript=
"john".length()
```
這兩個是一樣的
```javascript=
new String('john').length
```

使用 number 的話則會報錯摟,他也不能使用新增的方法必須乖乖使用 function constructor 才能

這邊我們嘗試對所有的 String 物件新增方法
```javascript=
String.prototype.isLengthGreaterThan = function(limit){
return this.length > limit
}
console.log("john".isLegnthGreaterThan(3)) // true
```
這邊發生了什麼事呢?
這邊的 "john" 自動地被辨別成 String 物件,並且我在上方給所有的 String 新增了方法 isLengthGreaterThan,因此所有的 String 都可以使用,因此 "john" 也可以使用
很多的函式庫以及框架也都利用這樣的方法來新增新的功能,不過使用這個方法的時候要注意不要複寫其他已經在裡面的功能