十. class 和 ES6转ES5阅读

94 阅读7分钟

十. class 和 ES6转ES5阅读

10.1. class定义方式

按照前面的构造函数形式创建 ,不仅仅和编写普通的函数过于相似,而且代码并不容易理解。

  • 在ES6(ECMAScript2015)新的标准中使用了class关键字来直接定义类;
  • 但是类本质上依然是前面所讲的构造函数、原型链的语法糖而已

可以使用两种方式来声明类:类声明和类表达式;

 // 类的声明
 class Person { }
 // 类的表达式
 let Student = class { }

10.2. class和构造函数的异同

对比后发现其实和我们的构造函数的特性其实是一致的;

 class Person { }
 let p=new Person()
 console.log(Person); //[Function: Person]
 console.log(Person.prototype); // Person {}
 console.log(Person.prototype.constructor); // [Function: Person]
 console.log(p.__proto__ === Person.prototype); // false
 console.log(typeof Person); // function

10.3. class的构造函数

创建对象的时候如何给类传递一些参数?

  • 每个类都可以有一个自己的构造函数(方法),这个方法的名称是固定的constructor;

    • 每个类只能有一个构造函数,如果包含多个构造函数,那么会抛出异常;
  • 当我们通过new操作符,操作一个类的时候会调用这个类的构造函数constructor;

    • 构造器会创建出一个对象,并赋给给this
    • class创建对象执行的操作和构造函数创建对象的原理一样的
  • 直接在类中定义的实例方法,是放到原型上的

  • 在类中可以直接添加setter和getter函数

    • 函数名和属性名不能一样,比如下面是age和 _age,
  • 静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用static关键字来定义:

 class Person {
     constructor(name, age) {
         // constructor中定义的属性是放到对象上的
         this.name = name
         this._age = age // 私有属性,不希望被在对象方法的外部被访问
     }
     // class中定义的属性是放到Person.prototype上的
     eating=function(){
         console.log("这是一个函数");
     }
     // 类的访问器方法
     set age(newAge) {
         // 可拦截修改操作
         this._age = newAge
     }
     get age() {
         // 可拦截访问操作
         return this._age
     }
     // 类的静态方法(也叫类方法):不用创建对象,直接用 对象.方法 进行访问
     static randomCreatePerson(){
         let nameIndex= Math.floor(Math.random() * names.length)
         let name= names[nameIndex]
         let age= Math.floor(Math.random()*100)
         return new Person(name,age)
     }
 }
 let p = new Person("why", 12)
 p.age = 20
 console.log(p.age);
 ​
 let names= ['张三','李四','王五','赵六']
 console.log(Person.randomCreatePerson());

10.4. extends_ super_ static_ 重写

extends:

在ES5中实现继承的方案,过程却是非常繁琐的。ES6中新增了使用extends关键字,可以方便的帮助我们实现继承。

super:

super关键字有不同的使用方式:

  • 注意:语法规定:在子(派生)类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数!
  • super的使用位置有三个:子类的构造函数、实例方法、静态方法;
 class Person {
     constructor(name, age) {
         this.name = name
         this.age = age
     }
     eating() {
         console.log("chi");
     }
     personMethod() {
         console.log("处理逻辑1");
         console.log("处理逻辑2");
         console.log("处理逻辑3");
     }
     static staticMethod() {
         console.log("父类的静态方法");
     }
 }
 class Student extends Person {
     // 如果有实现继承,在子类构造方法中,使用this之前必须调用super()
     constructor(name, age, sno) {
         // 复用父类构造函数
         super(name, age)
         this.sno = sno
     }
     stydying() {
         console.log("xuexi");
     }
     // 子类对父类方法的重写
     eating() {
         console.log("子类重写了eating");
     }
     // 重写personMethod方法
     personMethod() {
         // 复用父类中的personMethod
         super.personMethod()
         console.log("处理逻辑4");
         console.log("处理逻辑5");
         console.log("处理逻辑6");
     }
     // 重写staticMethod静态方法
     static staticMethod() {
         super.staticMethod()
         console.log("子类的静态方法");
     }
 ​
 }
 let stu = new Student("张三", 18, 111)
 // 子类class内定义的实例方法(如stydying方法),是加在子类的原型上的,反之父类也是
 console.log(Object.getOwnPropertyDescriptors(stu.__proto__)); 
 console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__));  
 stu.eating() // 子类重写了eating
 stu.personMethod() // 重写并复用personMethod方法
 stu.staticMethod() // 重写并复用staticMethod静态方法

10.5. 继承内置类_ 混入_ 多态

  • 继承内置类并拓展一些方法
 class hyArray extends Array{
     firstItem(){
         return this[0]
     }
     lastItem(){
         return this[this.length-1]
     }
 }
 let arr =new hyArray(1,2,3)
 console.log(arr.firstItem()); // 1
 console.log(arr.lastItem());  // 3
  • JavaScript的类只支持单继承:也就是只能有一个父类
  • 如果需要在一个类中添加更多相似的功能时,可以使用混入(mixin);
 class Person {}
 ​
 function mixinRunner(BaseClass) {
     class NewClass extends BaseClass {
         runing() {
             console.log("runing");
         }
     }
     return NewClass
 }
 // 在js中 类只能有一个父类:单继承
 class Student extends Person {}
 let NewStudent = mixinRunner(Student)
 let ns = new NewStudent()
 ns.runing()
  • 不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现。
 function sum(a, b) {
     console.log(a + b);
 }
 sum(10, 20)
 sum("aa", "bb")

10.6. label ES6转ES5源码阅读

v1:没有继承和静态方法

 class Person{
     constructor(name,age) {
         this.name=name
         this.age=age
     }
     eating(){
         console.log(`${this.name}eating`);
     }
 }

下面是转成ie10的es5代码

 "use strict";
 ​
 function _classCallCheck(instance, Constructor) {
     // undefind 或者 window是不是Person的实例,是就抛错
     if (!(instance instanceof Constructor)) {
         throw new TypeError("Cannot call a class as a function");
     }
 }
 ​
 // target:Person原型,props:数组
 function _defineProperties(target, props) {
     // 遍历数组
     for (var i = 0; i < props.length; i++) {
         // descriptor:数组中第一个对象
         var descriptor = props[i];
         descriptor.enumerable = descriptor.enumerable || false;
         descriptor.configurable = true;
         if ("value" in descriptor) descriptor.writable = true;
         // 用属性描述符给Person添加一个属性,并返回该对象
         Object.defineProperty(target, descriptor.key, descriptor);
     }
 }
 // Constructor:Person, protoProps:数组, staticProps:静态方法
 function _createClass(Constructor, protoProps, staticProps) {
     // 判断第二个参数是否有值,有就执行_defineProperties,并将Person原型和第二个参数传进去
     if (protoProps) _defineProperties(Constructor.prototype, protoProps);
     if (staticProps) _defineProperties(Constructor, staticProps);
     Object.defineProperty(Constructor, "prototype", {
         writable: false
     });
     return Constructor;
 }
 // /*#__PURE__*/: 标记这是个纯函数。
 // 作用:webpack压缩时发现Person从头到尾都没有用过,就用tree-shaking优化,将Person代码直接删掉
 var Person = /*#__PURE__*/ (function () {
     function Person(name, age) {
         // 如果是new Person() this就是Person的实例对象.
         // 如果是Person(),严格模式下this是undefined; 非严格模式下this是window,
         // 这里是为了防止Person()这样调用
         _classCallCheck(this, Person);
 ​
         this.name = name;
         this.age = age;
     }
     // eating(){console.log(`${this.name}eating`);} 
     封装了_createClass方法往原型上加eating方法
     // 把东西抽离出来是为了以后的复用
     _createClass(Person, [{
         key: "eating",
         value: function eating() {
             console.log("".concat(this.name, "eating"));
         }
     }]);
 ​
     return Person;
 })();

v2:有继承

 class Person {
     constructor(name, age) {
         this.name = name
         this.age = age
     }
     runing() {
         console.log(this.name + "runing");
     }
 }
 class Student extends Person {
     constructor(name, age, sno) {
         super(name,age)
         this.sno = sno
     }
     studying() {
         console.log(this.name + "studying");
     }
 }

下面是转成ie10的es5代码

 "use strict";
 ​
 function _typeof(obj) {
     "@babel/helpers - typeof";
     return (
         (_typeof =
          "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ?
          function (obj) {
             return typeof obj;
         } :
          function (obj) {
             return obj &&
                 "function" == typeof Symbol &&
                 obj.constructor === Symbol &&
                 obj !== Symbol.prototype ?
                 "symbol" :
             typeof obj;
         }),
         _typeof(obj)
     );
 }
 ​
 // 目的:实现寄生式继承
 function _inherits(subClass, superClass) {
     // 只要有一个返回false,结果就为false;目的是要求子类是一个函数,父类不是null
     if (typeof superClass !== "function" && superClass !== null) {
         throw new TypeError("Super expression must either be null or a function");
     }
     // 1.将创建新对象的隐式原型指向父类的显示原型,然后给新建的对象添加一个constructor属性
     // 2.再将新对象赋值给子类的显示原型
     // 注:这里的实现思路和寄生组合式继承一样,目的:子类.prototype= 新对象, 新的对象.__proto__= 父类.prototype, 
     subClass.prototype = Object.create(superClass && superClass.prototype, {
         constructor: {
             value: subClass,
             writable: true,
             configurable: true
         }
     });
     Object.defineProperty(subClass, "prototype", {
         writable: false
     });
     // 如果父类有值就执行_setPrototypeOf()
     // 目的:静态方法的继承 
     if (superClass) _setPrototypeOf(subClass, superClass);
 }
 ​
 // 目的:实现静态方法的继承, 实现思路:子类的__proto__ = 父类.  o:子类, p:父类
 function _setPrototypeOf(o, p) {
     _setPrototypeOf = Object.setPrototypeOf ||
         function _setPrototypeOf(o, p) {
         // 为什么子类的__proto__ = 父类,就可以继承静态方法?
         // 答: 假如父类有个static staticMethod(){}, 子类调用时,发现自身没有该方法,就会去子类的隐式原型找,也就是Function.prototepe
         // Function的显示原型根本就没有这个方法,所以我们让子类的__proto__= 父类,而父类里是有staticMethod方法的,从而实现了静态方法继承
         o.__proto__ = p;
         return o;
     };
     return _setPrototypeOf(o, p);
 }
 ​
 function _createSuper(Derived) {
     // 判断当前是否支持Reflect  true
     var hasNativeReflectConstruct = _isNativeReflectConstruct();
     return function _createSuperInternal() { 
         var Super = _getPrototypeOf(Derived), // 父类(name,age){}
             result; // undefined
         if (hasNativeReflectConstruct) {
             // 这里的this是_createSuperInternal也就是_super()显示绑定的Student实例对象
             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;
     } else if (call !== void 0) {
         throw new TypeError(
             "Derived constructors may only return object or undefined"
         );
     }
     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 {
         Boolean.prototype.valueOf.call(
             Reflect.construct(Boolean, [], function () {})
         );
         return true;
     } catch (e) {
         return false;
     }
 }
 ​
 // o:子类
 function _getPrototypeOf(o) {
     _getPrototypeOf = Object.setPrototypeOf ?
         Object.getPrototypeOf :
     function _getPrototypeOf(o) {
         // 返回子类的原型对象,_setPrototypeOf已经把子类的__proto__ = 父类,所以这里直接返回父类
         return o.__proto__ || Object.getPrototypeOf(o);
     };
     return _getPrototypeOf(o);
 }
 ​
 // 和之前一样
 function _classCallCheck(instance, Constructor) {
     if (!(instance instanceof 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);
     Object.defineProperty(Constructor, "prototype", {
         writable: false
     });
     return Constructor;
 }
 ​
 var Person = /*#__PURE__*/ (function () {
     function Person(name, age) {
         debugger
         _classCallCheck(this, Person);
 ​
         this.name = name;
         this.age = age;
     }
 ​
     _createClass(Person, [{
         key: "runing",
         value: function runing() {
             console.log(this.name + "runing");
         }
     }]);
 ​
     return Person;
 })();
 ​
 // Student是个立即执行函数,参数是Person.  /*#__PURE__*/: 标注为纯函数,
 var Student = /*#__PURE__*/ (function (_Person) {
     // 让Student继承Person(包括静态方法的继承),和寄生式继承核心一样,
     _inherits(Student, _Person);
     // 父类不能直接被调用,所以这里封装了一个_super函数
     debugger;
     var _super = _createSuper(Student);
 ​
     function Student(name, age, sno) {
         var _this;
 ​
         _classCallCheck(this, Student);
         debugger;
         // 为什么不和手写的寄生式组合继承一样去调用父类的构造方法 Person.call(this, name, age, friends)
         // 答: 父类不能当成一个函数直接去调用,否则报错, _classCallCheck()对调用做了限制
         _this = _super.call(this, name, age);
         _this.sno = sno;
         return _this;
     }
 ​
     _createClass(Student, [{
         key: "studying",
         value: function studying() {
             console.log(this.name + "studying");
         }
     }]);
 ​
     return Student;
 })(Person);
 //  new Student()