JavaScript ES6面向对象(实现继承)

229 阅读5分钟

一、ES6类的使用

1.1 class定义类

  • 类本质上依然是构造函数、原型链的语法糖
// ES5中定义类
// function Person() {}

// ES6定义类
// {key: value} -> 对象
// {表达式} -> 代码块
// {} -> 类的结构
class Person {

}

// 创建实例对象
var p1 = new Person()
var p2 = new Person()
console.log(p1, p2)

// 另外一种定义方法: 表达式写法(较少使用)
var Student = class {

}
var foo = function() {

}

var stu1 = new Student()
console.log(stu1)
  • 类和构造函数的区别
    • 相同点
      • 都能够通过new操作符创建对象
      • 原型对象的constructor指向本身
      • typeof 结果都是 function
    • 不同点
      • 构造函数能够直接调用,而类不能够当作普通函数调用
    // function定义类
    function Person1(name, age) {
      this.name = name
      this.age = age
    }
    
    Person1.prototype.running = function() {}
    Person1.prototype.eating = function() {}
    
    var p1 = new Person1("lilei", 18)
    console.log(p1.__proto__ === Person1.prototype)
    console.log(Person1.prototype.constructor)
    console.log(typeof Person1) // function
    
    // 不同点: 作为普通函数去调用
    Person1("abc", 100)
    
    // class定义类
    class Person2 {
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    
      running() {}
      eating() {}
    }
    
    var p2 = new Person2("hanmeimei", 17)
    console.log(p2.__proto__ === Person2.prototype)
    console.log(Person2.prototype.constructor)
    console.log(typeof Person2) // function
    
    // 不同点: class定义的类, 不能作为一个普通的函数进行调用
    Person2("cba", 0)
    

1.2 class类中的内容

  • constructor方法

  • 实例方法

  • 访问器方法

    class Person {
      // 程序员之间的约定: 以_开头的属性和方法, 是不在外界访问
      constructor(name, age) {
        this._name = name
      }
    
      set name(value) {
        console.log("设置name")
        this._name = value
      }
    
      get name() {
        console.log("获取name")
        return this._name
      }
    }
    
    var p1 = new Person("kobe", 18)
    p1.name = "james"
    console.log(p1.name) // james
    
  • 静态方法 static

    var names = ["abc", "cba", "nba", "mba"]
    class Person {
      // 1.类中的构造函数
      // 当我们通过new关键字调用一个Person类时, 默认调用class中的constructor方法
      constructor(name, age) {
        this.name = name
        this.age = age
      }
    
      // 2.实例方法
      // 本质上是放在Person.prototype
      running() {
        console.log(this.name + " running~")
      }
      eating() {
        console.log(this.name + " eating~")
      }
      
      // 3.类方法(静态方法)
      static randomPerson() {
        console.log(this)
        var randomName = names[Math.floor(Math.random() * names.length)]
        return new this(randomName, Math.floor(Math.random() * 100))
      }
    }
    
    // 创建实例对象
    var p1 = new Person("kobe", 24)
    
    // 使用实例对象中属性和方法
    console.log(p1.name, p1.age)
    p1.running()
    p1.eating()
    
    // 研究内容
    console.log(Person.prototype === p1.__proto__) // true
    console.log(Person.running) // undefined
    console.log(Person.prototype.running) // f running
    

1.3 class的extends

  • extends关键字
  • super关键字
    • 方式一: 构造方法 super()

      • 一定在使用this之前以及返回对象之前先调用super
      • 构造函数的首行
    • 方式二: 实例方法super.method

    • 方式三: 静态方法super.staticMethod

      // 定义父类
      class Person {
        constructor(name, age) {
          this.name = name
          this.age = age
        }
      
        running() {
          console.log("running~")
        }
        eating() {
          console.log("eating~")
        }
      
      }
      
      class Student extends Person {
        constructor(name, age, sno, score) {
          // this.name = name
          // this.age = age
          super(name, age)
          this.sno = sno
          this.score = score
        }
      
        // running() {
        //   console.log("running~")
        // }
        // eating() {
        //   console.log("eating~")
        // }
      
        studying() {
          console.log("studying~")
        }
      }
      
      var stu1 = new Student("lilei", 18, 111, 100)
      stu1.running()
      stu1.eating()
      stu1.studying()
      
      
      class Teacher extends Person {
        constructor(name, age, title) {
          // this.name = name
          // this.age = age
          super(name, age)
          this.title = title
        }
      
        // running() {
        //   console.log("running~")
        // }
        // eating() {
        //   console.log("eating~")
        // }
      
        teaching() {
          console.log("teaching~")
        }
      }
      
      class Animal {
        running() {
          console.log("running")
        }
        eating() {
          console.log("eating")
        }
      
        static sleep() {
          console.log("static animal sleep")
        }
      }
      
      class Dog extends Animal {
        // 子类如果对于父类的方法实现不满足(继承过来的方法)
        // 重新实现称之为重写(父类方法的重写)
        running() {
          console.log("dog四条腿")
          // 调用父类的方法
          super.running()
          // console.log("running~")
          // console.log("dog四条腿running~")
        }
      
        static sleep() {
          console.log("趴着")
          super.sleep()
        }
      }
      
      var dog = new Dog()
      dog.running()
      dog.eating()
      
      Dog.sleep()
      

1.4 继承自内置类

  • 对内置类进行扩展

    // 1.创建一个新的类, 继承自Array进行扩展
    class MyArray extends Array {
      get lastItem() {
        return this[this.length - 1]
      }
    
      get firstItem() {
        return this[0]
      }
    }
    
    var arr = new MyArray(10, 20, 30)
    console.log(arr)
    console.log(arr.length)
    console.log(arr[0])
    console.log(arr.lastItem)
    console.log(arr.firstItem)
    
    // 2.直接对Array进行扩展
    Array.prototype.lastItem = function() {
      return this[this.length - 1]
    }
    
    var arr = new Array(10, 20, 30)
    console.log(arr.__proto__ === Array.prototype) // true
    console.log(arr.lastItem()) // 30
    

1.5 类的混入Mixin

// JavaScript只支持单继承(不支持多继承)
function mixinAnimal(BaseClass) {
  return class extends BaseClass {
    running() {
      console.log("running~")
    }
  }
}

function mixinRunner(BaseClass) {
  return class extends BaseClass {
    flying() {
      console.log("flying~")
    }
  }
}

class Bird {
  eating() {
    console.log("eating~")
  }
}

// var NewBird = mixinRunner(mixinAnimal(Bird))
class NewBird extends mixinRunner(mixinAnimal(Bird)) {
}
var bird = new NewBird()
bird.flying()
bird.running()
bird.eating()

二、babel ES6转ES5源码

2.1 只写一个class类的源码

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  running() {}
  eating() {}

  static randomPerson() {}
}

var p1 = new Person()
  • 转化成ES5的代码
"use strict";

// 检测是否是当作普通函数调用
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) {
    _classCallCheck(this, Person);

    this.name = name;
    this.age = age;
  }

  _createClass(
    Person,
    [
      {
        key: "running",
        value: function running() {}
      },
      {
        key: "eating",
        value: function eating() {}
      }
    ],
    [
      {
        key: "randomPerson",
        value: function randomPerson() {}
      }
    ]
  );

  return Person;
})();

var p1 = new Person();

2.2 class的继承extends源码

class Person {
  constructor(name, age) {
    this.name = name
    this.age = age
  }

  running() {}
  eating() {}

  static randomPerson() {}
}

class Student extends Person {
  constructor(name, age, sno, score) {
    super(name, age)
    this.sno = sno
    this.score = score
  }

  studying() {}
  static randomStudent() {}
}

var stu = new Student()
  • 转化成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) {
  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 }
  });
  Object.defineProperty(subClass, "prototype", { writable: false });
  if (superClass) _setPrototypeOf(subClass, superClass);
}

function _setPrototypeOf(o, p) {
  _setPrototypeOf = Object.setPrototypeOf
    ? Object.setPrototypeOf.bind()
    : function _setPrototypeOf(o, p) {
        o.__proto__ = p;
        return o;
      };
  return _setPrototypeOf(o, p);
}

function _createSuper(Derived) {
  var hasNativeReflectConstruct = _isNativeReflectConstruct();
  return function _createSuperInternal() {
    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;
  } 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;
  }
}

function _getPrototypeOf(o) {
  _getPrototypeOf = Object.setPrototypeOf
    ? Object.getPrototypeOf.bind()
    : function _getPrototypeOf(o) {
        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) {
    _classCallCheck(this, Person);
    this.name = name;
    this.age = age;
  }
  _createClass(
    Person,
    [
      {
        key: "running",
        value: function running() {}
      },
      {
        key: "eating",
        value: function eating() {}
      }
    ],
    [
      {
        key: "randomPerson",
        value: function randomPerson() {}
      }
    ]
  );
  return Person;
})();

// function inherit(SubType, SuperType) {
  // SubType.prototype = Object.create(SuperType.prototype)
  // SubType.prototype.constructor = SubType
// }

// 立即执行函数传入Person,是为了保证该函数是一个纯函数
var Student = /*#__PURE__*/ (function (_Person) {
  _inherits(Student, _Person);

  var _super = _createSuper(Student);

  function Student(name, age, sno, score) {
    var _this;

    _classCallCheck(this, Student);

    _this = _super.call(this, name, age);
    _this.sno = sno;
    _this.score = score;
    return _this;
  }

  _createClass(
    Student,
    [
      {
        key: "studying",
        value: function studying() {}
      }
    ],
    [
      {
        key: "randomStudent",
        value: function randomStudent() {}
      }
    ]
  );

  return Student;
})(Person);

var stu = new Student();

三、JavaScript中的多态

3.1 什么是多态?

  • 维基百科对多态的定义:

    • 多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口,或使用一个单一的符号来表示多个不同的类型。
  • 总结:不同的数据类型进行同一个操作,表现出不同的行为,就是多态的体现

3.2 js多态的表现

function sum(a1, a2) {
  return a1 + a2
}

// number 类型
sum(20, 30)
// string 类型
sum("abc", "cba")

// 多态的表现
var foo = 123
foo = "Hello World"
console.log(foo.split())
foo = {
  running: function() {}
}
foo.running()
foo = []
console.log(foo.length)