原型链继承

缺点:

  1. 引用类型的属性被所有实例共享。
  2. 在创建 Child 的实例时,不能向 Parent 传参。
  1. function Parent () {
  2. this.names = ['kevin', 'daisy'];
  3. }
  4. Parent.prototype.getName = function () {
  5. console.log(this.names);
  6. }
  7. function Child () {
  8. }
  9. Child.prototype = new Parent();
  10. var child1 = new Child();
  11. var child2 = new Child();
  12. console.log(child1.getName()) // ['kevin', 'daisy']
  13. child1.names.push('yoyo')
  14. console.log(child1.getName()) // ['kevin', 'daisy', 'yoyo']
  15. console.log(child2.getName()) // ['kevin', 'daisy', 'yoyo']

借用构造函数(经典继承)

优点:

  1. 避免了引用类型的属性被所有实例共享。
  2. 可以在 Child 中向 Parent 传参。
  1. function Parent () {
  2. this.names = ['kevin', 'daisy'];
  3. }
  4. function Child () {
  5. Parent.call(this);
  6. }
  7. var child1 = new Child();
  8. child1.names.push('yayu');
  9. console.log(child1.names); // ["kevin", "daisy", "yayu"]
  10. var child2 = new Child();
  11. console.log(child2.names); // ["kevin", "daisy"]

组合继承

原型链继承和经典继承的结合。

  1. function Parent (name) {
  2. this.name = name;
  3. this.colors = ['red', 'blue', 'green'];
  4. }
  5. Parent.prototype.getName = function () {
  6. console.log(this.name)
  7. }
  8. function Child (name, age) {
  9. Parent.call(this, name);
  10. this.age = age;
  11. }
  12. Child.prototype = new Parent();
  13. Child.prototype.constructor = Child;
  14. var child1 = new Child('kevin', '18');
  15. child1.colors.push('black');
  16. console.log(child1.name); // kevin
  17. console.log(child1.age); // 18
  18. console.log(child1.colors); // ["red", "blue", "green", "black"]
  19. var child2 = new Child('daisy', '20');
  20. console.log(child2.name); // daisy
  21. console.log(child2.age); // 20
  22. console.log(child2.colors); // ["red", "blue", "green"]

寄生式继承

创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来增强对象,最后返回对象。

  1. function createObj (o) {
  2. var clone = Object.create(o);
  3. clone.sayName = function () {
  4. console.log('hi');
  5. }
  6. return clone;
  7. }

寄生组合式继承(重点)

用一个全新的例子解释

  1. function Human(options) {
  2. this.name = options.name
  3. this.肤色 = options.肤色
  4. }
  5. Human.prototype.eat = function() {}
  6. Human.prototype.drink = function() {}
  7. Human.prototype.poo = function() {}
  8. function Soldier(options) {
  9. Human.call(this, options)
  10. this.ID = options.ID
  11. this.生命值 = 42
  12. }
  13. // 目的:让 Solider.prototype 可以访问 Human.prototype
  14. // 写法一:使用 __proto__(不行)
  15. // 原因:在生产环境里面不能使用 __proto__
  16. Soldier.prototype.__proto__ = Human.prototype
  17. // 写法二:借助 new 的特性(不行)
  18. // 原因:造成实例和原型对象都有相同的属性
  19. Soldier.prototype = new Human({ name: 'xxx', 肤色: 'yellow' })
  20. // 写法三:借助中转函数(行)
  21. // 原因:即利用了 new 的特性,也避免的重复属性。
  22. function fakeHuman(){}
  23. fakeHuman.prototype = Human.prototype
  24. Soldier.prototype = new fakeHuman()
  25. // 等价于
  26. Soldier.prototype = Object.create(Human.prototype)
  27. Soldier.prototype.兵种 = '美国大兵'
  28. Soldier.prototype.攻击力 = 5
  29. Soldier.prototype.行走 = function() {}
  30. Soldier.prototype.奔跑 = function() {}
  31. Soldier.prototype.死亡 = function() {}
  32. Soldier.prototype.攻击 = function() {}
  33. Soldier.prototype.防御 = function() {}
  34. var s = new Soldier({ name: 'xxiang', 肤色: 'yellow', ID: 1 })

es6 class 的写法

  1. class Human {
  2. constructor(options) {
  3. this.name = options.name
  4. this.肤色 = options.肤色
  5. }
  6. // 不支持非函数
  7. eat() {}
  8. drink() {}
  9. poon() {}
  10. }
  11. class Soldier extends Human {
  12. constructor(options) {
  13. super(options)
  14. this.ID = options.ID
  15. this.生命值 = 42
  16. this.兵种 = '美国大兵'
  17. this.攻击力 = 5
  18. }
  19. // 不支持非函数
  20. 行走() {}
  21. 奔跑() {}
  22. 死亡() {}
  23. 攻击() {}
  24. 防御() {}
  25. }
  26. var s = new Soldier({ name: 'xxiang', 肤色: 'yellow', ID: 1 })

但是 class 写法也是有缺点的

  1. 共有属性不支持非函数。
  2. typeof className 竟然是个函数,但是这个函数只能用 new 来调用不能执行(只能当作特殊函数)。

image.png

参考:
[1] JS 的 new 到底是干什么的?
[2] JavaScript深入之继承的多种方式和优缺点