继承
六、继承(※)
假设原型链的重点object.prototype为原型链的E(end)端,原型链的起点为S(start)端。
通过前面学习原型链的学习我们知道,处于S端的对象,可以通过S->E的单向查早,访问到原型链上所有的方法与属性。因此这给继承提供了理论基础。我们只需再S端添加新的对象,那么新对象就能够通过原型链访问到父级的方法与属性。
因为封装一个对象由构造函数与原型链共同组成,因此继承也会分别有构造函数的继承与原型的继承。
先声明一个Person对象,该对象将多为父级,而子级cPerson将要继承Person的所有属性和方法。
var Person = function(name, age) { this.name = name; this.age = age; } Person.prototype.getName = function() { return this.name; } Person.prototype.getAge = function() { return this.age; }
1.原型链继承
要让子类继承父类方法,需要将子类对象的原型加入到原型链中。只需要将子集的原型对象设置为父级的实例,加入到原型链中即可。然后通过__proto__就可以访问父类对象的原型 +
cPerson.prototype = new Person(name, age); // 添加更多方法 cPerson.prototype.getLive = function() {}
有几点注意事项:
(1)不要忘记,所有的引用类型都默认继承了Object。这个继承也是通过原型链实现的。
(2)可以使用instanceof或者isPrototypeof()来确定原型和实例之间的关系。
instanceof:测试实例与原型链中出现过的构造函数;
isPrototypeof():测试原型链中出现过的原型。
alert(instance instanceof Object); //true alert(SuperType.prototype.isPrototypeof(instance)); //true
(3)子类型有时需要覆盖超类型中的某个方法,或者需要添加某个不存在的方法。给原型添加方法的代码一定要放在替换原型(替换原型就是实现继承的那句代码)的语句之后。
(4)不能使用字面量创建原型方法,否则会重写原型链。
原型链继承的问题:
(1)如果构造函数的某个属性的值是一个引用类型,那么该构造函数所有的实例都会共享这个属性。
(2)创建子类型的实例时,不能向超类型构造函数中传递参数。(其实可以,但会影响其它对象实例)
2.构造函数的继承
就是想让父级构造函数中的操作在子集构造函数中重现一遍即可。可以通过call方法来达到目的:
//构造函数的继承 var Student = function(name, age, grade) { // 通过call方法还原Person构造函数中的所有处理逻辑 Student.call(Person, name, age); this.grade = grade; } // 等价于 var Student = function(name, age, grade) { this.name = name; this.age = age; this.grade = grade; }
构造函数继承的优势:可以向超类型构造函数传递参数。(因为使用了call、apply)。
构造函数继承的缺点:
(1)仍然无法避免构造函数模式存在的问题,方法都再构造函数中定义,不能实现函数复用。
(2)在超类型的原型中定义的方法,对子类型是不可见的。
3.组合继承(常用)
将原型链继承和构造函数继承相结合。
这样,既通过在原型上定义方法实现了函数复用,又能保证每个实例都有它自己的属性。
//父类:人 function Person () { this.head = '脑袋瓜子'; this.emotion = ['喜', '怒', '哀', '乐']; } //将 Person 类中需共享的方法放到 prototype 中,实现复用 Person.prototype.eat = function () { console.log('吃吃喝喝'); } Person.prototype.sleep = function () { console.log('睡觉'); } Person.prototype.run = function () { console.log('快跑'); } //继承属性(子类:学生,继承了“人”这个类) function Student(studentID) { this.studentID = studentID; Person.call(this); } //继承方法 Student.prototype = new Person(); //此时 Student.prototype 中的 constructor 被重写了,会导致 stu1.constructor === Person Student.prototype.constructor = Student; //将 Student 原型对象的 constructor 指针重新指向 Student 本身 var stu1 = new Student(1001); console.log(stu1.emotion); //['喜', '怒', '哀', '乐'] stu1.emotion.push('愁'); console.log(stu1.emotion); //["喜", "怒", "哀", "乐", "愁"] var stu2 = new Student(1002); console.log(stu2.emotion); //["喜", "怒", "哀", "乐"] stu1.eat(); //吃吃喝喝 stu2.run(); //快跑 console.log(stu1.constructor); //Student
注意要重写constructor的指针。
缺点:无论在什么情况下,都会调用两次超类型构造函数:
一次是在创建子类型原型的时候;
一次是在子类型构造函数内部。
4.原型式继承
用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。
Object.create就是这个原理。
var person={ name:'Nico'; friends:['shelly'.'court','van'] } // 第二个参数可省 var person1=Object.create(person,{ name:{value:'gray'} }); alert(person1.name); //'gray'
Object.create第一个参数是用作新对象原型的对象,第二个参数可省,是新对象定义额外属性的对象。
原型式继承的缺点:
(1)所有实例都会继承原型上的属性。(和原型链继承类似)
(2)无法实现复用。(和构造函数继承类似)
5.寄生式继承
创建一个仅用于封装继承过程的函数,在内部以某种方式增强对象,再返回这个对象。
优点:寄生继承(原型式继承)就是不用实例化父类了,直接实例化一个临时副本实现了相同的原型链继承。(即子类的原型指向父类副本的实例从而实现原型共享)
缺点:无法实现复用。(和构造函数继承类似)
6.寄生组合式继承(常用)
之前说了组合继承的缺点是会调用两次超类型构造函数。
//父类:人 function Person () { this.head = '脑袋瓜子'; this.emotion = ['喜', '怒', '哀', '乐']; } //将 Person 类中需共享的方法放到 prototype 中,实现复用 Person.prototype.eat = function () { console.log('吃吃喝喝'); } Person.prototype.sleep = function () { console.log('睡觉'); } Person.prototype.run = function () { console.log('快跑'); } //子类:学生 function Student(studentID) { this.studentID = studentID; Person.call(this); //第二次调用 } Student.prototype = new Person(); //第一次调用 Student.prototype.constructor = Student; var stu1 = new Student(1001); console.log(stu1.emotion); //['喜', '怒', '哀', '乐'] stu1.emotion.push('愁'); console.log(stu1.emotion); //["喜", "怒", "哀", "乐", "愁"] var stu2 = new Student(1002); console.log(stu2.emotion); //["喜", "怒", "哀", "乐"] stu1.eat(); //吃吃喝喝 stu2.run(); //快跑 console.log(stu1.constructor); //Student
调用两次后,就会有两组属性,一组在实例上,一组在Student的原型中。
寄生式组合继承利用寄生继承解决组合继承的问题。
寄生式组合继承通过构造函数来继承属性,通过原型链的混成形式来继承方法。
function SuperType(name,colors){ this.name=name; this.colors=colors; } SuperType.prototype.getSuperProperty=function(){ return this.name; } function SubType(job,name,colors){ SuperType.call(this,name,colors); this.job=job; } SubType.prototype.getSubPrototype=function(){ return this.job; } function inherit(subType,superType){ var prototype=Object.create(superType.prototype); prototype.constructor=subType; subType.prototype=prototype; } inherit(SubType,SuperType); var instance=new SubType("doctor","John",["red","green"]); console.log(instance.getSubPrototype()); //输出"doctor" console.log(instance.getSuperProperty()); //输出"John",成功调用在父类原型定义的方法
其中
function inherit(subType,superType){ var prototype=Object.create(superType.prototype); prototype.constructor=subType; subType.prototype=prototype; }
就是寄生组合式继承的基本模式。如果暂时理解不了就先记住吧。
原文地址:https://www.cnblogs.com/sherrycat/p/11503699.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 用ORCA做结构优化及轨迹查看
- 前端兼容之痛
- 离线安装PySCF程序(1.5及更高版本)
- Python Debug(调试)的终极指南
- Linux下做计算常用的别名alias推荐
- 离线安装支持Intel MKL的R-3.6
- Dalton使用——磷光及其相关过程
- 【赵渝强老师】Flink的Watermark机制(基于Flink 1.11.0实现)
- 在Windows CMD里“使用”常见Linux命令
- 什么是Python中的Dask,它如何帮助你进行数据分析?
- a[i] = i++ 到底对不对?
- 如何监视Python程序的内存使用情况
- df -h和du -sh看到的硬盘使用不相等?
- MyBatis 中的一级和二级缓存
- 使用Cython加速你的Python代码