JS中万物皆对象
所以到了es6对一个对象生成器(构造函数)也定义了新的写法——类(class)使之写法和面向对象的写法更像
现如今我们定义一个一个构造函数就是定义一个类,写法如下:
class Parent {
constructor(name,age){
this.name = name;
this.age = age;
}
saySomething() {
console.log('Hi, I am Parent');
}
}
这是一个最简单的类,定义了实例属性name和age和一个原型方法saySomething
我们知道es6的语法需要编译成es5的语法后才能在浏览器端运行,babel这个工具就可以把我们的es6的代码编译成es5,
我们将这段代码复制到babel编译器看能得到什么:
"use strict";
function _instanceof(left, right) {
if ( // 判断传进来的构造函数right不为null并且当前Symbol类型有定义(即可用),并且构造函数的[Symbol.hasInstance]方法能访问
right != null &&
typeof Symbol !== "undefined" &&
right[Symbol.hasInstance]
) {
return !!right[Symbol.hasInstance](left); // 能则把实例传进构造函数的[Symbol.hasInstance]去看是不是由该构造函数生成的
} else {
return left instanceof right; // 若没有则直接调用instanceof来判断
}
}
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) { // 判断调用当前是否是用new操作符调用了函数
throw new TypeError("Cannot call a class as a 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; // 有value值则是可写的
Object.defineProperty(target, descriptor.key, descriptor); //再调用内部方法defineProperty进行定义
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps); // 添加原型方法
if (staticProps) _defineProperties(Constructor, staticProps); // 添加静态方法
return Constructor;
}
// 在es5写法中就是用变量声明的方式定义了一个Parent的自执行函数,得到一个Parent的构造函数
var Parent = /*#__PURE__*/ (function() {
function Parent(name, age) {
_classCallCheck(this, Parent); // 在这里为了确认Parent是通过new方法调用的
this.name = name; // 添加name和age两个实例方法
this.age = age;
}
// 这一步主要就是为这个构造函数添加原型方法和静态方法(如果有就作为第三个参数传递进去,与第二个参数一样是一个数组)
_createClass(Parent, [
{
key: "saySomething",
value: function saySomething() {
console.log("Hi, I am Parent");
}
}
]);
return Parent;
})();
这里定义Parent构造函数时主要就是调用了两个方法_classCallCheck和_createClass.
总结:_classCallCheck是用来确认Parent这个构造函数是不是由new调用的。_createClass.是用来挂载原型方法/静态方法的。
现在我们理解了在es6中定义一个类实质是发生了什么。
下一步就是看子类继承父类(即使用extends关键字时)es5中又是怎么实现的呢
// 现在我们再来定义一个子类,继承与上面的父类
class Child extends Parent {
constructor(name,age){
super(name,age); // es6中规定子类继承父类必须在constructor函数里实现super方法,因为子类没有this,必须继承自父类的this,所以也就是说只能在super后面使用this关键字
}
speak(){
console.log("I am Child");
}
}
我们把class Child和class Parent都复制到babel编译器中,得到如下es5代码:
"use strict";
function _typeof(obj) {
"@babel/helpers - typeof";
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
_typeof = function _typeof(obj) {
return typeof obj;
};
} else {
_typeof = function _typeof(obj) {
return obj &&
typeof Symbol === "function" &&
obj.constructor === Symbol &&
obj !== Symbol.prototype
? "symbol"
: typeof obj;
};
}
return _typeof(obj);
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function"); // 判断传进来的父类是不是null或函数,如果不是则抛出错误
}
// 这一步就是重点,用Object.create方法,使子类的prototype的__proto__指向父类的prototype,即共享其原型上的方法/属性
// superClass && superClass.prototype短路操作符保证存在superClass存在的情况下取superClass.prototype
// 第二个参数为了正确设置subClass.prototype的constructor指向
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, writable: true, configurable: true }
});
// 最后如果superClass存在,则调用_setPrototypeOf
if (superClass) _setPrototypeOf(subClass, superClass);
}
// 这个方法主要就是使用Object.setPrototypeOf方法使子类subClass.__proto__指向父类,实现静态方法/属性的继承
function _setPrototypeOf(o, p) {
_setPrototypeOf =
Object.setPrototypeOf || // 如果有Object.setPrototypeOf就调这个方法,没有就自己实现o.__proto__ = p
function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _possibleConstructorReturn(self, call) {
if (call && (_typeof(call) === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
return self;
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function() {}));
return true;
} catch (e) {
return false;
}
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf
? Object.getPrototypeOf
: function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _instanceof(left, right) {
if (
right != null &&
typeof Symbol !== "undefined" &&
right[Symbol.hasInstance]
) {
return !!right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
function _classCallCheck(instance, Constructor) {
if (!_instanceof(instance, Constructor)) {
throw new TypeError("Cannot call a class as a 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);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
// 构造函数定义在这里,变量声明式定义一个自执行函数,返回Parent构造函数,与上面无异
var Parent = /*#__PURE__*/ (function() {
function Parent(name, age) {
_classCallCheck(this, Parent);
this.name = name;
this.age = age;
}
_createClass(Parent, [
{
key: "saySomething",
value: function saySomething() {
console.log("Hi, I am Parent");
}
}
]);
return Parent;
})();
// 主要就是定义子类的时候,我们在使用关键字extends来继承一个类的时候,实际上是把这个类当作参数传入了这个自执行函数中,现在传入的是Parent,当然你也可以class Child extends null{}来继承null,此时就会把null传入这个自执行函数中
var Child = /*#__PURE__*/ (function(_Parent) { //_Parent就是自执行函数拿到的父类的形参
_inherits(Child, _Parent); // 第一步就是调用_inherits方法,把子构造函数(也就是下面的function Child()函数声明会有变量提升所以可以先调用再定义)和父构造函数(也就是上面的var Parent这个变量)
var _super = _createSuper(Child); // 这个方法以后再研究。。。hhhhhh(尴尬又不失礼貌的微笑)
function Child(name, age) {
_classCallCheck(this, Child); // _classCallCheck方法同上不在赘述
return _super.call(this, name, age);
}
_createClass(Child, [// _createClass方法同上不在赘述
{
key: "speak",
value: function speak() {
console.log("I am Child");
}
}
]);
return Child;
})(Parent);
通过理解上面的代码和旁边的注释。
总结:我们知道了子类继承父类使调用了一个最主要的方法_inherits(Child, _Parent);
在其内部实现了两个继承:
实现了Child.prototype.__proto__ = Parent.prototype的继承
实现了Child.__proto__ = Parent的继承
前者作为构造函数调用时,子类继承父类的原型属性/方法。
即:
let b = new Child();
b.saySomething(); // Hi, I am Parent
后者作为对象调用时,使子类继承父类的静态属性/方法。
即:
Child.index; // 1
清风徐来_hello_world
原创文章 4获赞 4访问量 1627
关注
私信
展开阅读全文
作者:清风徐来_hello_world