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);

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

JavaScript大杂烩4 - 理解JavaScript对象的继承机制的更多相关文章

  1. JavaScript大杂烩3 - 理解JavaScript对象的封装性

    JavaScript是面向对象的 JavaScript是一种基于对象的语言,你遇到的所有东西,包括字符串,数字,数组,函数等等,都是对象. 面向过程还是面向对象? JavaScript同时兼有的面向过 ...

  2. JavaScript大杂烩1 - 理解JavaScript的类型系统

    随着硬件水平的逐渐提高,浏览器的处理能力越来越强大,本人坚信,客户端会越来越瘦,瘦到只用浏览器就够了,服务端会越来越丰满:虽然很多大型的程序,比如3D软件,客户端仍然会存在,但是未来的主流必将是浏览器 ...

  3. JavaScript大杂烩6 - 理解JavaScript中的this

    在JavaScript开发中,this是很常用的一个关键字,但同时也是一个很容易引入bug的一个关键字,在这里我们就专门总结一下页面中可能出现的this关键字(包括几种在其他页面文件中出现的this) ...

  4. JavaScript大杂烩2 - 理解JavaScript的函数

    JavaScript中的字面量 书接上回,我们已经知道在JavaScript中存在轻量级的string,number,boolean与重量级的String,Number,Boolean,而且也知道了之 ...

  5. Javascript学习6 - 类、对象、继承

    原文:Javascript学习6 - 类.对象.继承 Javasciprt并不像C++一样支持真正的类,也不是用class关键字来定义类.Javascript定义类也是使用function关键字来完成 ...

  6. javascript之面向对象程序设计(对象和继承)

    总结的文章略长,慎点. 知识点预热 引用类型:引用类型的值(对象)是引用类型的一个实例.在ECMAScript中,引用类型是一种数据结构,用于将数据和功能组织在一起.在其他面向对象语言中被称为类,虽然 ...

  7. JavaScript大杂烩12 - 理解Ajax

    AJAX缘由 再次谈起这个话题,我深深的记得就在前几年,AJAX被炒的如火如荼,就好像不懂AJAX,就不会Web开发一样.要理解AJAX为什么会出现,就要先了解Web开发面临的问题. 我们先来回忆一下 ...

  8. JavaScript大杂烩10 - 理解DOM

    操作DOM 终于到了JavaScript最为核心的部分了,通常来说,操作DOM,为页面提供更为友好的行为是JavaScript根本目标.   DOM树 - HTML结构的抽象 既然DOM是操纵HTML ...

  9. JavaScript大杂烩7 - 理解内置集合

    JavaScript内置了很多对象,简单的类型如String,Number,Boolean (相应的"值类型"拥有相同的方法),复杂一点的如Function,Object,Arra ...

随机推荐

  1. python中不同文件中函数和类的调用

    最近在学习Python的时候,遇到了一个不同文件中类无法调用的问题,搜了很多,发现很多人针对 这个问题都说的相当含糊,让我费了好大劲才把这个东东搞明白.记录一下,权且温习. 调用分两种,一种是同种文件 ...

  2. Cellular Traffic Offloading

    Reference: [1] Mobile Data Offload for 3G Networks [2] Mobile Data Offloading through Opportunistic ...

  3. iOS开发笔记-Xcode添加pch文件

    xcode6以后苹果取消了pch文件,需要自己创建pch文件并手动添加引用. 1.新建pch文件 2.项目Build Settings添加引用 最后检查路径是否正确,编译一下查看是否出现问题. 如果出 ...

  4. PyTorch(一)Basics

    PyTorch Basics import torch import torchvision import torch.nn as nn import numpy as np import torch ...

  5. vue moment库格式化处理后端传的日期

    开篇 日期时间格式前端和后端都可以处理,我比较推荐前端来处理(定制化高),下面我就介绍下两种处理的方式 后端处理 django默认不经处理传给前端的日期格式为2018-08-26T19:53:36.5 ...

  6. 实例分析JAVA CLASS的文件结构

    今天把之前在Evernote中的笔记重新整理了一下,发上来供对java class 文件结构的有兴趣的同学参考一下. 学习Java的朋友应该都知道Java从刚开始的时候就打着平台无关性的旗号,说“一次 ...

  7. 从零开始学 Web 之 jQuery(六)为元素绑定多个相同事件,解绑事件

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  8. Centos7安装Mysql8(官方整合包)

    1. 下载整合包 [root@master ~]# wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.12-1.el7.x86_ ...

  9. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

  10. AOP技术基础(转)

    1.引言 2.AOP技术基础 3.Java平台AOP技术研究 4..Net平台AOP技术研究 2.1 AOP技术起源 AOP技术的诞生并不算晚,早在1990年开始,来自Xerox Palo Alto ...