js中实现继承的方法
借用构造函数
这种技术的基本思想很简单,就是在子类型构造函数的内部调用超类型的构造函数。另外,函数只不过是在特定环境中执行代码的对象,因此通过使用apply()和call()方法也可以在新创建的对象上执行构造函数。
function Box(name){
this.name = name
}
Box.prototype.age = 18
function Desk(name){
Box.call(this, name) // 对象冒充,对象冒充只能继承构造里的信息
}
var desk = new Desk('ccc')
console.log(desk.name) // --> ccc
console.log(desk.age) // --> undefined
从中可以看到,继承来的只有实例属性,而原型上的属性是访问不到的。这种模式解决了两个问题,就是可以传参,可以继承,但是没有原型,就没有办法复用。
组合继承
function Box(name){
this.name = name
}
Box.prototype.run = function (){
console.log(this.name + '正在运行...')
}
function Desk(name){
Box.call(this, name) // 对象冒充
}
Desk.prototype = new Box() // 原型链
var desk = new Desk('ccc')
console.log(desk.name) // --> ccc
desk.run() // --> ccc正在运行...
这种继承方式的思路是:用使用原型链的方式来实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
原型式继承
原型式继承:是借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型。讲到这里必须得提到一个人,道格拉斯·克罗克福德在2006年写的一篇文章《Prototype inheritance in Javascript》(Javascript中的原型式继承)中给出了一个方法:
function object(o) { //传递一个字面量函数
function F(){} //创建一个构造函数
F.prototype = o; //把字面量函数赋值给构造函数的原型
return new F() //最终返回出实例化的构造函数
}
看如下的例子:
function obj(o) {
function F (){}
F.prototype = o;
return new F()
}
var box = {
name: 'ccc',
age: 18,
family: ['哥哥','姐姐']
}
var box1 = obj(box)
console.log(box1.name) // --> ccc
box1.family.push('妹妹')
console.log(box1.family) // --> ["哥哥", "姐姐", "妹妹"]
var box2 = obj(box)
console.log(box2.family) // --> ["哥哥", "姐姐", "妹妹"]
因为上述的代码的实现逻辑跟原型链继承很类似,所以里面的引用数组,即family属性被共享了。
寄生式继承
function obj(o) {
function F (){}
F.prototype = o;
return new F()
}
function create(o){
var clone = obj(o) // 通过调用函数创建一个新对象
clone.sayName = function(){ // 以某种方式来增强这个对象
console.log('hi')
}
return clone // 返回这个对象
}
var person = {
name: 'ccc',
friends: ['aa','bb']
}
var anotherPerson = create(person)
anotherPerson.sayName() // --> hi
这个例子中的代码基于person返回一个新对象————anotherPerson。新对象不仅具有person的所有属性和方法,而且还有自己的sayHi()方法。在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率,这一点与构造函数模式类似。
寄生组合式继承
前面说过,组合继承是Javascript最常用的继承模式,不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用过两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性,再来看一下下面的例子:
function SuperType(name){
this.name = name;
this.colors = ['red','black']
}
SuperType.prototype.sayName = function (){
console.log(this.name)
}
function SubType(name, age){
SuperType.call(this, name) // 第二次调用SuperType
this.age = age
}
SubType.prototype = new SuperType() // 第一次调用SuperType
SubType.prototype.constructor = SubType
SubType.prototype.sayAge = function (){
console.log(this.age)
}
第一次调用SuperType构造函数时,SubType.prototype会得到两个属性:name和colors。他们都是SuperType的实例属性,只不过现在位于SubType的原型中。当调用SubType构造函数时,又会调用一次SuperType构造函数,这个一次又在新对象上创建了实例属性name和colors。于是,这两个属性就屏蔽了原型中的两个同名属性。即有两组name和colors属性:一组在实例上,一组在原型上。这就是调用两次SuperType构造函数的结果。解决这个问题的方法就是————寄生组合式继承。
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了制定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下:
function object(o) {
function F (){}
F.prototype = o;
return new F()
}
function inheritPtototype(subType, superType){
var prototype = object(superType.prototype) // 创建对象
prototype.constructor = subType // 增强对象
subType.prototype = prototype // 指定对象
}
function SuperType(name){
this.name = name
this.colors = ['red', 'white']
}
SuperType.prototype.sayName = function(){
console.log(this.name)
}
function SubType(name,age){
SuperType.call(this,name)
this.age = age
}
inheritPtototype(SubType, SuperType)
SubType.prototype.sayAge = function(){
console.log(this.age)
}
var instance = new SubType('ccc', 18)
instance.sayName() // --> ccc
instance.sayAge() // --> 18
console.log(instance)
控制台打印出的结构:

详细的图解:

这个例子的高效率提现在它值调用了一次SuperType构造函数,并且因此避免了在SubType.prototype上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用instanceof和isPrototypeOf()。这也是很多大厂用的继承方式。
js中实现继承的方法的更多相关文章
- js中实现继承的几种方式
首先我们了解,js中的继承是主要是由原型链实现的.那么什么是原型链呢? 由于每个实例中都有一个指向原型对象的指针,如果一个对象的原型对象,是另一个构造函数的实例,这个对象的原型对象就会指向另一个对象的 ...
- 【学习笔记】六:面向对象的程序设计——理解JS中的对象属性、创建对象、JS中的继承
ES中没有类的概念,这也使其对象和其他语言中的对象有所不同,ES中定义对象为:“无序属性的集合,其属性包含基本值.对象或者函数”.现在常用的创建单个对象的方法为对象字面量形式.在常见多个对象时,使用工 ...
- JS中的继承(上)
JS中的继承(上) 学过java或者c#之类语言的同学,应该会对js的继承感到很困惑--不要问我怎么知道的,js的继承主要是基于原型(prototype)的,对js的原型感兴趣的同学,可以了解一下我之 ...
- JS中的继承(下)
JS中的继承(下) 在上一篇 JS中的继承(上) 我们介绍了3种比较常用的js继承方法,如果你没看过,那么建议你先看一下,因为接下来要写的内容, 是建立在此基础上的.另外本文作为我个人的读书笔记,才疏 ...
- js中的继承和重载
js中有三种继承方式:一.通过原型(prototype)实现继承 二.借用构造函数式继承,可分为通过call()方法实现继承和通过apply()方法实现继承 仅仅通过原型继承我们可以发现在实例化子 ...
- node.js中的url.parse方法使用说明
node.js中的url.parse方法使用说明:https://blog.csdn.net/swimming_in_it_/article/details/77439975 版权声明:本文为博主原创 ...
- JS中 call() 与apply 方法
1.方法定义 call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明: call ...
- 浅谈JS中的继承
前言 JS 是没有继承的,不过可以曲线救国,利用构造函数.原型等方法实现继承的功能. var o=new Object(); 其实用构造函数实例化一个对象,就是继承,这里可以使用Object中的所有属 ...
- js中的继承
js中继承的实现方式很多,此处给出两种常用方式. <!DOCTYPE html> <html> <head> <meta charset='UTF-8'> ...
随机推荐
- Springboot基于assembly的服务化打包
(1)首先我们编辑 assembly.xml 配置文件,在前文的基础上新增第三方依赖设置(高亮部分),实现将第三方的 jar 包添加到压缩包里的 lib 目录: <?xml version=&q ...
- 第二章、Web中使用shiro(实现登陆)
虽然Apache Shiro的核心设计目标允许它用于保护任何基于JVM的应用程序(如命令行应用程序,服务器守护程序,Web应用程序等),但本指南将着重讨论最常见的用例:确保运行的Web应用程序一个Se ...
- Java WebService(实战) 简单实例
一.准备工作(以下为本实例使用工具) 1.MyEclipse10.7.1 2.JDK 1.6.0_22 二.创建服务端 1.创建[Web Service Project],命名为[TheService ...
- centos下面基本的linux命令操作
参考视频: 1.如何打开命令窗口 鼠标点击右键,选择open Terminal就可以打开命令行窗口了. 现在我们可以调整这个窗口的大小,增大窗口大小使用ctrl+shif+加号,缩小窗口ctrl+减号 ...
- shiro之redis频繁访问问题
目前安全框架shiro使用较为广泛,其功能也比较强大.为了分布式session共享,通常的做法是将session存储在redis中,实现多个节点获取同一个session.此实现可以实现session共 ...
- 学习Java的Day04
知识点 接口: 1.接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”. 2. 接口中只允许出现抽象方法,不能出现非抽象方法 3. 接口的源文件也是java文件,编译后的文件也是. ...
- (win7) 在IIS6.0 中配置项目
1.进入IIS管理器 右击“计算机”->管理->服务和应用程序->Internet信息服务(IIS)管理器 2.将项目加入IIS中 网站->右击“默认网站”->添加虚拟目 ...
- 一文读懂 Redis 分布式部署方案
为什么要分布式 Redis是一款开源的基于内存的K-V型数据库,因为内存访问速度快,一般被用来做系统的缓存. Redis作为单机部署能够支持业务简单,数据量不大的系统需求,但在实际应用中,一旦系统规模 ...
- Flutter 中渐变的高级用法
Flutter 中渐变有三种: LinearGradient:线性渐变 RadialGradient:放射状渐变 SweepGradient:扇形渐变 看下原图,下面的渐变都是在此图基础上完成. Li ...
- FocusBI:《商业智能7B理论模型》创造者
<商业智能7B理论模型>专门为培养企业级BI人才<如何一个人完成BI项目,成为企业级BI人才>课程而创造,历经我7年的商业智能项目实施工作和经验的提炼与总结,分别深入在甲方公司 ...