继承(extend)

继承(面试题)

类与类之间的继承 三种方式

1.原型链继承 ( extend-prototype)

原型继承 子类的原型指向父类的实例

<script>
       //继承 :类与类之间的继承 三种
       // 1.原型继承
     
       //父类
       function Animal(a, b) {
           //属性
           this.a = a;
           this.b = b;
      }
       Animal.prototype.say = function () {
           console.log('My name is ' + this.a)
      }
       
   
       //子类
       function Dog() {
           this.c = 5;
           this.zy = function () {
               console.log('哈哈哈');
          }
      }
     
   
       //原型继承 子类的原型指向父类的实例  
       Dog.prototype = new Animal(1, 2);
     
   
       var snoopy = new Dog()
       console.log(snoopy); //Dog {c: 5, zy: ƒ}__proto__: Animal a: 1 b: 2
       console.log(snoopy.a) // 1
       snoopy.say() //My name is 1
       snoopy.zy()  // 哈哈哈
 
       //instanceof:运算符。判断当前对象是否是另一个对象的实例
       console.log(snoopy instanceof Dog);  //true
       console.log(snoopy instanceof Animal); //true
   </script>

特点

1.非常纯粹的继承关系,实例是子类的实例,也是父类的实例

2.父类新增原型方法/原型属性,子类都能访问到

3.简单,易于实现

缺点

1.要想为子类新增属性和方法,必须要在new Animal()这样的语句之后执行,不能放到构造器中

2.无法实现多继承

3.来自原型对象的引用属性是所有实例共享的

4.创建子类实例时,无法向构造函数传参

2、构造继承 (call 、apply 继承) extend-constructor

构造继承不考虑原型

把父类的构造函数当成普通函数在子类的构造函数里调用,并且修改this指向

 <script>
       //第二种 借用继承 寄生继承
       // 父类

       function Animal(a, b) {
           this.a = a;
           this.b = b;
           this.aa = function () {
               console.log(55);

               return 555;
          }
      }
       Animal.prototype.say = function () {
           console.log("66")
      }
       // 子类
       function Dog(a, b) {
           this.c = 666;
           // 把父类的构造函数当成普通函数在子类的构造函数里调用,并且修改this指向
           // 这里Animal作为普通的全局函数再调用,所以里面的this指window
           // 所以希望在调用Animal的时候把this指向外层Cat的this
           // call或者apply a,b实参
           // Animal.apply(this, [ a, b ])
           Animal.call(this, a, b)
      }
       var snoopy = new Dog(1, "2");
       console.log(snoopy);
       console.log(snoopy.a); // 蓝色1
       console.log(snoopy.b);// 黑色2
       // console.log(snoopy.say());//报错 ,继承不了原型上的方法
       snoopy.aa() // 55
       console.log(snoopy.aa()); // 555
       var rr = new Animal(1, 2)
       rr.say()
       // console.log( snoopy.aa());
       console.log(snoopy instanceof Dog) // true
       console.log(snoopy instanceof Animal) // false
   </script>

特点

1、解决了1中,子类实例共享父类引用属性的问题

2、创建子类实例时,可以向父类传递参数

3、可以实现多继承(call多个父类对象)

缺点

1、实例并不是父类的实例,只是子类的实例

2、只能继承父类的实例属性和方法,不能继承原型属性/方法

3、无法实现函数复用,每个子类都有父类实例函数的副本,影响性能

3、组合继承(原型链和构造继承的组合) extend-group

组合继承是原型链继承和构造继承的组合

  <script>

       // 组合继承是原型链继承和构造继承的组合
       function Animal(name) {
           this.name = name
      }
       Animal.prototype.say = function () {
           console.log('My name is ' + this.name)
      }

       function Pig(name) {
           Animal.call(this, name)
      }
       Pig.prototype = new Animal()

       var peiqi = new Pig('Peiqi')
       peiqi.say()

       var qiaozhi = new Pig('Qiaozhi')
       qiaozhi.say()

       console.log(peiqi.say === qiaozhi.say) // true

       console.log(peiqi instanceof Pig)  //true
       console.log(peiqi instanceof Animal)  //true
   </script>

4、ES6继承(语法糖) extend-class

extends关键字相当于实现原型链继承

Dog.prototype = new Animal('Snoopy')

super是ES6新增的方法,这个方法在继承子类的构造函数里调用

相当于Animal.call(this, name, age)

  <script>
       class Animal {
           constructor (name, age) {
               this.name = name
               this.age = age
          }
           say () {
               console.log(`My name is ${this.name}, I am ${this.age} years old`)
          }
      }
       // extends关键字相当于实现原型链继承
       class Mouse extends Animal {
           constructor (name, age) {
               // super是ES6新增的方法,这个方法在继承子类的构造函数里调用
               // 相当于Animal.call(this, name, age)
               super(name, age)
          }
      }
       var jerry = new Mouse('Jerry', 80)
       jerry.say()

       console.log(jerry instanceof Mouse) // true
       console.log(jerry instanceof Animal) // true

   </script>

5、静态属性

        // 静态属性 是指 只能有 构造函数调用,实例无法调用的属性
       // 不是静态属性 则只能有 实例调用,构造函数无法调用的属性
       /*
      两种写法:
          1.类内部 定义属性时 加上 static关键字
          2,在外部通过 类.属性名 = 值 定义
        */
       class Animal {
           a = 10;
           b = 20;
           c = () => {
               console.log(11)
               console.log(this);
          }
           d = "我是一个静态afa f属性";
           static e = () => {
               console.log("我是静态adad方法")
          }
           p = function () {
               console.log("bushi静态");

          }
      }
       var pig = new Animal()
       console.log(typeof Animal); // function
       console.log(typeof pig);  //object

       // Animal.e() "我是静态adad方法"
       //   pig.p() "我是adad方法"
       //   pig.e   pig.e is not a function
       // Animal.p() Animal.p is not a function

   // Animal().p() // 报错 在没有“new”的情况下无法调用类构造函数Animal
       new Animal().p() //bushi静态

6、定义属性

第一种方法
        // 定义属性的第一种方法
        class Animal {
           a = 10;
           b = 20;
           c = () => {
               console.log(this, 11);
          }
      }
       var a1 = new Animal();
       console.log(a1)  //Animal {a: 10, b: 20, c: ƒ}
       a1.c();  //Animal {a: 10, b: 20, c: ƒ} 11
第二种方法 建议使用
        class Animal {
           constructor(name, age) {
               // 在new 类自动调用的 this指向实例
               // 属性第二种定义方法 是在constructor中定义 建议写法
               this.name = name;
               this.age = age;
          }
           a() {
               console.log(this, 1)
          }
      }
       var a2 = new Animal("小红", 20);
       console.log(a2);  // Animal {name: "小红", age: 20}
       console.log(a2.a === Animal.prototype.a)  // true
       a2.a() // Animal {name: "小红", age: 20} 1

 

原文地址:https://www.cnblogs.com/lilamisu/p/13226929.html