JS继承

JS继承就是子类继承父类的特征和行为,使得子类具有父类的属性和方法。

简单介绍下实例和原型:

  const instance = new Object(); 

  实例为instance,构造函数为Object,构造函数拥有一个prototype的属性指向原型,因此原型为const prototype = Object.prototype;

  这里可以看出三者关系:实例.__proto__ === 原型 ;

               原型.constructor === 构造函数 ;

             构造函数.prototype === 原型 ;

JS继承的6种方式。

1、原型链继承

核心:把父类实例作为子类的原型

优点:方法复用

缺点:①创建子类实例的时候不能传父类的参数;

   ②子类实例共享了父类构造函数的引用属性;

   ③无法实现多继承。

例子:function Parent(name){

     this.name = name || '默认值' ;

       this.arr = [1];

      }

   Parent.prototype.say = function () {

        console.log("hello");

   }

   function Child(like) {

     this.like = like;

   }

   Child.prototype = new Parent(); //核心,把Parent的实例作为Child的原型,但是此时的构造函数为Parent

   Child.prototype.constructor = Child;//将构造函数指向Child

   //创建两个实例对象

   let  array1 = new Child('小红');

   let  array2  = new Child('小明');

   console.log( array1.say() === array2.say() );//true,实现优点方法复用

   console.log( array1.name);//默认值,不能向父类传递参数

   console.log( array2.name);//默认值,不能向父类传递参数

   array1.arr.push(2);

   console.log(array2.arr);//[1,2],子类实例共享了父类构造函数的引用属性

 

2、借用构造函数

核心:借用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类

优点:①创建子类实例,可以向父类构造函数传参数;

   ②子类实例不共享父类构造函数的引用属性;

   ③可以显示多继承。

缺点:①父类方法不能复用;

   ②子类实例,继承不了父类原型上的属性(因为没有用到原型)。

   function Parent(name){

     this.name = name || '默认值';

     this.arr = [1];

     this.say = function(){

       console.log('hello');

     }

   }

   function Child(name,like){

     Parent.call(this,name);//核心,拷贝了父类的实例属性和方法

     this.like = like;

   }

   let array1 = new Child('小红','猫');

   let array2 = new Child('小明','狗');

   console.log(array1.name,array2.name);//小红,小明,可向父类构造函数传参

   array1.arr.push(2);

   console.log(array1.arr,array2.arr);//[1, 2],[1],不共享父类构造函数的引用属性

   console.log(array1.say === array2.say);//false,方法不能复用

   Parent.prototype.white = function(){

     console.log('写字');

   }

   console.log(array1.white);//undefined,不能继承父类原型上的方法

3、组合继承

核心:通过调用父类构造函数,继承父类的属性并保存传参的优点;然后通过将父类实例作为子类原型,实现函数复用

优点:①创造子类实例,可以向父类构造函数传参;

   ②父类的方法定义在父类的原型对象上,实现方法复用;

   ③不共享父类的引用属性。

缺点:调用了两次父类的构造方法,会存放一份多余的父类实例属性

   function Parent(name){

     this.name = name || '默认值';

     this.arr = [1];

   }

   Parent.prototype.say = function(){

     console.log('hello');

   }

   function Child(name,like){

     Parent.call(this,name,like);//核心,拷贝了父类的实例属性和方法

     this.like = like;

   }

   Child.prototype = new Parent();//核心,把Parent的实例作为Child的原型

   Child.prototype.constructor = Child;

   let array1 = new Child('小红','猫');

   let array2 = new Child('小明','狗');

   console.log(array1.name,array2.like);//小红 狗,可以向父类构造函数传参

   console.log(array1.say === array2.say);//true,可以复用父类原型上的方法

   array1.arr.push(2);

   console.log(array1.arr,array2.arr);// [1, 2] [1],不共享父类的引用属性

4、组合继承优化

核心:通过这种方式,去掉父类的实例属性,这样在调用父类的构造函数时,不会初始化两次实例,避免组合继承的缺点

优点:①只调用一次父类构造函数;

   ②可以向父类构造函数传参;

   ③父类的实例方法定义在父类的原型对象上,可以实现方法复用。

缺点:修正构造函数的指向之后,父类实例的构造函数指向,同时也发生改变

   function Parent(name){

     this.name = name || '默认值';

     this.arr = [1];

   }

   Parent.prototype.say = function(){

     console.log('hello');

   }

   function Child(name,like){

     Parent.call(this,name,like);//核心,拷贝了父类的实例属性和方法

     this.like = like;

   }

   Child.prototype = Parent.prototype;//核心,子类原型和父类原型实际上是一个

   let array1 = new Child('小红','猫');

   let array2 = new Child('小明','狗');

   console.log(array1.name,array2.like);//小红 狗,可以向父类构造函数传参

   console.log(array1.say === array2.say);//true,可以复用父类原型上的方法

   let p1 = new Parent('父类');

   console.log(array1.constructor);//Parent

   console.log(p1.constructor);//Parent

   Child.prototype.constructor = Child;

   console.log(array1.constructor);//Child

   console.log(p1.constructor);//Child

5、寄生组合继承

   function Parent(name){

     this.name = name || '默认值';

     this.arr = [1];

   }

   Parent.prototype.say = function(){

     console.log('hello');

   }

   function Child(name,like){

     Parent.call(this,name,like);//核心,拷贝了父类的实例属性和方法

     this.like = like;

   }

   Child.prototype = Object.create(Parent.prototype);//核心,通过创建中间对象,子类原型和父类原型就会隔离开

   let array1 = new Child('小红','猫');

   let array2 = new Child('小明','狗');

   console.log(array1.name,array2.like);//小红 狗,可以向父类构造函数传参

   console.log(array1.say === array2.say);//true,可以复用父类原型上的方法

   array1.arr.push(2);

   console.log(array1.arr,array2.arr);//[1,2] [1],不共享父类构造函数的引用属性

   let p1 = new Parent();

   console.log(array1.constructor);//Parent

   console.log(p1.constructor);//Parent

   Child.prototype.constructor = Child;

   console.log(array1.constructor);//Child

   console.log(p1.constructor);//Parent

6、基于ES6的extend方法(寄生组合继承的语法糖)

   class Parent{

     constructor(name){

       this.name = name || '默认值';

       this.arr = [1];

     }

   }

   class Child extends Parent{

     constructor(){
       super('父类');

       this.name = '小红';

     }

   }

   let array1 = new Child();

   let array2 = new Child();

   console.log(array1,array2);

    

 

posted @ 2022-07-15 11:00  我爱敲代码0000  阅读(454)  评论(0编辑  收藏  举报