JavaScript大杂烩4 - 理解JavaScript对象的继承机制

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/575411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css3盒模型、过渡、转换介绍

CSS3中盒模型&#xff1a; 前面CSS中学到的盒子模型给padding、border会撑开盒子的大小&#xff0c;实际大小要通过计算才能得到&#xff0c;为了解决这个问题&#xff0c;CSS3推出了box-sizing属性来解决此问题&#xff0c;当box-sizing的属性值为border-box时&#xff0c;无…

【Leetcode】Sort List

Sort a linked list in O(n log n) time using constant space complexity. 单向链表排序O(nlogn)&#xff0c;Mergesort可以实现。 1 /**2 * Definition for singly-linked list.3 * struct ListNode {4 * int val;5 * ListNode *next;6 * ListNode(int x) :…

css3中animation动画、浏览器私有前缀、文字阴影、滤镜

animation动画&#xff1a; 可以设置多个节点来控制一个或 一组动画&#xff0c;动画可以实现更多变化&#xff0c;更多控制&#xff0c;连续自动播放等效果。动画的使用分两步&#xff1a;1、定义动画 2、调用动画&#xff08;调用必须写调用名称和持续时间)&#xff0c;其属…

ASP.NET 页生命周期

参考&#xff1a;http://msdn.microsoft.com/zh-cn/library/ms178472(vvs.100).aspx转载于:https://www.cnblogs.com/pengpenghappy/p/3778721.html

计算机基础简介、编程语言、翻译器、数据储存

计算机基础&#xff1a; 编程语言&#xff1a; 编程语言分机器语言、汇编语言、高级语言&#xff0c;机器语言只有计算机本身认识&#xff0c;是二进制的&#xff1b;汇编语言是较低层的指令式语言&#xff0c;有很多指令单词发出命令&#xff0c;程序员可以认识&#xff0c;…

IE8“开发人员工具”(下)

浏览器模式 说白了&#xff0c;就是让用户选择当前页面用何种IE版本去渲染。 文本模式 说起“文本模式”这个名词&#xff0c;这又要回到渲染页面的3种模式了&#xff1a;诡异模式&#xff08;Quirks mode&#xff0c;也有翻译为兼容模式、怪异模式的&#xff09;&#xff0c;标…

javascript历史、作用、三大组成、javascript代码书写位置、注意事项、变量

JavaScript简介 JavaScript历史&#xff1a; JavaScript是一门解释型、动态类型、基于对象的脚本语言(不需要编译&#xff0c;直接执行&#xff0c;与之相对的是编译型语言)&#xff0c;由美国网景公司的布兰登艾奇发明&#xff0c;起初称LiveScript&#xff0c;其简称js。 …

数据字典视图

数据字典视图 数据自动视图分为三大类&#xff0c;分别用对应的前缀表示为user_*,all_*,dba_*分别表示的意思如下&#xff1a; user_*:有关用户所拥有的对象的信息&#xff0c;即用户自己创建的对象的信息 all_*:有关用户可以访问的对象的信息&#xff0c;即用户自己创建的对象…

javascript中数据类型及转换、String()和toString()的区别

数据类型&#xff1a; JavaScript中的数据类型&#xff1a; 在计算机中&#xff0c;不同的数据类型所占的储存空间是不同的。1、原始数据类型&#xff1a;number (数字)、string&#xff08;字符串&#xff09;、boolean&#xff08;布尔值&#xff09;、null&#xff08;空对…

Algorithm(1) - Karatsuba multiplication

这个系列主要是记一下目前效率较高或者比较出名的一些算法. Karatsuba multiplication: x5678 then: a56 b67 y1234 c12 d34 setps: 1: a*c 672 ① 2: b*d2652 ② 3: (ab)(cd)6164 ③ 4: ③-②-①2840 5: 6720000 2652284000 7006652 Recursive …

javascript中基本包装、算数运算符、递增递减运算符、赋值运算符、比较运算符、逻辑运算符、运算符优先级

基本包装类型&#xff1a; 指基本类型的数据变量通过调用属性或者方法包装成了复杂类型&#xff0c;这个变量也称为基本包装类型对象 <script>var str hello;str str.replace(ll, ee);console.log(str); //heeeo//通过基本包装&#xff0c;无论Boolean对象中传入true还…

grootjs 简明教程

grootJs简明教程 mvvm框架也是解决的一类问题&#xff0c;在某些时候会提高生产效率&#xff1b; 经过接近一个月的努力&#xff0c;grootJs测试版终于发布了 grootJs是一个mvvm的框架&#xff0c;名字取 grass 和root 两个单词的组合&#xff0c;既“草根”之意。在创作的中…

流程控制介绍,顺序结构、分支结构、循环结构、Promise对象、throw语句、break和continue关键字

流程控制 流程控制&#xff1a;指代码的执行顺序&#xff0c;有三种方式&#xff1a;顺序结构、分支结构、循环结构 顺序结构&#xff1a; 代码从上到下&#xff0c;从左到右的执行顺序。 分支语句&#xff1a; if语句、if-else语句、if-else if-else语句、switch-case语…

javascript中数组、冒泡排序、函数及函数实参形参、arguments伪数组、异步函数等介绍

数组&#xff1a; 指一组有顺序的数据&#xff0c;其作用就是用来一次性存储多个数据。&#xff08;数组元素&#xff1a;数组中的每一个数据&#xff1b;数组长度&#xff1a;数据中元素的个数&#xff1b;数组索引&#xff1a;用来存储或访问数组中的数据&#xff0c;也叫下…

中文字串截取无乱码的问题

UTF-8中文截取函数在PHP中&#xff0c;substr()函数截取带有中文字符串的话&#xff0c;可能会出现乱码&#xff0c;这是因为中西文一个字节所占有的字节数不一样&#xff0c;而substr的长度参数是按照字节去算的&#xff0c;在GB2312编码时&#xff0c;一个中文占2个字节&…

javascript中作用域、全局作用域、局部作用域、隐式全局变量、块级作用域、作用域链、预解析

作用域 作用域指的是代码的作用范围&#xff0c;按照作用域划分变量可分为全局变量和局部变量&#xff1b;作用域可分为&#xff1a; 全局作用域&#xff1a; 指全局变量作用的范围&#xff1b;全局变量指的是通过var在函数外面声明的变量&#xff0c;在js中任何位置都可以使…

楼宇对讲门铃的芯片选型分析

目前很多的高层住宅都使用了对讲门铃了&#xff0c;在频繁使用中&#xff0c;门铃会出现的越来越多种类&#xff0c;下面我就简单的介绍会有用到的几款芯片. 语音通话芯片&#xff1a;D34018,D34118,D5020,D31101; D34018 单片电话机通话电路&#xff0c;合并了必 需的放大器…

easyui 布局自适应

最近在把以前写的一个项目改成用easyui做前端。过程中遇到了不少问题。其中一个就是datagrid不能很好的布局。想了好多办法都有局限。最后想到会不会是布局&#xff08;easyui-layout&#xff09;的问题&#xff0c;经过实验&#xff0c;最后问题解。 1&#xff1a;比如在项目中…

javascript中对象、JSON格式数据、创建对象的方式、数据类型分类及特点

对象 对象指&#xff1a;具体的一个实物&#xff0c;javascript中对象是指一组没有顺序的属性和方法的集合&#xff0c;所有的事物都是对象&#xff0c;例如&#xff1a;函数&#xff0c;数组&#xff0c;字符串等。属性指事物的特征&#xff0c;一般为名词表示&#xff1b;方…

在存储过程中编写正确的事务处理代码

在 SQL Server 中数据库事务处理是个重要的概念&#xff0c;也稍微有些不容易理解&#xff0c;很多 SQL 初学者编写的事务处理代码存往往存在漏洞&#xff0c; 本文介绍了三种不同的方法&#xff0c;举例说明了如何在存储过程事务处理中编写正确的代码。 在编写 SQL Server 事务…