構(gòu)造函數(shù)與class實(shí)現(xiàn)類的區(qū)別
首先聊聊ES6 class定義的類和用構(gòu)造函數(shù)new出來(lái)的類的一些不同之處
- class聲明提升
- class聲明內(nèi)部會(huì)啟用嚴(yán)格模式
- class的所有方法都是不可枚舉的
- class的所有方法都沒(méi)有原型對(duì)象prototype
- class定義的類不能被當(dāng)做函數(shù)調(diào)用
ES6的class 關(guān)鍵字的實(shí)現(xiàn)原理
/**
*ES6的類
**/
class Parent{
constructor(a){
this.a = a;
}
print(){
console.log('parent')
}
}
---------------------------------------Babel 2 ES5------------------------------------------------------
/**
*ES5來(lái)實(shí)現(xiàn)ES6的類
**/
var Parent = /*#__PURE__*/function (a) {
function Parent() {
_classCallCheck(this, Parent);
this.a = a;
}
_createClass(Parent, [{
key: "print",
value: function print() {
console.log('parent');
}
}]);
return Parent;
}();
class的轉(zhuǎn)化
轉(zhuǎn)化之后的Parent,是一個(gè)變成自執(zhí)行函數(shù)。其中Person這個(gè)構(gòu)造函數(shù)包含了一些變量的定義,和_classCallCheck函數(shù)
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
_classCallCheck的作用是為了防止直接將class 定義的類當(dāng)做函數(shù)來(lái)使用,這也就很好的解釋了,ES6的class定義的類不能當(dāng)做函數(shù)來(lái)調(diào)用。
在這后是進(jìn)行了_createClass(Parent,{xxx})的調(diào)用,將定義的屬性掛在Person的原型鏈上
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
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);
}
}
_createClass通過(guò)調(diào)用_defineProperties將定義在類的方法和靜態(tài)方法分別掛載在prototype和類函數(shù)上
因?yàn)槟J(rèn)設(shè)置了enumerable為false,所以在class中定義的方法都不會(huì)被枚舉出來(lái)。
繼承
class Child extends Parent{
constructor(){}
childPrint(){
console.log('child')
}
}
---------------------------------------Babel 2 ES5------------------------------------------------------
var Child = /*#__PURE__*/function (_Parent) {
_inherits(Child, _Parent);
function Child() {
var _this;
_classCallCheck(this, Child);
return _possibleConstructorReturn(_this);
}
_createClass(Child, [{
key: "childPrint",
value: function childPrint() {
console.log('child');
}
}]);
return Child;
}(Parent);
Child中調(diào)用了_inherits函數(shù)來(lái)實(shí)現(xiàn)了繼承
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError(
"Super expression must either be null or a function"
);
}
subClass.prototype = Object.create(
superClass && superClass.prototype,
{
constructor: {
value: subClass,
writable: true,
configurable: true
}
}
);
if (superClass)
_setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf ||
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
將這里轉(zhuǎn)換一下就是
function F(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototypte.constructor = subClass
subClass._proto = superClass;
納尼為啥這是什么鬼
Object.create和new
Object.create = function(o){
var F = function(){};
F.prototype = o;
return new F();
}
//new關(guān)鍵字的對(duì)象的實(shí)現(xiàn)
var o = new Object();
o.prototype = Base.prototype;
//調(diào)用構(gòu)造函數(shù)
Base.call(o);
new和Object.create都是將相應(yīng)參數(shù)的prototype掛載在了新對(duì)象的prototype上
所以new Obj() 與 Object.create(Obj.prototype)最大的區(qū)別就是Object.create()并不會(huì)去調(diào)用Obj這個(gè)構(gòu)造函數(shù)
經(jīng)過(guò)上面的一系列調(diào)用就完成了原型鏈之間的各種連接,這里就不多說(shuō)啦,小紅書上講的很清楚了!
但是有個(gè)問(wèn)題,從上面這個(gè)代碼,從頭到尾都沒(méi)有對(duì)父類進(jìn)行實(shí)例化,那么怎么能從子類訪問(wèn)到父類的實(shí)例變量呢?
super
那么我們稍微改一改代碼呢
class Person {
constructor() {
this.a = 1;
}
}
class Child extends Person {
print(){
console.log(this.a);
}
}
轉(zhuǎn)換后
var Child = /*#__PURE__*/function (_Person) {
_inherits(Child, _Person);
function Child() {
_classCallCheck(this, Child);
return _possibleConstructorReturn(this, _getPrototypeOf(Child).apply(this, arguments));
}
_createClass(Child, [{
key: "print",
value: function print() {
console.log(this.a);
}
}]);
return Child;
}(Person);
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
這里通過(guò)_getPrototypeOf獲取到父類的構(gòu)造函數(shù),傳入當(dāng)前的Child的this指針進(jìn)行了調(diào)用,于是乎就把值給父類中實(shí)例變量掛載在了Child的this指針上了。
_getPrototypeOf(Child).apply(this, arguments)也就實(shí)現(xiàn)了super關(guān)鍵字啦