Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2021/04/25 - 常见的继承方式的实现 #79

Open
lxinr opened this issue Apr 25, 2021 · 0 comments
Open

2021/04/25 - 常见的继承方式的实现 #79

lxinr opened this issue Apr 25, 2021 · 0 comments
Labels

Comments

@lxinr
Copy link
Owner

lxinr commented Apr 25, 2021

1、原型链继承

function Parent () {
    this.name = 'kevin';
}

Parent.prototype.getName = function () {
    console.log(this.name);
}

function Child () {

}

Child.prototype = new Parent();

var child1 = new Child();

console.log(child1.getName()) // kevin

缺点:

  • 引用类型的属性被所有实例共享
  • 在创建 Child 的实例时,不能向Parent传参

2、构造函数继承(经典继承)

function  SuperType(){
    this.color=["red","green","blue"];
}
function  SubType(){
    //继承自SuperType
    SuperType.call(this); 
    // 大体逻辑:通过new绑定,此时的this为构造函数SubType,
    // call方法目的为将SuperType的this替换为传入的this即构造函数SubType,再执行SuperType函数,
    // 因为是引用类型,所以此时构造函数SubType就有了SuperType的属性
}
var instance1 = new SubType();
instance1.color.push("black"); 
alert(instance1.color);//"red,green,blue,black"

var instance2 = new SubType();
alert(instance2.color);//"red,green,blue"

缺点:

  • 只能继承父类的实例属性和方法,不能继承原型属性/方法
  • 无法实现复用,每个子类都有父类实例函数的副本,影响性能

3、组合继承

用原型链实现对原型属性和方法的继承,用借用构造函数技术来实现实例属性的继承

function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
  alert(this.name);
};

function SubType(name, age){
  // 继承属性
  // 第二次调用SuperType()
  SuperType.call(this, name);
  this.age = age;
}

// 继承方法
// 构建原型链
// 第一次调用SuperType()
SubType.prototype = new SuperType(); 
// 重写SubType.prototype的constructor属性,指向自己的构造函数SubType
SubType.prototype.constructor = SubType; 
SubType.prototype.sayAge = function(){
    alert(this.age);
};

var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29

var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

缺点:

  • 在使用子类创建实例对象时,其原型中会存在两份相同的属性/方法

4、寄生组合式继承

结合借用构造函数传递参数和寄生模式实现继承

function inheritPrototype(subType, superType){
  var prototype = Object.create(superType.prototype); // 创建对象,创建父类原型的一个副本
  prototype.constructor = subType;                    // 增强对象,弥补因重写原型而失去的默认的constructor 属性
  subType.prototype = prototype;                      // 指定对象,将新创建的对象赋值给子类的原型
}

// 父类初始化实例属性和原型属性
function SuperType(name){
  this.name = name;
  this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
  alert(this.name);
};

// 借用构造函数传递增强子类实例属性(支持传参和避免篡改)
function SubType(name, age){
  SuperType.call(this, name);
  this.age = age;
}

// 将父类原型指向子类
inheritPrototype(SubType, SuperType);

// 新增子类原型属性
SubType.prototype.sayAge = function(){
  alert(this.age);
}

var instance1 = new SubType("xyc", 23);
var instance2 = new SubType("lxy", 23);

instance1.colors.push("2"); // ["red", "blue", "green", "2"]
instance2.colors.push("3"); // ["red", "blue", "green", "3"]

5、类继承

extends关键字主要用于类声明或者类表达式中,以创建一个类,该类是另一个类的子类。其中constructor表示构造函数,一个类中只能有一个构造函数,有多个会报出SyntaxError错误,如果没有显式指定构造方法,则会添加默认的constructor方法

class Rectangle {
    // constructor
    constructor(height, width) {
        this.height = height;
        this.width = width;
    }
    
    // Getter
    get area() {
        return this.calcArea()
    }
    
    // Method
    calcArea() {
        return this.height * this.width;
    }
}

const rectangle = new Rectangle(10, 20);
console.log(rectangle.area);
// 输出 200

-----------------------------------------------------------------
// 继承
class Square extends Rectangle {

  constructor(length) {
    super(length, length);
    
    // 如果子类中存在构造函数,则需要在使用“this”之前首先调用 super()。
    this.name = 'Square';
  }

  get area() {
    return this.height * this.width;
  }
}

const square = new Square(10);
console.log(square.area);
// 输出 100

总结

1、函数声明和类声明的区别

函数声明会提升,类声明不会。首先需要声明你的类,然后访问它,否则会抛出一个ReferenceError

2、ES5继承和ES6类继承的区别
  • ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.call(this))
  • ES6的类继承有所不同,实质上是先创建父类的实例对象this,然后再用子类的构造函数修改this。因为子类没有自己的this对象,所以必须先调用父类的super()方法,否则新建实例报错
@lxinr lxinr added the JS基础 label Apr 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant