經常瀏覽各種文章,學習各種技術,看了,不用,忘了.
好記性,不如爛筆頭!
繼承概念:
繼承機制例項
說明繼承機制最簡單的方式是,利用一個經典的例子 - 幾何形狀。實際上,幾何形狀只有兩種,即橢圓形和多邊形。圓是橢圓的一種,它只有一個焦點。三角形、矩形和五邊形都是多邊形的一種,具有不同數量的邊。這就構成了一種完美的繼承關係。
在這個例子中,形狀 是橢圓形和多邊形的基類(base class)(所有類都由它繼承而來)。圓形繼承了橢圓形,因此圓形是橢圓形的子類(subclass),橢圓形是圓形的超類(superclass)。同樣,三角形、矩形和五邊形都是多邊形的子類,多邊形是它們的超類。最後,正方形(Square)繼承了矩形。
下面的圖示是解釋 Shape 和它的子類之間關係的 圖示:
在講解繼承之前先了解一下JavaScript中建立一個物件的過程
function Person(name){
//呼叫new的過程相當於 var this=new Object();
//給this物件賦值
//最後return this;
this.name=name;
};
Person.prototype.getName=function(){
return this.name;
};
var a=new Person("seven");
console.log(a.name); //輸出:seven
console.log(a.getName); //輸出:seven
console.log(Object.getPrototypeOf(a)===Person.prototype); //輸出:true
複製程式碼
javaScript的函式可以被當做普通函式呼叫,也可以作為構造器呼叫.
javaScript中的原型繼承
原型程式設計的規則:
- 1.所有資料都是物件
- 2.要得到一個物件不是例項化類,而是找到一個物件作為原型並克隆ta
- 3.物件會記住它的原型(__proto__)
- 4.如果物件無法響應某個請求,ta會把這個請求委託給自動的建構函式的原型
繼承的方式
ECMAScript 實現繼承的方式不止一種。這是因為 JavaScript 中的繼承機制並不是明確規定的,而是通過模仿實現的。這意味著所有的繼承細節並非完全由解釋程式處理。作為開發者,你有權決定最適用的繼承方式。
物件冒充
因為建構函式只是一個函式,所以可使 ClassA 建構函式成為 ClassB 的方法,然後呼叫它。ClassB 就會收到 ClassA 的建構函式中定義的屬性和方法。例如,用下面的方式定義 ClassA 和 ClassB:
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function () {
alert(this.color);
};
}
function ClassB(sColor) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
}複製程式碼
在這段程式碼中,為 ClassA 賦予了方法 newMethod(請記住,函式名只是指向它的指標)。然後呼叫該方法,傳遞給它的是 ClassB 建構函式的引數 sColor。最後一行程式碼刪除了對 ClassA 的引用,這樣以後就不能再呼叫它。
所有新屬性和新方法都必須在刪除了新方法的程式碼行後定義。否則,可能會覆蓋超類的相關屬性和方法:
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}複製程式碼
為證明前面的程式碼有效,可以執行下面的例子:
var objA = new ClassA("blue");
var objB = new ClassB("red", "John");
objA.sayColor(); //輸出 "blue"
objB.sayColor(); //輸出 "red"
objB.sayName(); //輸出 "John"複製程式碼
可以使用Call方法優化一下
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function () {
alert(this.name);
};
}複製程式碼
這種方式只是ClassB呼叫了ClassA,給ClassB的this賦值
原型鏈(prototype chaining)
function ClassA() {
}
ClassA.prototype.color = "blue";
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
複製程式碼
原型方式的神奇之處在於最後一行程式碼。這裡,把 ClassB 的 prototype 屬性設定成 ClassA 的例項。因為想要 ClassA 的所有屬性和方法,但又不想逐個將它們 ClassB 的 prototype 屬性。還有比把 ClassA 的例項賦予 prototype 屬性更好的方法嗎?
呼叫 ClassA 的建構函式,沒有給它傳遞引數。這在原型鏈中是標準做法。要確保建構函式沒有任何引數。
與物件冒充相似,子類的所有屬性和方法都必須出現在 prototype 屬性被賦值後,因為在它之前賦值的所有方法都會被刪除。為什麼?因為 prototype 屬性被替換成了新物件,新增了新方法的原始物件將被銷燬。所以,為 ClassB 類新增 name 屬性和 sayName() 方法的程式碼如下:
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "";
ClassB.prototype.sayName = function () {
alert(this.name);
};
複製程式碼
可通過執行下面的例子測試這段程式碼:
var objA = new ClassA();
var objB = new ClassB();
objA.color = "blue";
objB.color = "red";
objB.name = "John";
objA.sayColor();
objB.sayColor();
objB.sayName()複製程式碼
混合方式
建立類的最好方式是用建構函式定義屬性,用原型定義方法。這種方式同樣適用於繼承機制,用物件冒充繼承建構函式的屬性,用原型鏈繼承 prototype 物件的方法。用這兩種方式重寫前面的例子,程式碼如下:
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function () {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);//物件冒充
this.name = sName;
}
ClassB.prototype = new ClassA();//原型
ClassB.prototype.sayName = function () {
alert(this.name);
};複製程式碼
在 ClassB 建構函式中,用物件冒充繼承 ClassA 類的 sColor 屬性。用原型鏈繼承 ClassA 類的方法。
ES6 實現了Class,可以通過關鍵字extends實現繼承
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y); this.color = color; // 正確
}
}複製程式碼
轉換成es5
"use strict";
function _possibleConstructorReturn(self, call) {
return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
function _inherits(subClass, superClass) {
subClass.prototype = Object.create(
superClass && superClass.prototype,
{
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
}
);
if (superClass) {
Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
}
var Point = function Point(x, y) {
this.x = x;
this.y = y;
};
var ColorPoint = function (_Point) {
_inherits(ColorPoint, _Point);
function ColorPoint(x, y, color) {
var _this = _possibleConstructorReturn(this, (ColorPoint.__proto__ || Object.getPrototypeOf(ColorPoint)).call(this, x, y));
_this.color = color; // 正確
return _this;
}
return ColorPoint;l
}(Point);複製程式碼
這裡重點講解一下ES5中的繼承
1. _inherits,首先使用Object.create將父類Point原型拷貝到子類ColorPoint上
2.將父類建構函式掛載到子類的__proto__ 上
非建構函式的繼承
比如,現在有一個物件,叫做"中國人"。
var Chinese = {
nation:'中國'
};複製程式碼
還有一個物件,叫做"醫生"。
var Doctor ={ career:'醫生' }複製程式碼
請問怎樣才能讓"醫生"去繼承"中國人",也就是說,我怎樣才能生成一個"中國醫生"的物件?
可以利用空建構函式:
function extends(o) {
function F() {}
F.prototype = o;
return new F();
}
var Doctor = object(Chinese);
Doctor.career = '醫生';
複製程式碼
也可以使用Object.create()
var Doctor = Object.create(Chinese);
Doctor.career = '醫生';複製程式碼
到此結束!