js类的创建和继承

429 阅读4分钟

一.类的创建(es5)

定义:new一个function,在这个function的prototype里添加属性和方法

创建一个Animal类:

//创建一个动物类
function Animal(name){
    //属性
    this.name = name || "Animal"
    //实例方法
    this.sleep = function(){
        console.log(this.name + "正在睡觉!")
    }
}
//原型方法
Animal.prototype.eat = function(food){
    console.log(this.name + "正在吃" + food);
}

这样就生成了一个Animal类,实例化生成对象后,有方法和属性

二.类的继承(js的几种继承方式)

  1. 原型链继承
  2. 借助构造函数继承(经典继承)
  3. 组合继承:原型链+借用构造函数(最常用)
  4. 原型式继承(Object.create)
  5. 寄生式继承
  6. 寄生式组合继承(最理想)
  7. ES6中的继承

(1)原型链继承

定义:子类型的原型为父类型的一个实例对像

function Parent(){
    this.name = 'big';
    this.colors = ['red','blue']
}
Parent.prototype.getName = function(){
    console.log(this.name)
}
function Child(){
    this.subName = 'litter'
}
Child.prototype = new Parent();

let child1 = new Child();
let child2 = new Child();

child1.getName(); //big

child1.colors.push('pink');

console.log(child1.colors); //['red','blue','pink']
console.log(child2.colors); //['red','blue','pink']

核心代码:Child.prototype = new Parent();

特点:
  • 父类新增在构造函数上面的方法,子类都能访问到
缺点:
  • 来自原型对象的所有属性被所有实例共享
  • 创建子类实例时,无法向父类的构造函数传参

(2)借助构造函数继承

定义:在子类的构造函数中使用call()或者apply()调用父类构造函数

function Parent(name){
    this.name = name;
    this.colors = ['red','blue']
}

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

function Child(name,age){
    //核心代码‘借调’父类型的构造函数
    Parent.call(this,name);
    this.age = age;
}

let child1 = new Child('litter');
let child2 = new Child('lucky');

console.log(child1.name); //litter
console.log(child2.name); //lucky

//这种方式只是实现部分的继承,如果父类的原型还有方法和属性,子类是拿不到这些方法和属性的

child1.getName(); //TypeError

核心代码:Parent.call(this,name);

特点:
  • 避免引用类型的属性被所有实例共享
  • 创建子类实例时,可以向父类传递参数
缺点:
  • 实例并不是父类的实例,只是子类的实例
  • 只能继承父类的实例属性和方法,不能继承原型属性和方法
  • 无法实现函数复用,每次创建实例都会创建一遍方法,影响性能

(3)组合继承:原型链+借用构造函数

function Parent(name){
    this.name = name;
    this.colors = ['red','blue'];
}
Parent.prototype.getName = function(){
    console.log(this.name);
}
function Child(name,age){
    //核心代码
    Parent.call(this,name);
    this.age = age
}
//核心代码
Child.prototype = new Parent();
Child.prototype.constructor = child;

let child1 = new Child('litter');
let child2 = new Child('lucky');

child1.getName(); //litter
child2.getName(); //lucky

child1.colors.push('pink');
//修改child1.colors不会影响child2.colors

console.log(child1.colors); //['red','blue','pink']
console.log(child2.colors); //['red','blue']

核心代码:Parent.call(this,name);Child.prototype = new Parent()

特点:
  • 融合类原型链和借用构造函数的优点,称为JavaScript中最常用的继承模式
缺点:
  • 调用类两次父类构造函数,生成类两份实例

    • 一次是设置子类型实例的原型的时候Child.prototype = new Parent();
    • 一次是创建子类实型的时候,let child1 = new Child('litter'); 调用new会执行Parent.call(this,name);,此时会再次调用一次Parent 构造函数

(4)原型式继承(Object.create)

定义:借助原型可以基于现有方法来创建对象,var B = Object.create(A) 以A对象为原型,生成A对象,B继承类A的所有属性和方法。

const person = {
    name:'star',
    colors:['red','blue']
}
//核心代码:Object.create
const person1 = Object.create(person);
const person2 = Object.create(person);

person1.name = 'litter';
person2.name = 'lucky';

person1.colors.push('yellow');

console.log(person1.colors); //['red','blue','yellow']
console.log(person2.colors); //['red','blue','yellow']

核心代码:const person1 = Object.create(person);

特点:
  • 没有严格意义上的构造函数,借助原型可以基于已有对象创建新对象
缺点:
  • 来自原型对象的所有属性被所有实例共享,person1修改colors会影响person2的colors,这点跟原型链继承一样。

(5)寄生式继承

定义:创建一个用于封装继承过程的函数,该函数在内部以某种方式来增强对象

function createObj(original){
    //通过调用函数创建一个新对象
    var clone = Object.create(original);
    //以某种方式来增强这个对象
    clone.sayName = function(){
        console.log('hi');
    }
    //返回这个对象
    return clone;
}
缺点:
  • 每次创建对象都会创建一遍方法,跟构造函数模式一样

(6)寄生组合式继承(最理想)

JavaScript最常用继承模式:组合继承(原型链+借用构造函数),缺点:调用两次父构造函数(Child.prototype = new Parent();let child1 = new Child('litter');

怎么可以只调用一次?可以让Child.prototype访问到Parent.prototype

如果直接使用Child.prototype = Parent.prototype,在修改Child.prototype的时候会修改Parent.prototype

可以使用Object.create()实现,Object.create MDN上解释:它会创建一个新对象,使用现有的对象来提供新创建的对象的__prototype__

function Parent(name){
    this.name = name;
    this.colors = ['red','blue'];
}
Parent.prototype.getName = function(){
    console.log(this.name);
}
function Child(name,age){
    //核心代码
    Parent.call(this,name);
    this.age = age;
}
//核心代码
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

核心代码:Praent.call(this,name);Child.prototype = Object.create(Parent.prototype);

(7)ES6中class的继承

定义:ES6中引入了class关键字,可以通过extends关键字实现继承

class Parent{}
class Child extends Parent{
    constructor(name,age,color){
        super(name,age);
        this.color = color;
    }
    toString(){
        return this.color + ''+super.toString();
    }
}

class关键字只是原型的语法糖,JavaScript继承仍然是基于原型实现的。