自從有了babel這一個利器以後,es6如今已經被普遍的使用。JavaScript 類實質上是 JavaScript 現有的基於原型的繼承的語法糖。類語法不會爲JavaScript引入新的面向對象的繼承模型。也就是說babel不管是實現類,仍是實現繼承,本質上都是基於原型及原型鏈的,那我咱們就順這這個思路,一步一步往下走,直到揭開babel是如何實現類及類的繼承的。javascript
javascript中原型的概念比較抽象,不是很好理解,咱們仍是老老實實上代碼,用代碼來舉例說明。前端
function Person (name, age, job) {
this.name = name;
this.age = age;
this.job = job;
}
let p1 = new Person('張三', 18, '前端攻城獅')
複製代碼
咱們建立了一個Person的構造函數,並用new 建立了一個該對象的實例對象p1。java
前面咱們聊了聊原型和原型鏈,其實就是爲了咱們聊繼承作鋪墊的。廢話很少說,咱們上代碼吧:es6
// 咱們定義一個動物類,裏面有一個類型的屬性
function Animal (type) {
this.type = type
}
// 動物都有吃東西的方法
Animal.prototype.eat = function () {
console.log('我在吃東西')
}
// 咱們來個小貓咪類吧
function Cat () {}
// 如今咱們要實現讓Cat繼承Animal的屬性和方法,該怎麼作呢?
複製代碼
// 咱們將cat類的原型直接指向Animal的實例
Cat.prototype = new Animal()
let cat = new Cat()
console.log(cat.eat())
複製代碼
結果如圖 chrome
咱們發現沒法繼承父類的屬性。咱們改造一下,藉助call方法,代碼入下:function Animal (type) {
this.type = type
}
Animal.prototype.eat = function () {
console.log('我在吃東西')
}
function Cat (type) {
Animal.call(this, type)
}
Cat.prototype = new Animal()
let cat = new Cat('喵咪')
console.log(cat.eat())
console.log(cat.type)
複製代碼
運行結果以下:數組
nice!!!彷佛完美的解決了問題,但真的是這樣麼? 我就不賣關子啦,其實是有問題的,有什麼問題呢?咱們上代碼:function Animal (type, val) {
this.type = type
this.sum = [4,5,6] // 隨便給的屬性啊,爲了說明問題。
this.setSum = function () {
this.sum.push(val)
}
}
Animal.prototype.eat = function () {
console.log('我在吃東西')
}
function Cat (type, val) {
Animal.call(this, type, val)
}
Cat.prototype = new Animal()
let cat = new Cat('喵咪', 1)
let cat2 = new Cat('貓咪2', 2)
console.log(cat.setSum())
console.log(cat2.setSum())
console.log(cat.sum)
console.log(cat2.sum)
複製代碼
運行結果如圖: bash
發現了沒有,圖中setSum方法和sum屬性都是父類定義的,可是子類能夠調用。因此這個不是咱們想要的結果;還有一點就是Cat的constructor屬性,此時指向的並非Cat而是Animal。那應該怎麼解決呢?function inherit(C, P) {
// 等同於臨時構造函數
C.prototype = Object.create(P.prototype);
C.prototype.constructor = C; // 修復constructor
C.super = P;//存儲超類
}
function Animal(type) {
this.type = type;
}
Animal.prototype.eat = function () {
console.log('動物都是要吃飯滴')
}
function Cat(type, talk) {
Cat.super.call(this, type)
this.talk = talk
this.getTalk = function () {
return this.talk
}
}
inherit(Cat, Animal)
let cat = new Cat('貓咪', '咱們一塊兒學貓叫')
console.log(cat.getTalk())
複製代碼
代碼運行以下:babel
這樣咱們就比較完美的解決了繼承的問題。在背景中咱們已經聊到了es6中類的繼承其實是一個語法糖,如今咱們就想辦法撥開這顆糖。函數
class Animal {
constructor (type) {
this.type = type
}
eat () {
console.log('動物都要吃飯')
}
}
class Cat extends Animal {
constructor (type,talk) {
super(type) // 繼承父類constructor的屬性
this.talk = talk
}
getTalk () {
return this.talk
}
}
let cat = new Cat('喵咪', '喵喵喵')
console.log(cat.getTalk()) // 喵喵喵
console.log(cat.type) // 喵咪
複製代碼
var Animal = function () {
function Animal (type) {
this.type = type
this.getType = function () {
return this.type
}
}
return Animal
}()
var Cat = function () {
function Cat (talk) {
this.talk = talk
this.getTalk = function () {
return this.talk
}
}
return Cat
}()
複製代碼
function Animal() {
return {
name: '牛逼',
age: 18
}
}
let animal = new Animal()
console.log(animal) // { name: '牛逼', age: 18}
複製代碼
所以babel中對此作了校驗,這就是第二步要乾的事情。代碼以下:var _classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("不能返回一個對象")
}
}
// 嗯,你沒有看錯就是這麼幾行代碼
var Animal = function () {
function Animal (type) {
_classCallCheck(this, Animal)
this.type = type
}
}()
...此處省略喵咪類
複製代碼
class Animal {
eat () {
console.log('aaa')
}
sleep () {
console.log('bbb')
}
}
複製代碼
如上面代碼中的eat方法和sleep方法的處理。那怎麼能把方法添加到一個對象上呢?沒錯,Object.defineProperty()這個方法就是用來幹這件事情的,babel中也是用他來處理的。回到咱們的話題,在babel中,它是經過_createClass這個函數來處理的。廢話很少說咱們上代碼:/*
_createClass()這個函數接受兩個參數,
一個是這個類的構造函數,即給哪一個類添加方法。
第二個參數是一個數組對象,相似這樣嬸的
[{key: 'eat', val: function eat () { console.log('aaa') }}]
是否是恍然大悟?那接下來咱們就實現一下吧
*/
function definePropties (target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ('value' in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor)
}
}
var _createClass = function () {
return function (Constructor, protoProps, staticProps) {
// 添加原型上的方法
if (protoProps) definePropties(Constructor.prototype, protoProps)
// 添加靜態的方法
if (staticProps) definePropties(Constructor, staticProps)
return Constructor
}
}()
_createClass(Animal,[{
key: 'eat',
value: function () {
console.log('aaa')
}
}])
複製代碼
接下來,終於到了咱們的大boss啦,上面咱們聊了聊babel中是怎麼處理es6中類的,這節咱們就聊聊babel中是怎麼處理es6中的繼承。ui
function _inherits(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true;
}
})
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
複製代碼
好啦,這個話題就聊到這裏,歡迎你們拍磚啊。