Class是ES6中新加入的繼承機制,實際是Javascript關於原型繼承機制的語法糖,本質上是對原型繼承的封裝。本文將會討論:
一、ES6 class的實現細
二、相關Object API盤點
三、Javascript中的繼承實現方案盤點javascript
class Person{ constructor(name, age){ this.name = name this.age = age } static type = 'being' sayName (){ return this.name } static intro(){ console.log("") } } class Men extends Person{ constructor(name, age){ super() this.gender = 'male' } }
const men = new Men()java
以上代碼是ES6 class的基本使用方式,經過babel解析後,主要代碼結構以下:es6
'use strict'; var _createClass = function () {...}();// 給類添加方法 function _possibleConstructorReturn(self, call) { ...}//實現super function _inherits(subClass, superClass) {...}// 實現繼承 function _classCallCheck(instance, Constructor) {...} // 防止以函數的方式調用class var Person = function () { function Person(name, age) { _classCallCheck(this, Person); this.name = name; this.age = age; } _createClass(Person, [{ key: 'sayName', value: function sayName() { return this.name; } }], [{ key: 'intro', value: function intro() { console.log(""); } }]); return Person; }(); Person.type = 'being'; //靜態變量 var Men = function (_Person) { _inherits(Men, _Person); function Men(name, age) { _classCallCheck(this, Men); var _this = _possibleConstructorReturn(this, (Men.__proto__ || Object.getPrototypeOf(Men)).call(this)); _this.gender = 'male'; return _this; } return Men; }(Person); var men = new Men();
爲何說es6的class 是基於原型繼承的封裝呢? 開始省略的四個函數又有什麼做用呢?
下面,咱們就從最開始的四個函數入手,詳細的解釋es6的class 是如何封裝的。express
第一:_classCallCheck
函數, 檢驗構造函數的調用方式:
代碼segmentfault
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
咱們知道,在javascript中 person = new Person() ,一般完成如下幾件事:
一、建立一個新的對象 Object.create()
二、將 新對象的 this 指向 構造函數的原型對象
三、新對象的__proto__ 指向 構造函數
四、執行構造函數
而普通函數調用,this一般指向全局
所以,_classCallCheck
函數是用來檢測類的調用方式。防止類的構造函數以普通函數的方式調用。babel
第二: _createClass
給類添加方法網絡
var _createClass = function () { function defineProperties(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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); //非靜態函數 -> 原型 if (staticProps) defineProperties(Constructor, staticProps); return Constructor; // 靜態函數 -> 構造函數 }; }();
_createClass
是一個閉包+當即執行函數,以這種方式模擬一個做用域,將defineProperties
私有化。
這個函數的主要做用是經過Object.defineProperty
給類添加方法,其中將靜態方法添加到構造函數上,將非靜態的方法添加到構造函數的原型對象上。閉包
第三: _inherits
實現繼承函數
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, // 子類的原型的__proto__指向父類的原型 //給子類添加 constructor屬性 subclass.prototype.constructor === subclass { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } } ); if (superClass) //子類__proto__ 指向父類 Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
從這個函數就可以很明顯的看出來,class實現繼承的機制了。學習
第四: _possibleConstructorReturn
super()
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); //保證子類構造函數中 顯式調用 super() } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
要想理解這個函數的做用,須要結合他的調用場景
var _this = _possibleConstructorReturn(this, (Men.__proto__ || Object.getPrototypeOf(Men)).call(this));// function Men(){}
此時已經執行完_inherits
函數,Men.__proto__ === Person
至關於:
var _this = _possibleConstructorReturn(this, Person.call(this));
很明顯,就是將子類的this 指向父類。
根據以上的分析,es6 class 的實現機制也能夠總結出來了:
毫無疑問的,class機制仍是在prototype的基礎之上進行封裝的
——contructor 執行構造函數相關賦值
——使用 Object.defineProperty()方法 將方法添加的構造函數的原型上或構造函數上
——使用 Object.create() 和 Object.setPrototypeOf 實現類之間的繼承 子類原型__proto__指向父類原型 子類構造函數__proto__指向父類構造函數
——經過變動子類的this 做用域實現super()
1.原型鏈繼承
2.構造函數繼承
3.組合繼承
4.ES6 extends 繼承
詳細內容能夠參考 聊一聊 JavaScript的繼承方式https://segmentfault.com/a/11...
終於寫完了,在沒有網絡輔助的狀況下寫博客真是太難了!絕知此事要躬行呀!
原來以爲寫一篇關於class的博客還不簡單嗎,就是原型鏈繼承那一套唄,如今總結下來,仍是有不少地方須要注意的;學習到了不少!嗯 不說了, 我還有好幾個坑要填呢~
若是這篇文章對你有幫助的話,歡迎點贊收藏!
若是你有疑問的話,但願積極留言,共同討論,共同進步!
ES6—類的實現原理 https://segmentfault.com/a/11...
JavaScript 紅寶書