JS面向对象编程笔记(三):5种继承方式
  # 开头
笔记视频内容源自B站JavaScript从入门到放弃 第十二章 面向对象编程 (opens new window),笔记为自行整理复习使用,欢迎一同学习交流,转载请通知原作者
# 三、五种继承
# 3.1、原型链继承
js的继承:在原型对象的所有属性和方法都能被实例所共享:
<body>
    <script>
      //定制一个Animal
      function Animal() {
        this.name = "a";
      }
      Animal.prototype.getName = function () {
        return this.name;
      };
      function Dog() {}
      //Dog继承了Aniaml
        
      //本质:重写原型对象,将一个实例中的父对象的属性和方法作为一个子对象的原型对象的属性和方法
      Dog.prototype = new Animal();
      Dog.prototype.constructor = Dog;
      var d1 = new Dog();
      console.log(d1.name);
      console.log(d1.getName());
      
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
将代码转化为图解为:
原型链继承的问题:
1、父类中的实例属性一旦赋值给子类的原型属性(prototype),此时这些属性都属于子类的共享属性
2、实例化子类型的时候不能向父类型的构造函数传参,所以实践中很少单独用原型链继承,了解即可
<body>
    <script>
      //定制一个Animal
      function Animal() {
        this.name = "a";
        this.colors = ['red','green','blue'];
      }
      Animal.prototype.getName = function () {
        return this.name;
      };
      function Dog() {
      }
      //Dog继承了Aniaml
      Dog.prototype = new Animal();
      Dog.prototype.constructor = Dog;
      var d1 = new Dog();
      var d2 = new Dog();
      console.log(d1.colors);
      console.log(d2.colors);
      d1.colors.push('purple')
      console.log(d1.colors);
      console.log(d2.colors);
      
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 3.2、借用构造函数继承
也叫经典继承:在子类的构造函数内部去调用父类的构造函数
<body>
    <script>
      function Animal() {
        this.name = "a";
        this.colors = ["red", "green", "blue"];
      }
      Animal.prototype.getName = function () {
        return this.name;
      };
      function Dog() {
          //继承了Animal
        Animal.call(this);
      }
      var d1 = new Dog();
      console.log(d1.name);
      console.log(d1.colors);
      console.log(d1.getName());
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
好处是内部的变量不会被共享:
var d1 = new Dog();
var d2 = new Dog();
d1.colors.push('purple')
 2
3
也解决了原型链继承无法传递参数的问题:
<body>
    <script>
      function Animal(name) {
        this.name = name;
        this.colors = ["red", "green", "blue"];
      }
      Animal.prototype.getName = function () {
        return this.name;
      };
      function Dog(name) {
          //继承了Animal
          //当new实例的时候,内部构造函数中的this指向了d1,然后在当前的构造函数内部再去调用父类的通过call方法调用构造函数,父类中的构造函数中的this就指向了d1,但是方法不能被继承下来
        Animal.call(this,name);
      }
      var d1 = new Dog('chen');
      var d2 = new Dog();
      d1.colors.push('purple')
      console.log(d1.name);
      console.log(d1.colors);
    //   console.log(d1.getName());
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
缺点:父类中定义的共享方法不能被子类所继承下来
# 3.3、组合继承
组合继承结合了原型链继承与构造函数继承的优点。让父类的实例属性继承下来,实例修改引用类型的值,另一个实例也不会发生变化,通过重写原型对象,把父类共享的方法继承下来:
<body>
    <script>
      function Animal(name) {
        this.name = name;
        this.colors = ["red", "green", "blue"];
      }
      Animal.prototype.getName = function () {
        return this.name;
      };
      function Dog(name) {
        //继承了Animal
        Animal.call(this, name);
      }
      Dog.prototype = new Animal();
      Dog.prototype.constructor = Dog;
      var d1 = new Dog("chen");
      var d2 = new Dog("chen2");
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
**问题:**无论在什么情况下,都会调用父类的构造函数两次
- 一个是我们初始化子类的原型对象的时候
 - 在子类构造函数内部调用父类的构造函数
 
# 3.4、寄生组合式继承(重要)
通过Object.create方法将Animal的原型对象传入创建出的实例赋值给Dog,减少了 一次构造函数调用,这个方法实用度最广
<body>
    <script>
      function Animal(name) {
        this.name = name;
        this.colors = ["red", "green", "blue"];
      }
      Animal.prototype.getName = function () {
        return this.name;
      };
      function Dog(name) {
        //继承了Animal
        Animal.call(this, name);
      }
      Dog.prototype = Object.create(Animal.prototype)
      Dog.prototype.constructor = Dog;
      var d1 = new Dog("chen");
      var d2 = new Dog("chen2");
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 3.5、总结
1、原型链继承 **特点:**重写子类的原型对象,父类原型对象上的属性和方法都会被子类继承 **问题:**在父类中定义的实例引用类型的属性,一旦被修改,其它的实例也会被修改,当实例化子类的时候,不能传递参数到父类 2、借用构造函数模式 **特点:**在子类构造函数内部间接调用(call(),apply(),bind())父类的构造函数 **原理:**改变父类中的this指向 **优点:**仅仅的是把父类中的实例属性当做子类的实例属性,并且还能传参 **缺点:**父类中公有的方法不能被继承下来 3、组合继承 **特点:**结合了原型链继承和借用构造函数继承的优点 **原型链继承:**公有的方法能被继承下来 **借用构造函数:**实例属性能被子类继承下来 **缺点:**调用了两次两次父类的构造函数
		1.实例化子类对象
		2.子类的构造函数内部(好)
4、寄生组合式继承
       var b = object.create(a);
		将a对象作为b实例的原型对象
		把子类的原型对象指向了父类的原型对象
		Dog . prototype = Object. create( Animal . prototype)
		开发过程中使用最广泛的一种继承模式
# 3.6、多重继承
js不允许一个对象同时继承多个对象,但是有变通的方法,通过Object.assign来拷贝原型对象的方法实现多重继承
<body>
    <script>
      function Person() {
        this.name = "Person";
      }
      Person.prototype.sayName = function () {
        console.log(this.name);
      };
      //定制Parent
      function Parent() {
        this.age = 30;
      }
      Parent.prototype.sayAge = function () {
        console.log(this.age);
      };
      function Me() {
        //继承Person的属性
        Person.call(this);
        Parent.call(this);
      }
      //继承Person的方法
      Me.prototype = Object.create(Person.prototype);
      //不能重写原型对象来实现另一个对象的继承
      //混入技术 Mixin
      //   Me.prototype = Object.create(Parent.prototype)
      //   Object.assign(targetObj,copyObj)
      Object.assign(Me.prototype, Parent.prototype);
      Me.prototype.constructor = Me;
      var me = new Me();
    </script>
  </body>
 2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37