Classical Inheritance vs. Modern Inheritance
JavaScript 沒有 class 的概念,使用 new 雖然很像 class 的語法,但 constructor function 仍舊只是個 function。
- Classical pattern:以 class 的想法模擬出繼承(inheritance)的方式
 
- Modern pattern:不思考 class 的方式(應盡量選擇此 pattern)
 
Classical Inheritance
目的:使 Child() 所建立的 instance 可以取得 Parent() 的 property
1 2 3 4 5 6 7 8 9 10 11 12 
  | function Parent(name) { 	this.name = name || "Father"; } Parent.prototype.getName = function () { 	return this.name; }; function Child(name) {} inherit(Child, Parent); 
  | 
 
#1 The Default Pattern
1 2 3 4 5 6 
  | function inherit(C, P) { 	C.prototype = new P();  } var kid = new Child(); kid.getName();  
  | 
 
- 缺點:同時繼承了 
this 的 property 與 prototype 的 property,通常 this 的 property 是屬於 instance 專屬的,所以並不希望這些被繼承,而我們會把可 reuse 的 member 放在 prototype 中。 
- 缺點:無法透過 child 的 constructor 傳遞參數給 parent 的 method
 
#2 Rent a Constructor
1 2 3 4 5 6 7 
  | function Child(name) { 	Parent.apply(this, arguments);  } var kid = new Child("Son"); kid.name;  typeof kid.getName;  
  | 
 
- 優點:解決 #1 的第二個缺點,且將 parent 中 
this 的 property 複製到 child,不會有覆寫的風險 
- 缺點:prototype 的 property 都沒被繼承
 
#3 Rent & Set Prototype
1 2 3 4 5 6 7 8 9 10 
  | function Child(name) { 	Parent.apply(this, arguments); } Child.prototype = new Parent(); var kid = new Child("Son"); kid.name;  kid.getName();  delete kid.name; kid.getName();  
  | 
 
- 優點:child 繼承了 parent 自身 member 的複製,也繼承了 parent 的 prototype(即 #1 與 #2 的組合)
 
- 缺點:
Parent() 被呼叫了兩次,效率較差 
#4 Share the Prototype
1 2 3 
  | function inherit(C, P) { 	C.prototype = P.prototype; } 
  | 
 
- 優點:不會呼叫 
Parent() 兩次 
- 缺點:修改 prototype 後會影響繼承的所有 object
 
#5 Temporary Constructor
1 2 3 4 5 
  | function inherit(C, P) { 	var F = function () {}; 	F.prototype = P.prototype; 	C.prototype = new F(); } 
  | 
 
#最終版
1 2 3 4 5 6 7 8 9 
  | var inherit = (function () {  	var F = function () {}; 	return function (C, P) { 		F.prototype = P.prototype; 		C.prototype = new F(); 		C.uber = P.prototype;  		C.prototype.constructor = C;  	} }()); 
  | 
 
Modern Inheritance
概念:object 繼承自其他 object,即從 parent object 取得功能以建立 child object。
Prototypal Inheritance
1 2 3 4 5 6 7 8 9 10 11 12 
  | function object(o) { 	function F() {} 	F.prototype = o; 	return new F(); } var parent = { 	name: "Father" }; var child = object(parent); console.log(child.name);  
  | 
 
不一定要用 object literal,也可用 constructor function:
1 2 3 4 5 6 7 8 9 10 
  | function Parent() { 	this.name = "Father"; } Parent.prototype.getName = function () { 	return this.name; }; var kid = object(new Parent());  kid.getName();  
  | 
 
在 ECMAScript 5 中,不需自己實作 object(),可直接用 var child = Object.create(parent);。
Inheritance by Copying Properties
此 pattern 沒有複製到 prototype,只有自身 property。
淺層複製(不檢查是否為 object 或 array,只複製到 reference)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 
  | function extend(parent, child) { 	var i; 	child = child || {}; 	for (i in parent) { 		if (parent.hasOwnProperty(i)) { 			child[i] = parent[i]; 		} 	} 	return child; } var parent = {name: "Father"}; var child = extend(parent); child.name;  
  | 
 
深層複製(將 object 或 array 也複製)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 
  | function extendDeep(parent, child) { 	var i, 	    toStr = Object.prototype.toString, 	    astr = "[object Array]"; 	    child = child || {}; 	for (i in parent) { 		if (parent.hasOwnProperty(i)) { 			if (typeof parent[i] === "object") { 				child[i] = (toStr.call(parent[i]) === astr) ? [] : {}; 				extendDeep(parent[i], child[i]); 			} 			else { 				child[i] = parent[i]; 			} 		} 	} 	return child; } var parent = {list: [1, 2, 3, 4]}; var child = extendDeep(parent); child.list.push(5); child.list.toString();  parent.list.toString();  var child2 = extend(parent); child2.list.push(5); child2.list.toString();  parent.list.toString();  
  | 
 
Borrowing Method
只使用想用的 method,不真的繼承所有 property。使用 call() 與 apply() 實作:
1 2 3 
  | obj1.someFn.call(obj2, p1, p2, p3);  obj1.someFn.apply(obj2, [p1, p2, p3]); 
  |