JavaScript是单根的完全面向对象的语言

  JavaScript是单根的面向对象语言,它只有单一的根Object,所有的其他对象都是直接或者间接的从Object对象继承。而在JavaScript的众多讨论中,JavaScript的继承机制也是最让人津津乐道的,在了解它的机制之前,先让我们温习一下继承的本质。

继承的本质

  继承的本质是重用,就是这么简单,重用是软件编程技术向前发展最原始的动力。从语法上来讲,继承就是"D是B"的描述,其中B是基类,描述共性,D是子类,描述特性。例如"猫是动物",动物就是基类,猫是子类,动物的共性,比如会呼吸,猫也会,但是猫的特性,如打呼噜,别的动物就不一定有了。

JavaScript的原型继承机制

  前面我们已经总结过了原型的作用,原型链继承是JavaScript实现继承的主要方式。这种继承的典型实现方式如下:

function Base(name) {// 基对象构造函数实例成员this.name = name;
};
// 基对象原型中的实例成员
Base.prototype.showName = function() {alert(this.name);
};
// 子对象的构造函数
function Derived(name, age) {// 关键点1:获取基对象构造函数中的成员Base.call(this, name);// 定义子对象自己的成员this.age = age;
};
// 关键点2:获取基对象原型上的成员
Derived.prototype = new Base();
// 关键点3:将子对象的原型的构造函数属性设回正确的值
// 这样可以防止new对象的挂接对象出错
Derived.prototype.constructor = Derived;
// 扩展子类自己的原型成员
Derived.prototype.showAge = function() {alert(this.age);
};var d = new Derived('Frank', 10);
d.showName();
d.showAge();
// 验证继承关系
alert(d instanceof Base);

上面的实现关键点已经标了出来:

关键点1:使用call方式来调用基对象的构造函数,这样子对象就能按照基对象构造函数的逻辑来构造一份基对象构造函数中的成员。

关键点2:使用new方式来创建一个新的Base对象,然后把它挂到Derived对象的原型上,这样从"Derived对象->Derived原型->Base对象->Base原型->Object对象->Object原型"就形成了链条了。

关键点3:new操作符要求构造函数对象的原型的constructor属性要指向构造函数,而在关键点2中把Derived对象的原型完全换成Base对象了,这样有问题了。修复这个问题也很简单,直接把这个属性重新设一下就行了。

  上面的实现近乎完美,只有一个问题:Base对象的构造函数被调用了2次,一次在构造函数中,一次在构造原型链中,这样的效率并不高。而根据上面的几点说明我们知道第一次调用是为了复制基对象构造函数中的成员,必不可少;而第二次的调用纯粹是为了把原型链接上,构造函数中的成员并没有使用上,于是这里就存在一个优化的契机:既然构造函数中的成员没有使用到,那我就用一个空对象来辅助创建原型链不就就可以了,看下面的代码:

// 利用空对象来挂接原型链
function extend(Child, Parent) {var F = function(){};F.prototype = Parent.prototype;Child.prototype = new F();Child.prototype.constructor = Child;
}function Base(name) {this.name = name;
};Base.prototype.showName = function() {alert(this.name);
};function Derived(name, age) {Base.call(this, name);this.age = age;
};
// 挂接原型链
extend(Derived, Base);
Derived.prototype.showAge = function() {alert(this.age);
};var d = new Derived('Frank', 10);
d.showName();
d.showAge();
alert(d instanceof Base);

  这个版本的实现据说是YUI的实现继承的方式,个人并没参看其源代码,有这个爱好的同学可以自行研究一下。

复制继承

  既然继承的本质是复用,那么最直接的想法应该是复制基对象的所有成员,在JavaScript中确实可以这么做,看下面的代码:

// 浅拷贝实现
function extendCopy(p) {var c = {};for (var i in p) { c[i] = p[i];}return c;
}
// 深拷贝实现,最为安全
function deepCopy(p, c) {var c = c || {};for (var i in p) {if (typeof p[i] === 'object') {c[i] = (p[i].constructor === Array) ? [] : {};deepCopy(p[i], c[i]);} else {c[i] = p[i];}}return c;}
// 基对象
var Base = {name: 'Frank',showName: function() {alert(this.name);}
}
// 拷贝基对象的成员
var Derived = extendCopy(Base);
// 扩展子对象自己的成员
Derived.age = 10;
Derived.showAge = function() {alert(this.age);
};
// 测试基对象的成员
Derived.showName();

在上面的代码中,我们需要注意两个问题:

1. 我们从前面已经了解过,JavaScript有简单的“值类型”和复杂的“引用类型”,这样复制成员的时候就也有所谓的“浅拷贝”和“深拷贝”的说法。上面的代码实现了两种算法,深拷贝本质上就是为引用类型创建新的对象,这样修改的时候就不会误操作,修改了其他对象的成员。

2. 这种对象的扩展方式不能使用instanceof来检查继承关系,例如下面的代码是无效的:

alert(Derived instanceof Base);

运行这段代码会返回异常:Uncaught TypeError: Expecting a function in instanceof check, but got #<Object> 。这是因为instanceof只能用于检查函数实现的那种继承关系。

JavaScript的多态性

  谈完了JavaScript的继承机制,那就不能不说说与之密切相关的多态性。继承与多态从来都是面向对象语言中不可分割的两个概念。

  由于JavaScript是脚本语言,动态语言,所以静态的类型约束关系被压缩到了极致。这一方面体现最为明显的一点就是我们可以随意的给对象添加和删除成员,而另一个方面,很多语言都遵循“针对接口”的编程,这一点在动态语言中的表现也大为不同。在JavaScript这些动态语言中,我们不需要事先定义好一些接口,例如下面的例子:

var tank = {run : function () {alert('tank run');}
};var person = {run : function () {alert('person run');}
};
// 针对接口(run方法)的对象编程
function trigger(target) {target.run();
}trigger(tank);
trigger(person);

很多人对于这种使用方式不以为然,但是个人觉得这正是动态语言快捷编程的特点,很多时候还是很方便的。

转载于:https://www.cnblogs.com/dxy1982/p/2688525.html

JavaScript大杂烩4 - 理解JavaScript对象的继承机制相关推荐

  1. JavaScript大杂烩9 - 理解BOM

    毫无疑问,我们学习JavaScript是为了完成特定的功能.在最初的JavaScript类型系统中,我们已经分析过JavaScript在页面开发中充当着添加逻辑的角色,而且我们知道JavaScript ...

  2. javascript 对象基础 继承机制实例【对象冒充】

    面向对象语言 必须支持继承机制,既一个类能重用另一个类的方法和属性. 1.继承方式 对象冒充 工作原理:构造函数使用this关键字,给所有属性和方法赋值,因为构造函数值只是一个函数,所以可以使Clas ...

  3. javascript 符号_理解JavaScript中“ =”符号的直观指南

    javascript 符号 by Kevin Kononenko 凯文·科诺年科(Kevin Kononenko) 理解JavaScript中" ="符号的直观指南 (A Visu ...

  4. java mixin_理解Dart的Mixin继承机制

    2019独角兽企业重金招聘Python工程师标准>>> Dart语言集合了现代编程语言的众多优点,Mixin继承机制也是其一.但针对Java程序员来说,可能不是一下子能理解的,比如我 ...

  5. 读javascript高级程序设计06-面向对象之继承

    原型链是实现继承的主要方法,通过原型能让一个引用类型继承另一个引用类型. 1.原型链实现继承 function SuperType(){ this.superprop=1; } SuperType.p ...

  6. 深入理解JavaScript系列:根本没有“JSON对象”这回事!

    前言 写这篇文章的目的是经常看到开发人员说:把字符串转化为JSON对象,把JSON对象转化成字符串等类似的话题,所以把之前收藏的一篇老外的文章整理翻译了一下,供大家讨论,如有错误,请大家指出,多谢. ...

  7. 深入理解JavaScript系列(5):强大的原型和原型链

    前言 JavaScript 不包含传统的类继承模型,而是使用 prototypal 原型模型. 虽然这经常被当作是 JavaScript 的缺点被提及,其实基于原型的继承模型比传统的类继承还要强大.实 ...

  8. 深入理解JavaScript系列:This? Yes,this!

    介绍 在这篇文章里,我们将讨论跟执行上下文直接相关的更多细节.讨论的主题就是this关键字.实践证明,这个主题很难,在不同执行上下文中this的确定经常会发生问题. 许多程序员习惯的认为,在程序语言中 ...

  9. 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...

最新文章

  1. postgresql高可用_Postgresql高可用实现方案
  2. 未来的电视是什么样子?
  3. 2021牛客暑期多校训练营5 E-Eert Esiwtib(树形dp+位运算)
  4. 【Codeforces - 864D】Make a Permutation!(贪心,字典序)
  5. 前端基础:初步认识Chrome调试面板,学会简单的代码调试,必会!
  6. batik-all-1.7
  7. 20200329:K 个一组翻转链表(leetcode25)
  8. Windows button控件(按钮控件)
  9. 左链接和右链接及内链接详解
  10. 【手势识别】基于matlab GUI石头剪刀布【含Matlab源码 774期】
  11. Shiro面试题答案
  12. STK MATLAB联合仿真(一)STK与MATLAB的连接(COM与Connector)
  13. 穿山甲(巨量引擎)广告接入
  14. zabbix邮箱告警配置
  15. windows 安装h2o_H2O-安装
  16. 青出于蓝胜于蓝 (dfs序 + BIT)
  17. 切入点表达式的写法详解
  18. linux内核是如何实现分页机制的
  19. 谷歌浏览器Chrome错误代码:ERR_CONNECTION_ABORTED
  20. 运营商大数据:未来移动联通电信三大运营商数据会同步共享吗?

热门文章

  1. linux进程如何挂起自己,Linux Server HTTP进程每天挂起服务器
  2. 输出指定范围内的完数
  3. php本地文件打包代码,PHP实战:几行代码轻松实现PHP文件打包下载zip
  4. java 数据库外键查询_oracle中查询所有外键引用到某张表的记录
  5. Web前端期末大作业--响应式性感美女模特博客网页设计(HTML+CSS+JavaScript)实现
  6. Web前端期末大作业--重工机械设备检测生产企业官网网页设计(HTML+CSS+JavaScript )实现
  7. 《零基础》MySQL GROUP BY 语句(十九)
  8. 页眉页脚怎么单独设置某一页里面的_Word小技巧:如何从任意页面开始设置页眉页脚...
  9. 排序千万级数据_MySQL 对于千万级的大表要怎么优化?我写了6000字的深度解读...
  10. barrons ap计算机科学,巴朗ap心理学barrons ap psychology, 5th edition-201-240.pdf