学习js继承的6种方式

732 阅读4分钟

关于原型,原型链,构造函数和实例关系,可以参考上一篇文章

地址:juejin.cn/post/684490…

js 实现继承的方式一:原型链继承

function Father(){
    this.name = '父亲';
}
Father.prototype.sayName = function(){
    console.log(this.name);
}
function Child(){}
Child.prototype = new Father();
var child1 = new Child();
child1.sayName();//父亲

原型链继承存在的问题

1.父类中的引用类型的属性被所有子类共享; 2.子类不能向父类传递参数

例子:

function Father(){
    this.names = ['kevi','jack'];
}
function Son(){
    
}
Son.prototype = new Father();
var son1 = new Son();
var son2 = new Son();
son1.names.push('jhs');
console.log(son1.names);//["kevi", "jack", "jhs"]
console.log(son2.names);//["kevi", "jack", "jhs"]

js 实现继承的方式二:构造函数绑定法(借用构造函数法)

function Animal(job){
    this.species = '动物';
    this.ages = [1,2,3];//父类中存在引用类型的属性
    this.job = job;
}
function Dog(name,color){
    this.name = name;
    this.color = color;
}
//怎么样才能使Dog继承Animal呢?
//重新改写Dog
function Dog(name,color,job){
    //Animal.apply(this,arguments);
    //或
    Animal.call(this,job);
    this.name = name;
    this.color =color;
}
let dog1 = new Dog('大毛','黑色','eat');
console.log(dog1.name);//大毛
console.log(dog1.species);//动物
console.log(dog1.job);//eat
dog1.ages.push(4);
console.log(dog1.ages);//[1,2,3,4]
let dog2 = new Dog('二毛','灰色');
console.log(dog2.ages);//[1,2,3]

构造函数绑定法的优缺点:

优点: 1.避免了父类中的引用类型的属性被所有子类共享的问题; 2.可以向父类中传参;

缺点: 1.方法或属性定义在构造函数中,不能够复用这些属性或方法;(方法都在构造函数中定义,每次创建实例都会创建一遍方法。) 2.父类在原型中定义的方法对于子类不可见

function Animal(){
    this.species = '动物';
    this.sayHello = function(){
        console.log('hello world');
    }
}
Animal.prototype.saySpecies = function(){
    console.log(this.species);
}
function Dog(name,color){
    this.name = name;
    this.color = color;
}
//怎么样才能使Dog继承Animal呢?
//重新改写Dog
function Dog(name,color){
    Animal.apply(this,arguments);
    //或
    //Animal.call(this);
    this.name = name;
    this.color =color;
}
const dog1 = new Dog('大毛','黑色');
console.log(dog1.name);//大毛
console.log(dog1.species);//动物
dog1.saySpecies();

js 实现继承的方式三:组合继承

function Parent(name){
    this.name = name;
    this.colors = ['yellow','red','black'];
}
Parent.prototype.sayName = 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('kevin',12);
child1.colors.push('blue');
console.log(child1.name);//kevin
console.log(child1.age);//12
console.log(child1.colors);//['yellow','red','black','blue']
child1.sayName();//kevin

let child2 = new Child('jeck',11);
console.log(child2.name);//jeck
console.log(child2.age);//11
console.log(child2.colors);//['yellow','red','black']
child2.sayName();//jeck

组合继承的优点和缺点

优点: 1.子类可以向父类传递参数 2.父类中的引用类型的属性不会被所有实例共享 3.父类原型中定义的方法可以被子类继承 4.可以实现方法共享 5.是js实现继承的最常用方法

缺点: 1.两次调用父类构造函数; 一次是:Child.prototype = new Parent(); 另一次是:执行 let child1 = new Child('kevin',12)时候,其实调用了Parent.call(this,name);

js 实现继承的方式四:原型式继承

function createObj(o){
    function F(){}
    F.prototype = o;
    return new F();
}

var obj = {
    name:'kevin',
    colors:['red','blue','white']
}
var obj1 = createObj(obj);
var obj2 = createObj(obj);
console.log(obj1.name);//kevin
console.log(obj2.name);//kevin

obj1.name = 'kkk';
console.log(obj1.name);//kkk
console.log(obj2.name);//kevin

obj1.colors.push('green');
console.log(obj2.colors);//["red", "blue", "white", "green"]
//原型式继承中的引用类型的属性也会被共享,和原型链继承相似

原型式继承是Object.create()的模拟实现; Object.create(obj,[propertiesObject]); 接受两个参数,第一个参数为新创建对象的原型对象,第二个参数为可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数

const friend = {
    name:'lili',
}
let f1 = Object.create(friend,{
    name :{
        value:'mimo'
    }
});
console.log(f1.name);

原型式继承存在的缺点

1.原型式继承中的引用类型的属性也会被共享,和原型链继承相似

js 实现继承的方式五:寄生式继承

function createObj(o){
    var clone = Object.create(o);
    clone.sayName = function(){
        console.log('你正在使用寄生式继承');
    }
    return clone;
}
var obj = {
    name:'lll'
}
var obj1 = createObj(obj);
obj1.sayName();//你正在使用寄生式继承

寄生式继承的缺点

1.通过使用寄生式继承为对象添加方法,不能实现方法的共享,这一点和借用构造函数方式一致

js 实现继承的方式六:寄生组合式继承

我们一般认为组合继承是最常用的继承方式,不过组合继承也有自己的不足之处,那就是两次调用父类的构造函数,一次在创建子类原型的时候,一次在子类构造函数内部;寄生组合式继承就是为了降低调用父类构造函数开销而出现的; 背后的原理就是:不必为了指定子类型的原型而调用父类型的构造函数

function createObj(o){
    function F(){}
    F.prototype = o;
    return new F();
}
function prototype(child,parent){
    var prototype = createObj(parent.prototype);
    prototype.constructor = child;
    child.prototype = prototype;
}

//使用
prototype(child,parent);

//例子

 function createObj(o){
        function F(){}
        F.prototype = o;
        return new F();
    };
    function prototype1(child,parent){
        var pro = createObj(parent.prototype);
        pro.constructor = child;
        child.prototype = pro;
    };
    function Parent1 (){
       this.name='kk';
       this.age = 12;
    };
    Parent1.prototype.say=function(){
         console.log(this.name);
      }
    function Child1(){
        Parent1.call(this);
        this.name='ll'
     }
    //使用
    prototype1(Child1,Parent1);
    var son = new child1();
    console.dir(son.say());//ll

寄生组合式继承的优点

1.只调用了一次父类的构造函数; 2.避免了在子类的原型上创建不必要的,多余的属性; 3.原型链保持不变,因此能够正常使用instanceof 和isPrototypeOf()