js之继承

js中的继承有多种方式,因为其不像java一样有可以直接类继承(es6 中扩展了extend),他用的是原型链继承

首先我们来构造一个动物的父类

function Animal (name = 'Animal'){
            this.name = name;
            this.play = function(){
                console.log( this.name+"can play");
            }
        } // 定义了name 属性和play方法
        Animal.prototype.say = function(){
            console.log("hello,i can speak Animal language")
        } // 在原型链上挂载了 say 方法

 在该父类中我们有一个play的实例私有方法和一个say方法,该方法为原型链共享方法,所有的实例都可以修改该方法。

1.原型继承,将子类的原型指向父类的实例,这样子类的实例就可以通过原型链去继承父类的方法以及属性

        function Cat(){}        
        Cat.prototype = new Animal();
        Cat.prototype.name = 'lowcat';
        
        var cat = new Cat(); 
        console.log(cat.name); //输出lowcat
        console.log(cat.say()); //输出 hello, i can speak Animal language
        console.log(cat.play()); //输出 lowcat can play
        console.log(cat instanceof Animal); // true
        console.log(cat instanceof Cat); // true

优点 1. 非常纯粹的继承关系 实例是子类的实例 也是父类的实例
 2. 父类新增原型方法/原型属性 子类都能访问到
 3. 简单
 缺点 1.要想为子类新增原型属性和方法,必需要在new Animal()语句执行之后 (因为先要把原型指向搞清楚)
 2.无法实现多继承
 3.来自原型的所有属性被所有实例共享
 4.创建子类实例时,无法想父类构造函数传参(因为直接执行父类的构造函数)

2.构造继承(使用call函数改变当亲父类的上下文)

         function Dog(name = 'pony'){
             Animal.call(this);
             this.name = name ;
         }
         var dog = new Dog("tony");
         console.log(dog.name); //输出tony
         console.log(dog.play());// 输出 i can play
console.log(dog.say()); 输出错误 console.log(dog instanceof Animal); //false console.log(dog instanceof Dog); // true

优点 :1. 解决了原型继承中所有子类实例共享父类的属性与方法
 2. 创建子类实例是,可以向父类传递参数
 3. 实现了多继承
 缺点 :1.实例斌不是父类的实例,只是子类的实例
 2.只能继承父类的实例属性和方法,不能继承原型属性方法
3.无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
 此继承方式使用了call函数 改变当前子类构造函数的运行环境,所有子类能够访问到父类的方法,但却不能访问原型链,因为在原型链上并没有实质的改变;
三、工厂模式继承(内部使用obj直接作为父类的实例,然后return 回去当作子类的实例)

function Fish (name = "fish") {
             var obj = new Animal();
             obj.name = name;
             return  obj;
         }
         var fish = new Fish("fish");
         console.log(fish.name); //输出 fish
         console.log(fish.play()); // 输出 i can play
         console.log(fish.say()); // 输出hello ,i can speak Animal lanvuage
         console.log(fish instanceof Animal);// true
         console.log(fish instanceof Fish); //true

优点: 1.不限制调用方式,不管是new 还是直接调用 返回的对象具有相同的效果
 缺点:1.实例是父类的实例,不是子类的实例
            2.不支持多继承

四、拷贝继承

            function Mouse(name ="mouse"){
              var animal = new Animal();
              for(var p in animal){
                  Mouse.prototype[p] = animal[p];
              }
              Mouse.prototype.name = name;
          }
         var mouse = new Mouse("Jack");
         console.log(mouse.name); // Jack
         console.log(mouse.play()); // i can play
         console.log(mouse.say()); // hello, i can speak Animal language
         console.log(mouse instanceof Animal); //ture
         console.log(mouse instanceof Mouse) // ture

优点 1.支持多继承 (多复制几个父类)
 缺点 1.效率低,用了遍历copy(内存占用高)
          2.无法获取父类的不可枚举方法(在对象中的atrributes object(属性描述对象)可以设置对象属性的状态)

五、寄生组合继承(首先将父类的上下文拿到子类当中,在将子类的原型指向父类的原型,这里并不是直接指向)

function Bird(name ="bird"){
               Animal.call(this);
               this.name = name;
           }
           (function(){
                 var Obj = function(){}; // 首先创建一个空对象
                 Obj.prototype = Animal.prototype; // 将空对象的原型指向父类的原型
                 Bird.prototype  = new Obj(); // 将子类的原型指向 空对象的实例,见解继承了父类的原型
                 Bird.prototype.constructor = Child; // 因为此时obj的原型的constrcutor指向了obj的构造函数,手动修复指向
           })();
          var bird = new Bird();
         console.log(bird.name);
         console.log(bird.play());
         console.log(bird.say());
         console.log(bird instanceof Animal);
         console.log(bird instanceof Bird);

这个方法十分完美,解决了组合继承的两次实例化问题

原文地址:https://www.cnblogs.com/maoxiaodun/p/10007759.html