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 寄生组合式继承
<a name="KD4Tt"></a>## 1 从原型到原型链(4.19)```javascript// 1 构造函数创建对象function Person() {} // Person 就是一个构造函数var person = new Person() // 使用 new 创建了一个实例对象 personperson.name = 'Kevin'console.log(person.name)// 2 prototype// -- 每个函数都有一个 prototype 属性// -- prototype 是函数才会有的属性// 函数的 prototype 属性到底指向的是什么呢?是这个函数的原型吗?// -- 函数的 prototype 属性指向了一个对象,这个对象是调用该构造函数而创建的实例的原型// == 这个例子中的 person1 和 person2 的原型function Person() {}Person.prototype.name = 'Kevin'var person1 = new Person()var person2 = new Person()console.log(person1.name)console.log(person2.name)// -- 怎么表示实例与实例原型,也就是 person 和 Person.prototype 之间的关系// 3 _proto_// -- 这是每个 JavaScript 对象(除了 null) 都具有的一个属性,叫 _proto_, 这个属性会指向该对象的原型function Person() {}var person = new Person()console.log(person._proto_ === Person.prototype) // true// 补充-- _proto_// 绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中// 实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,// 当使用 obj.__proto__ 时,可以理解成返回了 Object.getPrototypeOf(obj)。// 4 constructor// 实例对象和构造函数都可以指向原型,那么原型是否有属性指向构造函数或者实例?// -- 指向实例没有,因为一个构造函数可以生成多个实例// -- 原型指向构造函数是有的// 每个原型都有一个 constructor 属性指向关联的构造函数function Person() {}console.log(Person === Person.prototype.constructor)// 补充-- constructorfunction Person() {}var person = new Person()console.log(person.constructor === Person.prototype.constructor)// 当获取 person.constructor 时,person 中并没有 constructor 属性// -- 当不能读取到 constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:person.constructor === Person.prototype.constructor// 5 实例与原型// 当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性// 如果还查不到,就去找原型的原型,一直找到最顶层为止function Person() {}Person.prototype.name = 'Kevin'var person = new Person()person.name = 'Daisy' // 给实例对象 person 添加了 name 属性console.log(person.name)delete person.name // 删除了 person 的 name 属性时,读取 person.name, 从 person 对象中找不到 name 属性就会从 person 的原型也就是 person._proto_, 也就是 Person.prototype 中查找console.log(person.name)// 6 原型的原型// 原型也是一个对象// 既然原型是对象,就可以用最原始的方式创建// 原型对象就是通过 Object 构造函数生成的,实例的 _proto_ 指向构造函数的 prototypevar obj = new Object()obj.name = 'Kevin'console.log(obj.name)// 7 原型链// 由互相关联的原型组成的链状结构就是原型链// Object.prototype 的原型呢?// -- null, null 代表了什么?// 引用阮一峰老师的 《undefined 与 null 的区别》// -- null 表示没有对象,即此处不应该有值// -- 所以 Object.prototype._proto_ 的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思// -- 所以查找属性的时候查到 Object.prototype 就可以停止查找了console.log(Object.prototype._proto_ === null)// 补充 -- 真的是继承吗?(这点不理解)// 关于继承,前面我们讲到“每一个对象都会从原型‘继承’属性”,// 实际上,继承是一个十分具有迷惑性的说法,引用《你不知道的JavaScript》中的话,就是:// 继承意味着复制操作,然而 JavaScript 默认并不会复制对象的属性,// 相反,JavaScript 只是在两个对象之间创建一个关联,这样,一个对象就可以通过委托访问另一个对象的属性和函数,// 所以与其叫继承,委托的说法反而更准确些。
