详解JavaScript基于面向对象之继承


Posted in Javascript onDecember 13, 2015

一、面相对象继承机制
      这个实例使用UML很好的解释了继承机制。
      说明继承机制最简单的方式是,利用一个经典的例子就是几何形状。实际上,几何形状只有两种,即椭圆形(是圆形的)和多边形(具有一定数量的边)。圆是椭圆的一种,它只有一个焦点。三角形、矩形和五边形都是多边形的一种,具有不同数量的边。正方形是矩形的一种,所有的边等长。这就构成了一种完美的继承关系,很好的解释了面向对象的继承机制。
       在这个例子中,形状是椭圆形和多边形的基类(通常我们也可以叫它父类,所有类都由它继承而来)。椭圆具有一个属(foci),说明椭圆具有的焦点的个数。圆形继承了椭圆形,因此圆形是椭圆形的子类,椭圆形是圆形的超类。同样,三角形、矩形和五边形都是多边形的子类,多边形是它们的超类。最后,正方形继承了矩形。
      最好用图来解释这种继承关系,这是 UML(统一建模语言)的用武之地。UML的主要用途之一是,可视化地表示像继承这样的复杂对象关系。下面的图示是解释形状和它的子类之间关系的UML图示:

详解JavaScript基于面向对象之继承

      在UML中,每个方框表示一个类,由类名说明。三角形 、矩形和五边形顶部的线段汇集在一起,指向形状,说明这些类都由形状继承而来。同样,从正方形指向矩形的箭头说明了它们之间的继承关系。
二、ECMAScript继承机制的实现
      要用ECMAScript实现继承机制,您可以从要继承的基类入手。所有开发者定义的类都可作为基类。出于安全原因,本地类和宿主类不能作为基类,这样可以防止公用访问编译过的浏览器级的代码,因为这些代码可以被用于恶意攻击。
       选定基类后,就可以创建它的子类了。是否使用基类完全由你决定。有时,你可能想创建一个不能直接使用的基类,它只是用于给子类提供通用的函数。在这种情况下,基类被看作抽象类。尽管ECMAScript并没有像其他语言那样严格地定义抽象类,但有时它的确会创建一些不允许使用的类。通常,我们称这种类为抽象类。
      创建的子类将继承超类的所有属性和方法,包括构造函数及方法的实现。记住,所有属性和方法都是公用的,因此子类可直接访问这些方法。子类还可添加超类中没有的新属性和方法,也可以覆盖超类的属性和方法。由于JS并不是正统的面向对象语言,一些名词也需要做出改变。
三、ECMAScript继承的方式
      ECMAScript语言中将被继承的类(基类)称为超类型,子类(或派生类)称为子类型。和其他功能一样,ECMAScript实现继承的方式不止一种。这是因为JavaScript中的继承机制并不是明确规定的,而是通过模仿实现的。这意味着所有的继承细节并非完全由解释程序处理。作为开发者,你有权决定最适用的继承方式。下面为您介绍几种具体的继承方式。
(1)原型链方式
      继承这种形式在ECMAScript中原本是用于原型链的。上一篇博文已经介绍了创建对象的原型方式。原型链扩展了这种方式,以一种有趣的方式实现继承机制。prototype 对象是个模板,要实例化的对象都以这个模板为基础。总而言之,prototype 对象的任何属性和方法都被传递给那个类的所有实例。原型链利用这种功能来实现继承机制。我们来看一个例子:

function A() {//超类型A中必须没有参数 
 this.color = "red"; 
 this.showColor = function () { 
  return this.color; 
 }; 
}; 
function B() {//子类型B 
 this.name = "John"; 
 this.showName = function () { 
  return this.name; 
 }; 
}; 
B.prototype = new A();//子类型B继承了超类型A,通过原型,形成链条 
var a = new A(); 
var b = new B(); 
document.write(a.showColor());//输出:blue 
document.write(b.showColor());//输出:red 
document.write(b.showName());//输出:John

      在原型链中,instanceof运算符的运行方式也很独特。对B的所有实例,instanceof为A和B都返回true。ECMAScript的弱类型世界中,这是极其有用的工具,不过使用对象冒充时不能使用它。例如:

var b = new B(); 
document.write(b instanceof A);//输出:true 
document.write(b instanceof B);//输出:true

       使用原型链方式实现了继承,但是这种方式无法共享和子类型给超类型传递参数。我们可以借用构造函数方式(也就是对像冒充)的方式来解决这两个问题。
(2)对象冒充方式
      对象冒充方式的其原理如下:构造函数使用this关键字给所有属性和方法赋值(即采用对象声明的构造函数方式)。因为构造函数只是一个函数,所以可使A构造函数成为B的方法,然后调用它。B就会收到A的构造函数中定义的属性和方法。例如,用下面的方式改写上面的例子创建对象A和B:
call()方法

function A(Color) {//创建超类型A 
 this.color = Color; 
 this.showColor = function () { 
   return this.color; 
 }; 
}; 
function B(Color,Name) {//创建子类型B 
 A.call(this, Color);//对象冒充,给超类型传参 
 this.name = Name;//新添加的属性 
 this.showName = 
}; 
var a = new A("blue"); 
var b = new B("red", "John"); 
document.write(a.showColor());//输出:blue 
document.write(b.showColor());//输出:red 
document.write(b.showName());//输出:John

apply()方法
和上面call()方法唯一的区别就是在子类型B中的代码:
A.call(this,arguments);//对象冒充,给超类型传参 
      当然,只有超类型中的参数顺序与子类型中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。
      使用对象冒充方式虽然解决了共享和传参的问题,但是没有原型,复用就更不可能了,所以我们组合上述的两种方式,即原型链方式和对象冒充的方式实现JS的继承。
(3)混合方式
      这种继承方式使用构造函数定义类,并非使用任何原型。对象冒充的主要问题是必须使用构造函数方式,这不是最好的选择。不过如果使用原型链,就无法使用带参数的构造函数了。开发者如何选择呢?答案很简单,两者都用。由于这种混合方式使用了原型链,所以instanceof运算符仍能正确运行。
       在上一篇文章,创建对象的最好方式是用构造函数定义属性,用原型定义方法。这种方式同样适用于继承机制,用对象冒充继承构造函数的属性,用原型链继承prototype对象的方法。用这两种方式重写前面的例子,代码如下:

function A(Color) { 
 this.color = Color; 
}; 
A.prototype.showColor = function () { 
 return this.color; 
}; 
function B(Color, Name) { 
 A.call(this, Color);//对象冒充 
 this.name = Name; 
}; 
B.prototype = new A();//使用原型链继承 
B.prototype.showName = function () { 
 return this.name; 
}; 
var a = new A("blue"); 
var b = new B("red", "John"); 
document.write(a.showColor());//输出:blue 
document.write(b.showColor());//输出:red 
document.write(b.showName());//输出:John

       继承的方式和创建对象的方式有一定的联系,推荐使用的继承方式还时原型链和对象冒充的混合方式。使用这种混合方式可以避免一些不必要的问题。
       看这篇文章的时候,必须看一下前面的创建对象的方式:详解JavaScript基于面向对象之创建对象(1)详解JavaScript基于面向对象之创建对象(2)

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
JS鼠标事件大全 推荐收藏
Nov 01 Javascript
JS格式化数字金额用逗号隔开保留两位小数
Oct 18 Javascript
JavaScript实现生成GUID(全局统一标识符)
Sep 05 Javascript
JavaScript中发布/订阅模式的简单实例
Nov 05 Javascript
js+html5实现复制文字按钮
Jul 15 Javascript
jQuery实现的页面遮罩层功能示例【测试可用】
Oct 14 jQuery
jquery学习笔记之无new构建详解
Dec 07 jQuery
webpack 3.X学习之多页面打包的方法
Sep 04 Javascript
js实现每日签到功能
Nov 29 Javascript
VUE组件中的 Drawer 抽屉实现代码
Aug 06 Javascript
vue实现表格过滤功能
Sep 27 Javascript
15分钟上手vue3.0(小结)
May 20 Javascript
轻松使用jQuery双向select控件Bootstrap Dual Listbox
Dec 13 #Javascript
基于jQuery通过jQuery.form.js插件实现异步上传
Dec 13 #Javascript
推荐阅读的js快速判断IE浏览器(兼容IE10与IE11)
Dec 13 #Javascript
JS如何判断是否为ie浏览器的方法(包括IE10、IE11在内)
Dec 13 #Javascript
javascript性能优化之DOM交互操作实例分析
Dec 12 #Javascript
JavaScript文档碎片操作实例分析
Dec 12 #Javascript
javascript性能优化之事件委托实例详解
Dec 12 #Javascript
You might like
php数组函数序列之asort() - 对数组的元素值进行升序排序,保持索引关系
2011/11/02 PHP
深入php内核之php in array
2015/11/10 PHP
详解PHP对数组的定义以及数组的创建方法
2015/11/27 PHP
laravel自定义分页效果
2017/07/23 PHP
PHP按一定比例压缩图片的方法
2018/10/12 PHP
PHP的PDO事务与自动提交
2019/01/24 PHP
关于__defineGetter__ 和__defineSetter__的说明
2007/05/12 Javascript
Javascript学习笔记2 函数
2010/01/11 Javascript
让textarea自动调整大小的js代码
2011/04/12 Javascript
Angular.js回顾ng-app和ng-model使用技巧
2016/04/26 Javascript
javascript实现平滑无缝滚动
2020/08/09 Javascript
巧用jQuery选择器提高写表单效率的方法
2016/08/19 Javascript
JS实现数组去重复值的方法示例
2017/02/18 Javascript
详解Angular5 服务端渲染实战
2018/01/04 Javascript
Vue自定义指令封装节流函数的方法示例
2018/07/09 Javascript
vue项目前端错误收集之sentry教程详解
2019/05/27 Javascript
快速对接payjq的个人微信支付接口过程解析
2019/08/15 Javascript
VUE:vuex 用户登录信息的数据写入与获取方式
2019/11/11 Javascript
Node.js API详解之 dgram模块用法实例分析
2020/06/05 Javascript
python使用xauth方式登录饭否网然后发消息
2014/04/11 Python
Python cookbook(数据结构与算法)在字典中将键映射到多个值上的方法
2018/02/18 Python
python ddt实现数据驱动
2018/03/14 Python
Python requests发送post请求的一些疑点
2018/05/20 Python
以SQLite和PySqlite为例来学习Python DB API
2020/02/05 Python
python 实现表情识别
2020/11/21 Python
Html5 audio标签样式的修改
2016/01/28 HTML / CSS
编辑个人求职信范文
2013/09/21 职场文书
资产经营总监岗位职责范文
2013/12/01 职场文书
广告学专业自荐信范文
2014/02/24 职场文书
公司会计主管岗位责任制
2014/03/01 职场文书
2014年感恩母亲演讲稿
2014/05/27 职场文书
大学竞选班干部演讲稿
2014/08/21 职场文书
2015世界地球日活动总结
2015/02/09 职场文书
个人党性分析总结
2015/03/05 职场文书
2015年全国爱眼日活动方案
2015/05/05 职场文书
学校青年志愿者活动总结
2015/05/06 职场文书