2 继承的多种方式(4.20)

TODO

  • 尚未理解使用场景 ```javascript // 1 原型链继承 function Parent() { this.name = ‘kevin’ } Parent.prototype.getName = function () { console.log(this.name) } function Child() {} Child.prototype = new Parent() var child1 = new Child() console.log(child1.getName()) // kevin

// 问题 // — 1 引用类型的属性被所有实例共享 // — 2 在创建 Child 的实例时,不能向 Parent 传参 function Parent() { this.name = [‘kevin’, ‘daisy’] } function Child() {} Child.prototype = new Parent() var child1 = new Child() child1.names.push(‘yayu’) console.log(child1.names) // [‘kevin’, ‘daisy’, ‘yayu’] var child2 = new Child() console.log(child2.names) // [‘kevin’, ‘daisy’, ‘yayu’]

// 2 借用构造函数(经典继承) function Parent() { this.names = [‘kevin’, ‘daisy’] } function Child() { Parent.call(this) } var child1 = new Child() child1.names.push(‘yayu’) console.log(child1.names) // [‘kevin’, ‘daisy’, ‘yayu’] var child2 = new Child() console.log(child2.names) // [‘kevin’, ‘daisy’]

// 优点 // 1 避免了引用类型的属性被所有实例共享 // 2 可以在 Child 中向 Parent 传参 function Person(name) { this.name = name } function Child(name) { Parent.call(this, name) } var child1 = new Child(‘kevin’) console.log(child1.name) // kevin var child2 = new Child(‘daisy’) console.log(child2.name) // daisy

// 缺点 // 方法都在构造函数中定义,每次创建实例都会创建一遍方法

// 3 组合继承 // 原型链继承 和 经典继承 双剑合璧 // 优点:融合原型链继承和构造函数的优点,是 JS 中最常用的继承模式

// 4 原型式继承 // ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型 // 缺点:包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样

// 5 寄生式继承 // 创建一个仅用于封装继承过程的函数,该函数在内部以某种形式来做增强对象,最后返回对象 // 缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法

// 6 寄生组合式继承

  1. <a name="KD4Tt"></a>
  2. ## 1 从原型到原型链(4.19)
  3. ![图片.png](https://cdn.nlark.com/yuque/0/2022/png/394112/1650374110535-a02eb679-1d43-4c5f-a78d-632132efa2aa.png#clientId=u32840c78-afbb-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=263&id=u317a11e9&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=525&originWidth=590&originalType=binary&ratio=1&rotation=0&showTitle=false&size=42740&status=done&style=none&taskId=u9d0e83ff-91c5-4011-b6be-e65b3db6cdd&title=&width=295)
  4. ```javascript
  5. // 1 构造函数创建对象
  6. function Person() {} // Person 就是一个构造函数
  7. var person = new Person() // 使用 new 创建了一个实例对象 person
  8. person.name = 'Kevin'
  9. console.log(person.name)
  10. // 2 prototype
  11. // -- 每个函数都有一个 prototype 属性
  12. // -- prototype 是函数才会有的属性
  13. // 函数的 prototype 属性到底指向的是什么呢?是这个函数的原型吗?
  14. // -- 函数的 prototype 属性指向了一个对象,这个对象是调用该构造函数而创建的实例的原型
  15. // == 这个例子中的 person1 和 person2 的原型
  16. function Person() {}
  17. Person.prototype.name = 'Kevin'
  18. var person1 = new Person()
  19. var person2 = new Person()
  20. console.log(person1.name)
  21. console.log(person2.name)
  22. // -- 怎么表示实例与实例原型,也就是 person 和 Person.prototype 之间的关系
  23. // 3 _proto_
  24. // -- 这是每个 JavaScript 对象(除了 null) 都具有的一个属性,叫 _proto_, 这个属性会指向该对象的原型
  25. function Person() {}
  26. var person = new Person()
  27. console.log(person._proto_ === Person.prototype) // true
  28. // 补充-- _proto_
  29. // 绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中
  30. // 实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,
  31. // 当使用 obj.__proto__ 时,可以理解成返回了 Object.getPrototypeOf(obj)。
  32. // 4 constructor
  33. // 实例对象和构造函数都可以指向原型,那么原型是否有属性指向构造函数或者实例?
  34. // -- 指向实例没有,因为一个构造函数可以生成多个实例
  35. // -- 原型指向构造函数是有的
  36. // 每个原型都有一个 constructor 属性指向关联的构造函数
  37. function Person() {}
  38. console.log(Person === Person.prototype.constructor)
  39. // 补充-- constructor
  40. function Person() {}
  41. var person = new Person()
  42. console.log(person.constructor === Person.prototype.constructor)
  43. // 当获取 person.constructor 时,person 中并没有 constructor 属性
  44. // -- 当不能读取到 constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:
  45. person.constructor === Person.prototype.constructor
  46. // 5 实例与原型
  47. // 当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性
  48. // 如果还查不到,就去找原型的原型,一直找到最顶层为止
  49. function Person() {}
  50. Person.prototype.name = 'Kevin'
  51. var person = new Person()
  52. person.name = 'Daisy' // 给实例对象 person 添加了 name 属性
  53. console.log(person.name)
  54. delete person.name // 删除了 person 的 name 属性时,读取 person.name, 从 person 对象中找不到 name 属性就会从 person 的原型也就是 person._proto_, 也就是 Person.prototype 中查找
  55. console.log(person.name)
  56. // 6 原型的原型
  57. // 原型也是一个对象
  58. // 既然原型是对象,就可以用最原始的方式创建
  59. // 原型对象就是通过 Object 构造函数生成的,实例的 _proto_ 指向构造函数的 prototype
  60. var obj = new Object()
  61. obj.name = 'Kevin'
  62. console.log(obj.name)
  63. // 7 原型链
  64. // 由互相关联的原型组成的链状结构就是原型链
  65. // Object.prototype 的原型呢?
  66. // -- null, null 代表了什么?
  67. // 引用阮一峰老师的 《undefined 与 null 的区别》
  68. // -- null 表示没有对象,即此处不应该有值
  69. // -- 所以 Object.prototype._proto_ 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思
  70. // -- 所以查找属性的时候查到 Object.prototype 就可以停止查找了
  71. console.log(Object.prototype._proto_ === null)
  72. // 补充 -- 真的是继承吗?(这点不理解)
  73. // 关于继承,前面我们讲到“每一个对象都会从原型‘继承’属性”,
  74. // 实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:
  75. // 继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,
  76. // 相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,
  77. // 所以与其叫继承,委托的说法反而更准确些。

数组