总结一下JavaScript中的几种继承方案:
将父类的实例作为子类的原型
- // 父类
- function Person(name,friend) {
- this.name = name;
- this.friend = friend
- this.say = function () {
- console.log(`My name is ${this.name}.`);
- };
- }
- Person.prototype.listen = function () {
- console.log('I am listening...');
- }
-
- function Student(no) {
- this.no = no;
- }
- Student.prototype = new Person(); // 将父类的实例作为子类的原型
- Student.prototype.constructor = Student; // 修正
- Student.prototype.sayNo = function () {
- console.log(`My No. is ${no}`);
- }
-
- const stu = new Student(1);
- stu.sayNo(); // My No. is 1
- stu.say(); // My name is undefined.
- stu.listen(); // I am listening...
-
- console.log(stu instanceof Student); // true
- console.log(stu instanceof Person); // true
缺点:
- var stu1 = new Student(1)
- var stu2 = new Student(2)
-
- // 1.直接修改继承的属性,是给本对象添加了一个新属性
- stu1.name = "kobe"
- console.log(stu1) // {no: 1, name: 'kobe'}
- console.log(stu2) // {no: 2}
-
- // 2.访问继承的引用数据类型,获取引用, 修改引用中的值, 会相互影响
- stu1.friends.push("kobe")
- console.log(stu1.friends) // ['kobe']
- console.log(stu2.friends) // ['kobe']
- function Person(age) {
- this.name= name;
- this.age = age
- }
- Person.prototype.say = function(){
- console.log('啦啦啦~')
- }
-
- function Student(no, name, age) {
- Person.call(this, name);
- this.no = no;
- }
- const stu = new Student(233, 'Ben', 18);
- stu.say();// 无法继承到父类原型的方法
-
- console.log(stu2 instanceof Student2); // true
- console.log(stu2 instanceof Person); // false
缺点:
原型链继承和借用构造函数继承方法的结合,用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承。
- // 父类: 公共属性和方法
- function Person(name, age, friends) {
- this.name = name
- this.age = age
- this.friends = friends
- }
-
- Person.prototype.eating = function() {
- console.log(this.name + " eating~")
- }
-
- // 子类: 特有属性和方法
- function Student(name, age, friends, sno) {
- Person.call(this, name, age, friends) //1.借用构造函数
- this.sno = 111
- }
-
- var p = new Person()
- Student.prototype = p //2.原型链继承
- p.constructor = Person
-
- Student.prototype.studying = function() {
- console.log(this.name + " studying~")
- }
-
- const stu = new Student("why", 18, ["kobe"], 111)
- console.log(stu) // {age:18,name:'why',sno:111,firends:['kobe']}
缺点:
- // 原型式继承函数1
- function createObject1(o) {
- var newObj = {}
- Object.setPrototypeOf(newObj, o)
- return newObj
- }
-
- // 原型式继承函数2
- function createObject2(o) {
- function Fn() {}
- Fn.prototype = o
- var newObj = new Fn()
- return newObj
- }
-
- // 原型式继承3
- // Object.create可以替代上述两个函数的功能
- var obj = {
- name: "why",
- age: 18
- }
- var info = Object.create(obj)
- console.log(info)
- console.log(info.__proto__===obj) //true
优点:
创建一个新的对象(子类实例),这个对象的原型是传入的对象(父类构造函数的实例),子类实例就可以通过原型链访问到父类实例的属性。
缺点:
- function createAnother(original){
- var clone = object(original); // 通过调用 object() 函数创建一个新对象
- clone.sayHi = function(){ // 以某种方式来增强对象(相比原型式继承多出的部分)
- alert("hi");
- };
- return clone; // 返回这个对象
- }
-
- var person = {
- name: "Nicholas",
- friends: ["Shelby", "Court", "Van"]
- };
- var anotherPerson = createAnother(person);
- anotherPerson.sayHi(); //"hi"
缺点:和原型式继承一样
借用构造函数继承和寄生式继承的组合,通过借用构造函数继承父类实例上的属性,寄生式继承父类原型的属性。
- function createObject(o) {
- function Fn() {}
- Fn.prototype = o
- return new Fn()
- }
-
- function inheritPrototype(SubType, SuperType) {
- SubType.prototype = Object.create(SuperType.prototype)
- Object.defineProperty(SubType.prototype, "constructor", {
- enumerable: false,
- configurable: true,
- writable: true,
- value: SubType
- })
- }
-
- function Person(name, age, friends) {
- this.name = name
- this.age = age
- this.friends = friends
- }
-
- Person.prototype.running = function() {
- console.log("running~")
- }
-
- Person.prototype.eating = function() {
- console.log("eating~")
- }
-
- function Student(name, age, friends, sno, score) {
- Person.call(this, name, age, friends) //借用构造函数继承
- this.sno = sno
- this.score = score
- }
-
- inheritPrototype(Student, Person) //寄生式继承
-
- Student.prototype.studying = function() {
- console.log("studying~")
- }
- // 继承的属性都添加在子类实例自身上
- console.log(stu) //{name:'why',age:18,score:100,sno:111,friends:['kobe']}
- stu.studying() //studying~ 可以通过原型链访问到父类方法
- stu.running() //running~
- stu.eating() //eating~
-
- console.log(stu.constructor.name) //Student
没有使用父类实例作为子类原型来继承原型上的属性,采用寄生式继承,将一个原型为父类原型的对象直接赋值给子类原型,可以避免多次调用父类构造函数和保存两份父类属性。
这是最成熟的方法,也是现在库实现的方法
- function SuperClass(){}
- SuperClass.prototype.saySuper=function(){
- console.log('hellow super')
- }
- function OtherSuperClass(){}
- OtherSuperClass.prototype.sayOther=function(){
- console.log('hellow other')
- }
- function MyClass() {
- // SuperClass.call(this);
- // OtherSuperClass.call(this);
- }
- // 继承一个类
- MyClass.prototype = Object.create(SuperClass.prototype);
- // 重新指定constructor
- MyClass.prototype.constructor = MyClass;
- // 混合其它
- Object.assign(MyClass.prototype, OtherSuperClass.prototype);
-
- MyClass.prototype.myMethod = function() {
- // do something
- };
- console.log(MyClass.prototype)
Object.assign会把 OtherSuperClass原型上的函数拷贝到 MyClass原型上,使 MyClass 的所有实例都可用 OtherSuperClass 的方法。
- class Person {
- constructor(name, age) {
- this.name = name
- this.age = age
- }
-
- running() {
- console.log(this.name + " running~")
- }
-
- eating() {
- console.log(this.name + " eating~")
- }
-
- personMethod() {
- console.log("处理逻辑1")
- console.log("处理逻辑2")
- console.log("处理逻辑3")
- }
-
- static staticMethod() {
- console.log("PersonStaticMethod")
- }
- }
-
- // Student称之为子类(派生类)
- class Student extends Person {
- // JS引擎在解析子类的时候就有要求, 如果我们有实现继承
- // 那么子类的构造方法中, 在使用this之前,必须显式通过super调用父类的构造函数
- constructor(name, age, sno) {
- super(name, age)
- this.sno = sno
- }
-
- studying() {
- console.log(this.name + " studying~")
- }
-
- // 类对父类的方法的重写
- running() {
- console.log("student " + this.name + " running")
- }
-
- // 重写personMethod方法
- personMethod() {
- // 复用父类中的处理逻辑
- super.personMethod()
-
- console.log("处理逻辑4")
- console.log("处理逻辑5")
- console.log("处理逻辑6")
- }
-
- // 重写静态方法
- static staticMethod() {
- super.staticMethod()
- console.log("StudentStaticMethod")
- }
- }
-
- var stu = new Student("why", 18, 111)
- console.log(stu)
-
- console.log(Object.getOwnPropertyDescriptors(stu.__proto__))
- console.log(Object.getOwnPropertyDescriptors(stu.__proto__.__proto__)) //父类原型
参考: