继承
面向对象的继承方式有很多种,原型链继承、借用构造函数继承、组合继承、原型式继承、寄生式继承、寄生式组合继承、深拷贝继承
原型链继承
利用原型链的特性,当在自身找不到时,会沿着原型链往上找。
- Person.prototype.eat = function(){
复制代码- [/code][code]let student = new Student();
复制代码- console.log(student.num) // 001230
复制代码- console.log(student.name) // undefind
复制代码- console.log(student.pets) // undefind
复制代码 从上面我们可以看到,Student并没有继承Person,此时他们之间的联系是这样的。
既然要让实例student访问到Person的原型对象属性方法,
我们可以想到,Student.prototype = Person.prototype
- [/code][code]Person.prototype.eat = function(){
复制代码- [/code][code]function Student(){
复制代码- [/code][code]//改写Student.prototype的指针指向
复制代码- Student.prototype = Person.prototype;
复制代码- [/code][code]let student = new Student();
复制代码- console.log(student.num) // '030578000'
复制代码- console.log(student.name) // undefined
复制代码- console.log(student.pets) // undefined
复制代码 此时的关系图为
现在修改了Student.prototype指针方向为Person.prototype后,可以访问Person.prototype上的eat方法,但是student还不能继承Person.name和Person.pets,会想到,Person的实例,才会同时拥有实例属性方法和原型属性方法。
- [/code][code]Person.prototype.eat = function(){
复制代码- [/code][code]function Student(){
复制代码- [/code][code]// new一个Person的实例,同时拥有其实例方法和原型属性方法
复制代码- [/code][code]// 把Student的原型对象指向实例p
复制代码- [/code][code]// 把Student的原型对象的constructor指向Student,解决类型判断问题
复制代码- Student.prototype.constructor = Student
复制代码- [/code][code]let student = new Student();
复制代码- console.log(student.num) // '030578000'
复制代码- console.log(student.name) // * '邵威儒'
复制代码- console.log(student.pets) // * '[ '旺财', '小黄' ]'
复制代码 因为实例p是由Person构造函数实例化出来的,所以同时拥有其实例属性方法和原型对象方法,并且把这个实例p作为Student的原型对象,此时的关系如下
这种成为原型链继承,到此为止原型链继承就结束了
借助构造函数继承
通过这样的方式,会有一个问题,原型对象类似一个共享库,所有实例共享原型对象同一个属性方法,如果原型对象上有引用类型,那么会被所有实例共享,也就是某个实例更改了,则会影响其他实例,我们可以看一下
- [/code][code]Person.prototype.eat = function(){
复制代码- [/code][code]function Student(){
复制代码- Student.prototype = p //让Student的原型同时拥有Person的实例属性方法,和原型属性方法
复制代码- Student.prototype.constructor = Student
复制代码- [/code][code]let student = new Student()
复制代码- let student2 = new Student() // * new多一个实例
复制代码- console.log(student.num) // '030578000'
复制代码- console.log(student.name) // '邵威儒'
复制代码- console.log(student.pets) // '[ '旺财', '小黄' ]'
复制代码- [/code][code]// 此时我们修改某一个实例,pets是原型对象上的引用类型 数组
复制代码- [/code][code]console.log(student.pets) // * [ '旺财', '小黄', '小红' ]
复制代码- console.log(student2.pets) // * [ '旺财', '小黄', '小红' ]
复制代码 从上面可以看出来,student的pets(实际上就是原型上的pets)被修改后,相关实例student2也会受到影响
那么我们能不能把Person上的属性方法,添加到Student上呢?以防都存在原型对象上,会被所有实例共享,特别是引用类型的修改,会影响所有相关实例。
可以利用call实现
- [/code][code]Person.prototype.eat = function(){
复制代码- [/code][code]function Student(){
复制代码- Person.call(this);//利用call调用Person上的属性方法拷贝一份放到Student中
复制代码- [/code][code]let p = new Person();
复制代码- Student.prototyoe.constructor = Student;
复制代码- [/code][code]let student = new Student();
复制代码- let student2 = new Studetn();
复制代码- console.log(student.num) // '030578000'
复制代码- console.log(student.name) // '邵威儒'
复制代码- console.log(student.pets) // '[ '旺财', '小黄' ]'
复制代码- [/code][code]//此时我们修改某一个实例,pets是原型对象上的引用类型 数组
复制代码- [/code][code]console.log(student.pets);//["旺财","小黄","小红"]
复制代码- console.log(student2.pets);//["旺财","小黄"]
复制代码 上面在子构造函数(Student)中利用call调用父构造函数(Person)的方式,叫做借助构造函数继承
结合上面所看,使用原型链继承和借助构造函数继承,两者结合起来叫做组合继承,关系如下图
还有个问题,当父构造函数需要接受参数时,怎么处理?
- function Person(name,pets){
复制代码- [/code][code]Person.prototype.eat = function(){
复制代码- [/code][code]function Student(num,name,pets){// 子构造函数中也要接受参数
复制代码- Person.call(this,name,pets);// 把name和pets传参数
复制代码- [/code][code]let p = new Person();
复制代码- Student.prototype.constructor = Student;
复制代码- [/code][code]let student = new Student("030578000","邵威儒",["旺财","小黄"])
复制代码- let student2 = new Student("030578001","iamswr",["小红"])
复制代码- console.log(student.num) // '030578000'
复制代码- console.log(student.name) // '邵威儒'
复制代码- console.log(student.pets) // '[ '旺财', '小黄' ]'
复制代码- [/code][code]student.pets.push('小红')
复制代码- [/code][code]console.log(student.pets) // * [ '旺财', '小黄', '小红' ]
复制代码- console.log(student2.pets) // * [ '小红' ]
复制代码
这样我们就可以在构造函数中给父构造函数传参了,而且我们也发现上图中,2两个红圈的地方,代码是重复的,那么怎么解决?
能否在子构造函数设置原型对象的时候,只要父构造函数的原型对象属性方法呢?
当然是可以的,接下来的寄生式组合继承,也是目前程序员认为解决继承问题最好的方案
寄生式组合继承
- function Person(name,pets){
复制代码- [/code][code]Person.prototype.eat = function(){
复制代码- [/code][code]function Student(num){
复制代码- Person.call(this,name,pets);
复制代码- function Temp(){} //*声明一个空的构造函数,用于桥梁作用
复制代码- Temp.prototype = Person.prototype;//*把Temp构造函数的原型对象指向Person的原型对象
复制代码- let temp = new Temp();//* 用构造函数Temp创建一个temp实例
复制代码- Student.prototype = temp;//* 把子构造函数的原型对象指向temp
复制代码- temp.constructor = Student;// *把temp的constructor指向Student
复制代码- [/code][code]let student1 = new Student("001230","xiaoming",["旺财","小黄"]);
复制代码- console.log(student1);//Student{name:xiaoming,pets:["旺财","小黄"],num:001230}
复制代码- let student2 = new Student("002340","xiaohong",["小黑"]);
复制代码- console.log(student2);//Student{name:"xiaohong",pets["小黑"],num:"002340"}
复制代码 至此为止,我们就完成了寄生式组合继承了,主要逻辑就是用一个空的构造函数,来当做桥梁,并且把其原型对象指向父构造函数的原型对象,并且实例化一个temp,temp会沿着这个原型链,去找到父构造函数的原型对象
原型式继承
- function createObjwithObj(obj){
复制代码- [/code][code]// 把Person的原型对象当做temp的原型对象
复制代码- let temp = creatObjwithObj(Person.prototype);
复制代码- [/code][code]//也可以使用Object.create实现
复制代码- //把Person的原型对象当做temp2的原型对象
复制代码- let temp2 = Object.create(Person.prototype);
复制代码 寄生式继承
- // 我们在原型式的基础上,希望给这个对象新增一些属性方法
复制代码- function createNewObjWithObj(obj) {
复制代码- let o = createObjWithObj(obj)
复制代码 深拷贝继承
- //方法一:利用JSON.stringify和JSON.parse
复制代码- [/code][code]let sercopy = JSON.parse(JSON.stringify(swr));
复制代码- console.log(swrcopy) // {name:"xiaoming",age:23}
复制代码- console.log(swr) // {name:"xiaoming",age:29}
复制代码- console.log(swrcopy); //{name:"xiaoming",age23}
复制代码- //这种方法进行深拷贝,只针对json数据这样的键值对有效
复制代码- function deepCopy(formObj,toObj){ //深度拷贝
复制代码- if(fromObj === null) return null //当fromObj为null时
复制代码- if(fromObj instanceof RegExp) return new RegExp(fromObj) //当fromObj为正则
复制代码- if(fromObj instanceof Data) return new Date(fromObj) //当fromObj为Date
复制代码- if(typeof fromObj[key] !== 'object'){ // 是否为对象
复制代码- toObj[key] = fromObj[key] // 如果为原始数据类型,则直接赋值
复制代码- toObj[key] = new fromObj[key].constructor // 如果为object,则new这个object指向的构造函数
复制代码- deepCopy(fromObj[key],toObj[key]) // 递归
复制代码- [/code][code]let dogcopy = deepCopy(dog)
复制代码- console.log(dog) // { name: '小白',
复制代码- firends: [ { name: '小黄', sex: '公' }] }
复制代码- // 当我们打印dogcopy,会发现dogcopy不会受dog的影响
复制代码- console.log(dogcopy) // { name: '小白',
复制代码- firends: [ { name: '小黄', sex: '母' } ] }
复制代码- [/code][list][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][/list][code]// 方法三:
复制代码- [/code][code]function deepCopy(obj) {
复制代码- if(obj === null) return null
复制代码- if(typeof obj !== 'object') return obj
复制代码- if(obj instanceof RegExp) return new RegExp(obj)
复制代码- if(obj instanceof Date) return new Date(obj)
复制代码- let newObj = new obj.constructor
复制代码- newObj[key] = deepCopy(obj[key])
复制代码- [/code][code]let dogcopy = deepCopy(dog)
复制代码 |
|