一個系列: 一個列子看盡一個知識點
1. 首先我們來個簡單的,ES6中寫一個繼承
是不是非常的開心,這是在寫C#嗎?JS的繼承也如此簡單哦,那還看盡啥呢
class father {
}
class child extends father {
}
2. 但是這是ES6呢,在babel轉(zhuǎn)成ES5后的代碼呢,如下,我們來好好看下呢
"use strict";
// 獲取對象上面的屬性
var _get = function get(_x, _x2, _x3) {
var _again = true;
// 循環(huán)往上查找屬性
_function: while (_again) {
var object = _x, property = _x2, receiver = _x3;
_again = false;
if (object === null) // 為空就變回原來的親生父母Function調(diào)用
object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property); // 獲取屬性的描述,比如constructor的描述
if (desc === undefined) { // 沒有屬性描述,則往原型上找,就是小蝌蚪找媽媽
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
_x = parent;
_x2 = property;
_x3 = receiver;
_again = true;
desc = parent = undefined;
continue _function;
}
} else if ("value" in desc) { // 屬性描述里面的value就是這個屬性的指向,返回
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
}
};
// 類的繼承
function _inherits(subClass, superClass) {
// 判斷了一下類型檢查,否則拋錯
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
// 作為一個函數(shù)的原型屬性繼承!?。? // 以父類原型屬性為模板,創(chuàng)建對象給子類的原型屬性,同時把函數(shù)的constructor指向了自己
// 這樣子類通過這種方式繼承了父類的原型屬性,有上面的東西啦
// Object.create標明這里子類和父類的原型屬性不是同一個,新創(chuàng)建了一個
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { // 更改原有函數(shù)的constructor的指向,以后我生出的孩兒,都要知道我才是他們媽媽
value: subClass, // 你看,指向了自己了吧
enumerable: false, // 不可枚舉
writable: true, // 可寫
configurable: true // 可配置
}
});
// 作為一個對象的原型指向繼承?。?!
// 徹底改變子類原型,指向父類,標明我是父類創(chuàng)建噠, Object.setPrototypeOf這個是ES6的語法,等同于subClass.__proto__
// 那么如果這里不設(shè)置的話,我們可以看到subClass.__proto__ === Function.prototype, 還是由原生Function創(chuàng)建的
// 所以這里的作用就是徹底撇清和親生父母Function的關(guān)系,徹底投靠給了養(yǎng)父母SuperClass, 你通過__proto__查戶口本我也是superClass的兒子
// 當然subClass.__proto__.__proto__ === superClass.__proto__ === Function.prototype 這下你爺爺變成Function了
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
// 判斷是否是繼承此類的實例
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
// 1. 父類的函數(shù)聲明
var father = function father() {
_classCallCheck(this, father);
};
// 2. 子類的函數(shù)聲明
var child = (function (_father) {
// 3. 繼承
_inherits(child, _father);
// 4. 返回真實函數(shù)聲明
function child() {
// 5. 判斷調(diào)用者是否是當前實例
_classCallCheck(this, child);
// 6. 調(diào)用父類上的constructor構(gòu)造函數(shù)來創(chuàng)建實例,就是調(diào)用父類函數(shù)創(chuàng)建對象
// Object.getPrototypeOf(child.prototype) === child.prototype.__proto__ :這里是找原型屬性的原型,找到father啦
// 為什么能找到呢, Object.setPrototypeOf(subClass, superClass) , 就是這句話的效果撒
// 一開始以為是自己的原型,都快被繞暈了
_get(Object.getPrototypeOf(child.prototype), "constructor", this).apply(this, arguments);
}
return child;
})(father);
- 哇,好長,這個代碼還是得從最后看起,注意大概步驟分為6步,仔細看源碼注釋哦!
- 父類的函數(shù)聲明,這里很簡單了,原來類就是一個function啊,雖然我早就知道了,里面只有一句來驗證用father來創(chuàng)建對象的時候,this是不是father的實例
var father = function father() {
_classCallCheck(this, father);
};
- 子類的函數(shù)聲明,我們看最外面,變成一個閉包了喲,傳入的參數(shù)是father,因為我們指定是繼承father的嘛,所以在new Child()的時候需要father, 這里就把father先存起來
var child = (function (_father) {
_inherits(child, _father);
function child() {
_classCallCheck(this, child);
_get(Object.getPrototypeOf(child.prototype), "constructor", this).apply(this, arguments);
}
return child;
})(father);
- 繼承:上面說存起了father的嘛,存起來就是為了繼承的喲,就是這句
_inherits(child, _father);小小一句話是怎么實現(xiàn)的呢,看這個函數(shù)如下
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { // 更改原有函數(shù)的constructor的指向,以后我生出的孩兒,都要知道我才是他們媽媽
value: subClass, // 你看,指向了自己了吧
enumerable: false, // 不可枚舉
writable: true, // 可寫
configurable: true // 可配置
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
這里最重要的兩步,
- 作為一個函數(shù)的原型屬性繼承!??!
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { // 更改原有函數(shù)的constructor的指向,以后我生出的孩兒,都要知道我才是他們媽媽
value: subClass, // 你看,指向了自己了吧
enumerable: false, // 不可枚舉
writable: true, // 可寫
configurable: true // 可配置
}
});
以父類原型屬性為模板,創(chuàng)建對象給子類的原型屬性,同時把函數(shù)的constructor指向了自己,這樣子類通過這種方式繼承了父類的原型屬性,有上面的東西啦,Object.create標明這里子類和父類的原型屬性不是同一個,新創(chuàng)建了一個
- 作為一個對象的原型指向繼承?。?!
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
徹底改變子類原型,指向父類,標明我是父類創(chuàng)建噠, Object.setPrototypeOf這個是ES6的語法,等同于subClass.proto ,那么如果這里不設(shè)置的話,我們可以看到subClass.proto === Function.prototype, 還是由原生Function創(chuàng)建的,所以這里的作用就是徹底撇清和親生父母Function的關(guān)系,徹底投靠給了養(yǎng)父母SuperClass, 你通過proto查戶口本我也是superClass的兒子.當然subClass.proto.proto === superClass.proto === Function.prototype 這下你爺爺變成Function了
返回真實函數(shù)聲明,這句簡單,返回了child函數(shù),跟father一樣,還是一個函數(shù)呀
判斷調(diào)用者是否是當前實例,同樣跟父類一樣
調(diào)用父類上的constructor構(gòu)造函數(shù)來創(chuàng)建實例,就是調(diào)用父類函數(shù)創(chuàng)建對象
_get(Object.getPrototypeOf(child.prototype), "constructor", this).apply(this, arguments);
注意這里的Object.getPrototypeOf(child.prototype) 等價于 child.prototype.__proto__ 等價于 father.prototype , 這句明白了才好理解呢,想用this調(diào)用父類上的構(gòu)造函數(shù)來創(chuàng)建象,所以繼承后你創(chuàng)建一個對象,真正是一層一層往上調(diào)用構(gòu)造函數(shù)來給你創(chuàng)建對象滴,具體_get里怎么拿到構(gòu)造函數(shù)就看源碼啦,
3. 最后附上博主幸幸苦苦畫的繼承關(guān)系圖呢,是不是很清晰明了了呢,對了,這里還有js深層的繼承關(guān)系呢,所謂一生二,二生三,三生萬物,這里的一就是null啊,喜歡給個贊喲?。?!
