构造函数与class实现类的区别
首先聊聊ES6 class定义的类和用构造函数new出来的类的一些不同之处
- class声明提升
- class声明内部会启用严格模式
- class的所有方法都是不可枚举的
- class的所有方法都没有原型对象prototype
- class定义的类不能被当做函数调用
ES6的class 关键字的实现原理
/**
*ES6的类
**/
class Parent{
constructor(a){
this.a = a;
}
print(){
console.log('parent')
}
}
---------------------------------------Babel 2 ES5------------------------------------------------------
/**
*ES5来实现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的转化
转化之后的Parent,是一个变成自执行函数。其中Person这个构造函数包含了一些变量的定义,和_classCallCheck
函数
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
_classCallCheck
的作用是为了防止直接将class 定义的类当做函数来使用,这也就很好的解释了,ES6的class定义的类不能当做函数来调用。
在这后是进行了_createClass(Parent,{xxx})
的调用,将定义的属性挂在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通过调用_defineProperties
将定义在类的方法和静态方法分别挂载在prototype和类函数上
因为默认设置了enumerable
为false,所以在class中定义的方法都不会被枚举出来。
继承
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中调用了_inherits
函数来实现了继承
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);
}
将这里转换一下就是
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关键字的对象的实现
var o = new Object();
o.prototype = Base.prototype;
//调用构造函数
Base.call(o);
new和Object.create都是将相应参数的prototype挂载在了新对象的prototype上
所以new Obj() 与 Object.create(Obj.prototype)最大的区别就是Object.create()并不会去调用Obj这个构造函数
经过上面的一系列调用就完成了原型链之间的各种连接,这里就不多说啦,小红书上讲的很清楚了!
但是有个问题,从上面这个代码,从头到尾都没有对父类进行实例化,那么怎么能从子类访问到父类的实例变量呢?
super
那么我们稍微改一改代码呢
class Person {
constructor() {
this.a = 1;
}
}
class Child extends Person {
print(){
console.log(this.a);
}
}
转换后
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);
}
这里通过_getPrototypeOf
获取到父类的构造函数,传入当前的Child的this指针进行了调用,于是乎就把值给父类中实例变量挂载在了Child的this指针上了。_getPrototypeOf(Child).apply(this, arguments)
也就实现了super关键字啦
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。