面向对象-继承
三个类
① 超类型
② 父类型
③ 子类型
④ 举例:
类: 动物 狗类 哈士奇
对于狗类来说:动物是它的父类型,而哈士奇类是它的子类型
对于哈士奇类来说:狗类是它的父类型,动物类是它的超类型.
继承:就是让子类拥有父类的资源
原型链继承
借用构造函数继承
组合继承 ????
原型式继承
寄生式继承
寄生式组合继承
拷贝属性继承
减少代码冗余
方便统一操作
弊端:耦合性比较强
继承-原型链继承-原型链实战
每个函数都能构建出一个对象, 这个对象内部有个属性指向着这个函数的原型对象
原型对象本质也是一个对象,也是由另外一个构造函数构造出来, 也指向那个构造函数的原型对象
以上,形成一个链式的结构,就称为是原型链
实战:画出数组的完整原型链
运行结果:
图示:
继承-原型链继承-原型链检索规则
对象.属性的方法去访问属性的时候,先查找有没有对应的实例属性,如果有那么就直接使用
如果没有,那么就去该对象的原型对象上面去找,如果有那么就直接使用
如果没有,那么就接着查找原型对象的原型对象,如果有,那么就直接使用,
如果没有,那么就继续上面的搜索过程
直到搜索到object.prototype为止,如果还是没有找到就返回undefined或者是报错
注意:原型链搜索的路径越长,查询属性所花费的时间就越多
原则:就近原型
继承-原型链继承-0
未继承效果:stu对象, 访问不到person里面的任何内容
问题:子对象无法访问到父类的任何东西
继承-原型链继承-1
思路
① 查找规则:自己没有, 就到原型对象上面查找…
② 所以:
Ⅰ 要不考虑给添加到自己身上:只能自己有,以后的其他对象, 就没了
Ⅱ 要不考虑给添加到原型身上
解决方案:修改原型指向比较方便
图解
问题:无法访问到父类的对象属性?
继承-原型链继承-2
思路:怎样才能拥有父类的实例属性和原型属性?
构造父类的实例
解决方案:构造父类的实例,并设置为子类的原型对象
图解:
问题:类型问题
继承-原型链继承-3
解决方案:修复constructor指针即可
问题:继承过来的实例属性, 如果是引用类型, 会被多个子类的实例共享
注意:到此为止,原型链继承已经结束????
继承-原型链继承-4
思路:只要在原型对象上,肯定都会被共享
解决方案
① 添加到对象自己身上
在创建过对象后添加:复用性差,冗余度高, 稳定性差, 不采用
在构造函数内部添加
② 优化
可以直接调用父类构造函数, 但是需要修改this指向
③ 再优化
注意覆盖关系,如果产生重名, 应该, 子类覆盖父类。即把继承的放在上面
概念 ????
“借用构造函数继承”:在子构造函数内部, 调用父构造函数
问题:父类构造函数的参数无法修改
注意 ????
从此处开始,已经涵盖了原型链继承和借助构造函数继承
称之为**“组合继承”= 原型链 + 借助构造函数**
继承-原型链继承-5
父类构造函数, 需要设置接收可变参数
子类构造函数在调用父类构造函数的时候, 传递参数即可
问题:父类属性重复
实例上有一份,原型对象上有一份
继承-原型链继承-6
① 理解为什么会有两份?因为调用了两次父类构造函数。1次, 在子类构造函数内部,2次, 创建子类构造函数原型对象时
② 分析:两次调用的意义?
1次, 在子类构造函数内部:为了获取父类的实例属性
2次, 创建子类构造函数原型对象时:为了获取父类的实例属性,为了获取实例的原型对象属性
③ 结论::想办法, 设置子类构造函数的原型对象时, 只要父类构造函数的原型对象属性
① 方案一:修改子类构造函数的原型对象指针, 为父类构造函数的原型对象
问题:共享原型对象、容易引发冲突、无法判定类型
不采用
② 方案二
能不能修改子类构造函数的原型对象指针, 为一个对象
这个对象, 只能访问到父类原型对象的属性/方法?
① 拷贝父类构造函数的原型对象
注意: 此处是指拷贝原型对象的内容, 不是整个地址过来
后面会讲解如何拷贝, 到时自行实现这块
② 借助父类构造函数的原型对象, 创建出来一个空对象实例
此处用到了寄生式继承,所以最终, 到此为止, 变成了:寄生组合式继承!!
这是业界程序猿公认的引用类型最理想的范式
① 原型式继承
借助原型,然后基于已有的对象, 创建出新对象;同时不需要创建自定义类型
核心
系统实现:object.create
① 作用:创建对象,并且设置该对象的原型对象为传递过来的参数
兼容es5,ie8不支持
② 简单用法
创建了一个空对象obj2
把obj, 作为obj2的原型对象
③ 兼容处理
② 寄生式继承
在原型式基础上增强这个对象,所谓增加, 就是指, 再次给这个对象增加一些属性或者方法
面向对象-继承-原型链继承-总结
使用借助构造函数继承的方式
在子构造函数中, 调用父构造函数
注意修改this指针
注意调用顺序
寄生式继承方式
面向对象-继承-原型链继承-练习-30m
继承两步走:
1. 借助构造函数继承
2. 原型链继承 和 寄生式继承
3. 注意!!!person的原型对象一定要放在继承的后面,不然会被继承的内容覆盖。
实例属性
name
age
原型方法
eat
run
父构造函数全部
job
jump
classname
study
面向对象-继承-应用练习(扩展内置对象)
直接给对象动态添加属性和方法
弊端: 如果操作很多个对象, 则无法共享;代码比较冗余
直接给array原型对象添加方法
弊端: 可能会产生覆盖的情况
提供一个新的构造函数,修改构造函数的原型指向为数组的原型对象,为了能够获取数组里面的属性和方法
问题: 依然会修改数组原型对象内容
优化: 原型对象就是一个对象,可以直接根据array创建一个对象, 给新创建的函数原型对象进行赋值
补充
函数的prototype属性
每个函数都有一个prototype属性, 它默认指向一个object空对象(即称为: 原型对象)
原型对象中有一个属性constructor, 它指向函数对象
给原型对象添加属性(一般都是方法)
函数的所有实例对象自动拥有原型中的属性(方法)
每个函数function都有一个prototype,称为显式原型(属性)
每个实例对象都有一个__proto__,称为隐式原型(属性)
对象的隐式原型的值为其对应构造函数的显式原型的值
我们能直接操作显式原型, 但不能直接操作隐式原型(es6之前)
内部语句: this.proto = fn.prototype
两个方法是function的原型对象上面的方法
两个函数的作用是一样的,都可以用来借用方法实现
参数:
第一个参数是调用该方法的对象(函数内部的this绑定的对象)
第二个参数:call:参数列表;apply:数组
实例
对象1.方法.call(调用方法的真正的对象,参数1,参数2,参数3);
对象1.方法.apply(调用方法的真正的对象,[参数1,参数2,参数3…])
this – >总是指向一个对象
函数的调用方式
① 作为对象的方法来调用 this—>当前的对象
② 作为普通的函数调用 this—>window
③ 作为构造函数和new使用 this—>构造函数内部新创建的对象
④ 被call或者是apply调用(函数上下文调用) this—>第一个参数