JS继承的一些见解
JS继承的一些见解
js在es6之前的继承是五花八门的。而且要在项目中灵活运用面向对象写法也是有点别扭,更多的时候还是觉得面向过程的写法更为简单,效率也高。久而久之对js的继承每隔一段时间就会理解出现困难。所以这次我要把对对象的理解写下来,这样应该就深刻一点了。
我们先来看看一个对象是怎么生成的
// 三种创建对象的方法
var obj = {}
var obj2 = new Object()
var obj3 = Object.create(null)
// 创建一个空字符串对象
var obj4 = new String()
obj.constructor === obj2.constructor // true
obj.__proto__ === obj2.__proto__ === Object.prototype // true
obj4.__proto__ === String.prototype // true
obj4.__proto__.__proto__ === Object.prototype // ture
这三种方法,前面两种是一样的。它们创建的空对象都具有原型,而第三种方式创建的是一个真正意义上的空对象,它不继承任何属性;而obj4呢是一个字符串对象。看下图的对比:
下面对象除了obj3都有个__proto__的隐性属性,这个属性指向的是该创建该实例的构造函数的原型。
这里obj和obj2虽然是空对象,不过它们具有Object构造函数的属性的方法,而obj4除了有obj拥有的属性和方法,它还具备了String拥有的属性和方法;而obj3是真正的空对象,它的__proto__指向的是null。
我们再将obj4全部展开看看:
再来看看它们之间的关系:
如上图,我举得例子是Object和String这两个原生具有的构造器。它们的关系和我们平时写的父类和子类之间的关系是一样的,所有的类最终都会指向Object。
在es5的时代,我们用到的继承最简单的就是原型链继承了
// 我们先创建一个父类
function Super (name) {
this.name = name
this.type = '我是父类'
}
Super.prototype.move = function () {
console.log(this.name + '在跑')
}
// 子类继承父类
function Child (name) {
this.name = name
}
Child.prototype = new Super()
// 原型链继承,子类原型的私有方法和属性要在继承之后添加,不然会被覆盖
Child.prototype.constructor = Child // 修复对构造函数的指向
Child.prototype.eat = function () {
// 做点啥
}
原型链继承就是将子类的原型等于父类的实例,然后再添加子类原型的属性和方法。这样的缺点就是只能继承一个父类。而且在创建子类的实例的时候,不能传参给父类。在做单一继承而且父类不需要传参的时候,这种写法是最好的选择了。
下面在介绍一种我个人觉得已经很不错的继承方式(组合继承)
// 子类继承父类
function Child (name) {
Super.call(this)
this.name = name
}
Child.prototype = new Super()
Child.prototype.constructor = Child // 修复对构造函数的指向
Child.prototype.eat = function () {
// 做点啥
}
- 这种继承方式就是粗暴的将父类Super构造函数利用对象冒充的方式将父类的实例属性复制到子类里。在用原型继承的方式继承父类原型的属性和方法。
- 这样的继承方式,你会发现子类的实例的属性和__proto__下都有父类的属性,而子类实例的把子类原型的覆盖了。
- 这样的方式可以实现对多个父类(非原型部分)的继承。因为原型的继承是链式的,所以只能继承一个。
一般来说这种继承方式已经很好了,代码量少。继承性好,弊端也没有影响。这就是ES6之前的继承。下面再来看看ES6是怎么操作的。
ES6的class
- ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
- 基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写,就是下面这样。
上面这两句话是引用阮一峰对ES6的class的简介(Class 的基本语法)。阮大神的ECMAScript 6 入门真的是对es6初学者居家必备的好东西。我在这里就谈谈我的理解好了。
// 写一个class
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return '(' + this.x + ', ' + this.y + ')';
}
}
var point = new Point(2, 3) // new一个实例
下面看下point内部是怎样的,可以看出constructor,toString都是在“proto”内的。
在这里会等价于构造函数的什么写法呢
// 写一个class
function Point (x, y) {
this.x = x
this.y = y
}
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
}
var point = new Point(2, 3) // new一个实例
在不考虑class静态属性的情况下,可以简单理解为constructor内this下的新属性和方法都属于构造函数内的,而constructor外的方法都是prototype下的。
- 在看看class的继承
class ChildPoint extends Point {
constructor (x, y, z) {
super(x, y)
this.z = z
}
toString () {
return '(' + this.x + ', ' + this.y + ','+ this.z +')';
}
changeX (x) {
this.x = x
}
}
var childPoint = new ChildPoint(2, 3, 4) // new一个ChildPoint的实例
console.log(childPoint.x) // 2
console.log(childPoint.y) // 3
console.log(childPoint.z) // 4
console.log(childPoint.toString()) // 2,3,4
childPoint.changeX(5)
console.log(childPoint.x) // 5
console.log(childPoint.__proto__ === ChildPoint.prototype) // true
console.log(childPoint.__proto__.__proto__ === Point.prototype) // true
console.log(childPoint.constructor === ChildPoint) // true
这里class是通过extends继承父类的,而子类的constructor方法内需要调用super()方法,这相当与调用了父类的constructor()方法。
下面看下childPoint的内部情况:
从图片和上面打印的情况可以看出,es6的继承和组合继承很是相似。唯一的不同点就是es6的继承没有组合继承产生的多余的一份实例属性在原型里。看起来很顺眼。不过相对的es6的不能多继承(虽然组合继承只能多继承多个构造函数,并不能继承多个原型)。
其实组合继承还可以进一步升级成es6继承那个样子的(寄生组合继承):
// 我们先创建一个父类
function Super (name) {
this.name = name
this.type = '我是父类'
}
Super.prototype.move = function () {
console.log(this.name + '在跑')
}
// 子类继承父类
function Child (name) {
Super.call(this)
this.name = name
}
(function() {
// 创建一个没有Super实例的中间类
var MiddleSuper = function() {}
MiddleSuper.prototype = Super.prototyoe // 跟Super共享原型
// 在这里MiddleSuper和Super的区别就是有没有实例了。
// 然后在正常进行组合继承的操作
Child.prototype = new MiddleSuper()
})()
Child.prototype.constructor = Child // 修复对构造函数的指向
Child.prototype.eat = function () {
// 做点啥
}
经过这一系列复杂的操作后我们就得到了一份和es6一样的结构了。不过es6只需要一个extends,而es6之前我们就得长篇大论的写才能实现,而且还特别绕。所以一句话,快去学es6吧。
- 该文纯属个人见解,有什么问题请指出。
本文参考了:
JS继承的一些见解的更多相关文章
- js继承
js继承有5种实现方式: 继承第一种方式:对象冒充 function Parent(username){ this.username = username; this.hello = function ...
- js继承之call,apply和prototype随谈
在js中,call,apply和prototype都可以实现对象的继承,下面我们看一个例子: function FatherObj1() { this.sayhello = "I am jo ...
- js继承精益求精之寄生式组合继承
一.混合/组合继承的不足 上一篇JS继承终于混合继承,认真思考一下,发现其还是有不足之处的: 空间上的冗余:在使用原型链的方法继承父类的原型属性(Animal.prototype)的同时,也在子类的原 ...
- 老生常谈--Js继承小结
一直以来,对Js的继承有所认识,但是认识不全面,没什么深刻印象.于是,经常性的浪费很多时间重新看博文学习继承,今天工作不是特别忙,有幸看到了http://www.slideshare.net/stoy ...
- Js继承小结
Js继承小结 一直以来,对Js的继承有所认识,但是认识不全面,没什么深刻印象.于是,经常性的浪费很多时间重新看博文学习继承,今天工作不是特别忙,有幸看到了http://www.slideshare.n ...
- js继承实现
JS实现继承可以分为:对象冒充和原型链继承 其中对象冒充又包括:临时变量,call 和 apply 临时变量方法: function Person(name,sex){ this.name = nam ...
- js继承之借用构造函数继承
我的上一篇文章介绍了,原型链继承模式.但是单纯的原型链模式并不能很好地实现继承. 一.原型链的缺点 1.1 单纯的原型链继承最大的一个缺点,来自于原型中包含引用类型的值. 本来,我们没有通过原型链实现 ...
- js继承之原型链继承
面向对象编程都会涉及到继承这个概念,JS中实现继承的方式主要是通过原型链的方法. 一.构造函数.原型与实例之间的关系 每创建一个函数,该函数就会自动带有一个 prototype 属性.该属性是个指针, ...
- js继承的常用方法
写在前面的话:这篇博客不适合对面向对象一无所知的人,如果你连_proto_.prototype...都不是很了解的话,建议还是先去了解一下JavaScript面向对象的基础知识,毕竟胖子不是一口吃成的 ...
随机推荐
- 实现Canvas2D绘图 使元素绕中心居中旋转
我之前用canvas写了个头像剪切的demo,但是关于让载入的图片旋转是个问题,虽然通过其它方法实现了,但是感觉并不太好,于是查了些资料,想试着重新做一下canvas的旋转. 在开始之前,先让我们来做 ...
- 【Python】 汉字转化汉语拼音pinyin
pinyin pinyin模块是github上一个小项目,在github.com/cleverdeng/pinyin.py上面可以下到源码.衷心感谢那些为这个模块做出贡献的人来方便我们[鞠躬] 安装: ...
- 三十天学不会TCP,UDP/IP网络编程 -- TCP中的智慧之连续ARQ
突然发现上一篇文章贴图有问题,关键我怎么调也调不好,为了表达歉意,我再贴一篇gitbook上的吧,虽然违背了我自己的隔一篇在这里发一次的潜规则~其余完整版可以去gitbook(https://www. ...
- python安装的时候报SSL连接错误的解决办法
Collecting xlwt Could not fetch URL https://pypi.python.org/simple/xlwt/: There was a problem conf ...
- Android开发之漫漫长途 XVI——ListView与RecyclerView项目实战
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- 网络1711班 C语言第七次作业批改总结
网络1711班 C语言第七次作业批改总结 1.本次作业评分细则 1.1 基本要求(1分) 按时交 - 有分 未交 - 0分 迟交一周以上 - 倒扣本次作业分数 抄袭 - 0分 泛泛而谈(最多七分) 1 ...
- Alpha冲刺集合
Alpha冲刺集合 Day1 http://www.cnblogs.com/bugLoser/p/7901016.html Day2 http://www.cnblogs.com/bugLoser/p ...
- Java语言基础组成
写完才发现,这个博客不提供目录这个功能,真是想骂爹了...... 目录 关键字 标识符 注释 常量和变量 运算符 语句 函数 数组 1.关键字 描述:刚刚开始学这个的时候,真是傻傻分不清楚,不过没关系 ...
- Nginx在windows环境下的安装与简单配置
版权声明:本文为博主原创文章,未经博主允许不得转载. 一. 下载并安装Nginx 去Nginx官网下载 我这里选取nginx/Windows-1.10.3版本,下载后解压出来即可,解压出来的路径不能含 ...
- 我自己总结的C#开发命名规范整理了一份
我自己总结的C#开发命名规范整理了一份 标签: 开发规范文档标准语言 2014-06-27 22:58 3165人阅读 评论(1) 收藏 举报 分类: C#(39) 版权声明:本文为博主原创文章, ...