Javascript高级编程学习笔记(22)—— 对象继承
继承是所有面向对象的语言最让人津津乐道的概念
许多面向对象的语言都支持两种实现继承的方式:
1、接口继承
2、实现继承
由于ECMAScript中没有函数签名,所以自然也是不支持接口继承
所以JS中能实现的也只能是实现继承,而实现继承主要是依靠原型链
至于原型链的构成在昨天的文章中也大概讲了一下
无非就是每个对象的实例都有一个[[Prototype]]的属性(在游览器中以__proto__来显式地支持此属性)指向了其构造函数的原型对象
每个对象都有一个这样的引用,就构成了原型链
原型链的顶端是Object.prototype
而实现继承很大程度上都需要依靠,原型对象,除了少数情况之外
原型链的基本实现
function SuperType(){
this.property = true;
} SuperType.prototype.getSuperValue = function(){
return this.property;
}; // 创建一个新的构造函数
function SubType(){
this.subprototype = false;
} // 重写这个构造函数的原型对象让其指向SuperType的实例
SubType.prototype = new SuperType(); // 添加这个新构造函数的自身的方法
SubtType.prototype.getSubValue = funtion(){
return this.subproperty;
} var instance = new SubType();
alert(instance.getSuperValue()); // true 可以访问到原型对象上的方法和属性
关于上述原型链的实现其实并不完整
因为昨天我们已经说过,重写原型对象的方法会存在一些问题
上方代码中 SubType.prototype.constructor 并不会正确地指向 SubType 这一构造函数
而是会指向 SuperType, 因为 SuperType 的实例并没有 constructor 属性,所以JS会访问SuperType实例所指的原型对象的 constructor 属性也就是 SuperType这一构造函数
PS. constructor属性会在构造函数创建原型对象时,让这个对象指向构造函数,但当我们重写构造函数的prototype属性时(也就是让prototype指向另一个对象),JS并不会自动帮我们完成这一过程
确定原型和实例的关系
一般来说有两种方法
1、instanceof
2、isPrototypeOf()
两种方式从原理上来说并没有什么太大的区别
因为只要是在实例的原型链上出现过的原型对象都会被判为 true
所以当我们需要具体的判断某个实例是否是由某个特定的构造函数构造的时候,就不能使用上述的方法
但我们可以借助原型链的特点来判断
实例对象.__proto__.constructor === 要判断的构造函数
原型链继承
在这么长的铺垫下终于进入主题了
第一种实现继承的方式,在刚在的展示中基本上已经展示了
还是在给个完整的原型链继承的代码吧
function SuperType(){
this.property = true;
} SuperType.prototype.getSuperValue = function(){
return this.property;
}; // 创建一个新的构造函数
function SubType(){
this.subprototype = false;
} // 重写这个构造函数的原型对象让其指向SuperType的实例
SubType.prototype = new SuperType();
// 修改 constructor 让其指向正确地构造函数
SubType.prototype.constructor = SubType; // 添加这个新构造函数的自身的方法
SubtType.prototype.getSubValue = funtion(){
return this.subproperty;
} var instance = new SubType();
alert(instance.getSuperValue()); // true 可以访问到原型对象上的方法和属性
PS. 在实现继承的过程中有几个地方需要注意
1. 给原型对象添加方法的语句一定要在替换原型之后
2.一旦替换了原型就要小心不要在定义原型上的新方法时错误地替换了原型
这种继承方式存在以下问题
1、对于引用类型的值会被所有实例所共享
2、没有办法在不影响所有对象实例的情况下,向父类的构造函数传递参数
所以在实践当中我们很少对单独使用这种方式来实现继承
经典继承(借用构造函数)
这种继承方式的核心思想就是在子类的构造函数中调用父类的构造函数
这样就可以获得父类的所有属性
function SuperType(){
this.color = 'color';
} function SubType (){
SuperType.applay(this,arguments);
this.name = 'lhy';
}
通过改变this指向来让父类构造函数中创建的属性创建到子类构建的对象上
和构造函数模式样
这样的方法没法解决函数复用的问题,也没法继承到父类的原型上的属性
但是这种方法可以在创建子类时给父类的构造函数传递参数并且不会影响其实例对象
虽然这种方法有独特的优势,但是它的问题也不少
所以我们在实践中也很少单独使用这种继承方式
组合继承
有小伙伴可能发现之前的两种方式在某种程度上是互补的
所以第三种继承的实现方式当然就是由前面两种方式组合而来的组合继承
function SuperType(){
this.property = true;
} SuperType.prototype.getSuperValue = function(){
return this.property;
}; // 创建一个新的构造函数
function SubType(){
// 调用父类的构造函数
SuperType.applay(this,arguments);
this.subprototype = false;
} // 重写这个构造函数的原型对象让其指向SuperType的实例
SubType.prototype = new SuperType();
// 修改 constructor 让其指向正确地构造函数
SubType.prototype.constructor = SubType; // 添加这个新构造函数的自身的方法
SubtType.prototype.getSubValue = funtion(){
return this.subproperty;
} var instance = new SubType();
alert(instance.getSuperValue()); // true 可以访问到原型对象上的方法和属性
这种组合方式也是在实践中我们最常用的方法(ES5)
融合了前两种方式优点,除此而外 instanceof 和 isPrototypeOf() 也能正确识别
原型式继承
有些时候我们要实现继承,很可能只是希望新的这个类型在已有类型的基础上多出一些属性
没有必要大费周章地创建新的原型函数,那么原型式继承给了我们新的选择
function object(o){// o是参照的对象
function F(){};
F.prototype = o;
return new F();
}
这个简短的函数就是原型式继承的核心思想
虽然都是将新对象以旧对象为原型创建,但是和原型链继承的方式的区别也很显著
那就是这种继承方式不需要构造函数,这也是它存在的意义
为了规范这种继承,ES5规范了Object.create() 方法
传入两个参数,1、原型对象 2、为新对象定义额外属性的对象(可选)
所以我们可以直接使用create 方法
但是这种方式存在的问题也很明显,那就是引用类型的值在实例中会共享(在以同一个对象为参照对象的情况下)
寄生式继承
这种继承方式是原型式继承的进阶版
其思路与寄生构造函数类似
function object(o){// o是参照的对象
function F(){};
F.prototype = o;
return new F();
} function createAnother(o){
var clone = object(o);
clone.say = function(){
alert('lhy');
}
return clone;
}
这种方法相当于,在原型式继承的基础上把给新对象添加新属性的工作,以工厂模式的思路实现了
当然,这种方式的缺点就是,函数没法复用(构造函数模式的通病)
寄生组合继承
看到这里,大家可能以为组合式继承,就是最好的选择
其实细心的小伙伴可能已经发现了,在我们创建子类实例的时候父类的构造函数调用了两次
我们调用两次的原因无非就是需要一个父类的原型副本而已
所以寄生组合继承的思路就是通过原形式继承来获取父类原型的副本,从而避免调用两次父类的构造函数
function object(o){// o是参照的对象
function F(){};
F.prototype = o;
return new F();
} // 定义继承的方法
function inheritPrototype(SubType,SuperType){
var prototype = object(SuperType);
prototype.constructor = SubType;
SubType.prototype = prototype;
} function SuperType(name){
this.name = name;
this.color = ['blue','red','green'];
} function SubType(name,age){
SuperType.call(this,name);// 调用父类的构造函数
this.age = age;
} inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}
这里为了大家更好理解,所以没有用 Object.create()
在实践中大家最好用 Object.create() 代替上面我定义的 object 函数
以上就是JS对象继承的主要内容了
Javascript高级编程学习笔记(22)—— 对象继承的更多相关文章
- Javascript高级编程学习笔记(21)—— 对象原型
JS中对象相关的最重要的恐怕就是原型链了 原型链也是JS中对象继承的实现的基础 接昨天的文章,我们使用构造函数创建对象的时候仍然存在一些问题 那就是所有的实例没法共用一个函数 这样无疑会造成极大的内存 ...
- Javascript高级编程学习笔记(20)—— 创建对象
由于今天有点事,加上对象原型链的东西有点多,所以今天这篇就讲一个小的知识点吧 也算为明天的对象继承做铺垫 工厂模式 虽然使用对象字面量来创建一个对象十分地便捷,但是这个方法有一个显著的缺点 那就是如果 ...
- Javascript高级编程学习笔记(17)—— 引用类型(6)基本包装类
基本包装类 基本包装类这个概念或许有的小伙伴没有听说过 但是小伙伴们有没有想过,为什么基本数据类型的实例也有方法呢? 其实这些方法都来自基本包装类型 这是JS为了方便操作基础数据类型而创建的特殊引用类 ...
- Javascript高级编程学习笔记(18)—— 引用类型(7)单体内置对象
什么是内置对象呢? js高级程序设计中给出的定义为:由ES规定不依赖于宿主环境的对象,这些对象在JS执行前就已经存在 前面我们介绍的引用类型都是内置对象 除了这些对象外ECMA还规定了两个单体内置对象 ...
- Javascript高级编程学习笔记(59)—— 事件(3)事件对象
事件对象 在触发DOM‘事件时,会产生一个事件对象 event 该对象包含着所有与事件有关的信息 所有浏览器都支持 event 对象但是支持的方式有所不同 DOM事件对象 兼容DOM的浏览器会将eve ...
- Javascript高级编程学习笔记(31)—— BOM(5)screen、history对象
screen对象 screen对象应该是BOM对象中最不常用的对象了 其主要用于提供客户端的显示能力信息 包括浏览器外部显示的信息,和像素的宽高等 这个对象的主要用于检测客户端能力,一般不会影响功能 ...
- Javascript高级编程学习笔记(30)—— BOM(4)navigator对象
window对象作为浏览器的全局对象.location对象保存了页面的url信息 那么navigator对象又有什么作用呢? navigator对象 该对象最早由 Netspace Navigator ...
- Javascript高级编程学习笔记(29)—— BOM(3)location对象
在JS中location是一个神奇的对象 它既是window对象的属性,也是document对象的属性 它的作用主要在于保存当前文档页面的信息,以及将 url 解析为独立的片段 location对象属 ...
- Javascript高级编程学习笔记(28)—— BOM(2)window对象2
今天讲一下window对象和浏览器导航,弹窗等有关的内容 导航和打开窗口 window.open() 用于导航到某个特定 url 该方法接收四个参数 1.url 2.窗口目标(当页面中有多个框架fra ...
随机推荐
- leetcode8:字符串转整数 (atoi)
实现 atoi,将字符串转为整数. 在找到第一个非空字符之前,需要移除掉字符串中的空格字符.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为整数的值 ...
- Python-docx 读取word.docx内容
第一次写博客,也不知道要写点儿什么好,所以就把我在学习Python的过程中遇到的问题记录下来,以便之后查看,本人小白,写的不好,如有错误,还请大家批评指正! 中文编码问题总是让人头疼,想要用Pytho ...
- [精华][推荐] CAS SSO单点登录环境搭建及实例
1.因为是本地模拟sso环境,而sso的环境测试需要域名,所以需要虚拟几个域名出来,步骤如下: 2.进入目录C:\Windows\System32\drivers\etc 3.修改hosts文件 12 ...
- Finance财务软件(自定义报表专题)
我们可以通过存储过程自定义报表 1.在菜单中新增报表菜单,这里的代码约束为报表对应存储过程名称,配置完成成后重启客户端生效 2.在自定义模板中适配存储过程入参,这里的功能键值为存储过程名称,字段键值与 ...
- spring Resource(转)
http://blog.csdn.net/u011225629/article/details/47143075
- IAR FOR AVR 仿真过程中出现全局变量值不断随意变化的问题
本文记录使用IAR FOR AVR 使用过程中出现的问题,确保自己以后能够有史可查,也分享给遇到同样问题的朋友. 版本信息:IAR Assembler for AVR 5.40.0 (5.40.0. ...
- Linux任务计划命令 :crontab -e
crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond ...
- ELK的文档搭建
一.安装elasticsearch 官网:https://www.elastic.co/guide/index.html https://www.elastic.co/guide/en/elastic ...
- C++二分图匹配基础:zoj1002 FireNet 火力网
直接给出题目吧... 问题 D(1988): [高级算法]火力网 时间限制: 1 Sec 内存限制: 128 MB 题目描述 给出一个N*N的网格,用'.'表示空地,用'X'表示墙.在网格上放碉堡,可 ...
- 2019.03.26 bzoj4447: [Scoi2015]小凸解密码(线段树)
传送门 题意简述:咕咕咕 思路:考虑预处理出bbb数组,然后每次改动aaa都只会对第iii和i+1i+1i+1这两个位置产生影响,于是可以用线段树来维护bbb数组. 现在求答案的方法是断环为链,倍增整 ...