本文同时也发表在我另一篇独立博客 《Javascript: 从prototype漫谈到继承(2)》(管理员请注意!这两个都是我自己的原创博客!不要踢出首页!不是转载!已经误会三次了!)

上一篇漫谈继承的结尾我们得出了第一个比较完美的解决方案:

function extend(Child, Parent) {
var F = function(){};
F.prototype = Parent.prototype;
Child.prototype = new F();
// 一旦重置了函数的prototype,需要重新赋值prototype.constructor,
// 忽略这方面的介绍
Child.prototype.constructor = Child;
// 保留对父类的引用,
// 忽略对这方面的介绍
Child.uber = Parent.prototype;
}

这个方法的“比较完美”之处就在于引入了一个中间变量var F = function(){}。因为如果直接让子类的prototype直接继承自父类的话Child.prototype = Parent.prototype;,出于是浅拷贝,可能对子类prototype某个属性的修改会影响到父类。而是用了一个中间变量则能避免这样的情况发生,具体请参看上一篇《Javascript: 从prototype漫谈到继承(1)》

再让我们换一种思路,继承的本质是把自己能子类能访问父类的属性和方法,那么也可以one by one的把父类的属性全部拷贝过来,像

function extend2(Child, Parent) {
var p = Parent.prototype;
var c = Child.prototype;
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
}

这个方法的不是那么的优雅,因为明明是可以引用父类的属性结果又都拷贝了一份。但是尽可能的减少了引用,减少了查找的次数。

从开始谈继承到现在我们的聊的都是类与类,或者说构造函数与构造函数之间的继承,但这里是javascript,我们还需要考虑已经实例化了的对象之间的继承,把上面的extend2稍稍改造一下就是了

function extendCopy(p) {
var c = {};
for (var i in p) {
c[i] = p[i];
}
c.uber = p;
return c;
}

在使用这个方法的时候,传入的参数就不在是一个构造函数,而是一个实实在在的对象。这是jquery的$.extend继承方法最基本的思想

上面的方法,或者说以上的所有方法,都没有解决一个深浅拷贝的问题。以上的所有方法使用的都是浅拷贝(shallow copy),你拷贝的仅仅是指向原对象内存地址的指针而已,如果你修改子类继承自父类的某个属性,很可能父类的某个属性也被修改了。比如:

var c = {};
var p = {
pro: [1, 2, 3]
} c.pro = p.pro
c.pro.push(4) console.log(p.pro) //[1,2,3,4]

在上面的例子中c的pro属性继承自p.pro,pro是一个数组类型,但是当你修改c.pro时,p的pro也被修改了,这就是浅拷贝的危害!除去5种基本数据类型(Number, String, Boolean, Null, Undefined)以外的数据类型的拷贝都会是浅拷贝,但是这种危险还是要依据对子类属性的操作方式而定。比如当子类继承自父类的某个属性时object对象:

var c = {};
var p = {
name: "lee"
} c = p;
c = "kill" console.log(p)

如果你把整个属性重新赋值,父类属性是不会被修改的。其中的原理是,继承的时候你是子类和父类该属性的指针都指向同一内存区域,这样的修改只是修改了子类该属性的指针,指向另一块地方去了,但是你按下面的方式修改就有问题了

var c = {};
var p = {
name: "lee"
} c = p;
c.name = "wang" console.log(p) // wang

没错,这种情况下父类也被修改了,因为此时子类和父类的该属性还在用同一块内存区域。

解决这个问题的方法也很简单,当我们沿用上面extendCopy方法,但是在拷贝每一个属性时,我们都去检查它是否需要深拷贝,如果需要,则进行深拷贝,这就是jquery的方法,只不过它做了更多更严密的判断,比如判断object类型和array类型的时候:

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

在实战中我们除了继承外还想再生产这个对象的时候添加一些自己的属性,那么我们就可以同时用到prototype继承和一对一的拷贝属性

function objectPlus(o, stuff) {
var n;
function F() {}
F.prototype = o;
n = new F();
// 继承父类属性完毕
n.uber = o;
// 继承自定义属性
for (var i in stuff) {
n[i] = stuff[i];
}
return n;
}

最后来看看大神道格拉斯的(Douglas Crockford)提出的解决方案。

首先他提出了一个Object()方法:

function object(o) {
function F() {}
F.prototype = o;
return new F();
}

其实这个方法与我们的第一个,本文开头的extend方法类似,只不过我们的方法指定了要继承的子类

假设我们有一个var twoD = {name: "2d shape"}需要被继承,利用object他的解决方法是

  1. 利用object方法,把twoD克隆至一个that对象中,
  2. 给that对象添加自己的属性
  3. 返回that
function triangle(s, h) {
var that = object(twoD);
that.name ='Triangle';
that.getArea = function(){return this.side * this.height / 2;};
that.side = s;
that.height = h;
return that;
}

留意到triangle只是一个普通的函数,而不是一个构造函数,传入的参数是你自定义化的值

基本上介绍完了,其实还一个借用构造函数来实现继承的方法,但个人觉得,在上面那么多方法的衬托下,这个方法显得不清晰,比较晦涩一些,上面方法以及足够用了。就不予介绍了。

最后本来想贴一段jquery的extend源码让你们好好感受一下的,但是代码有点长有点宽,有兴趣的同学可以去github的jquery源码里瞧瞧,在/src/core.js文件中

最后吐槽一句,其实继承的本质我们是希望能实现以下功能

  • 父类有的我都有,我也能重载,但不至于影响到父类的属性和方法
  • 除了继承之外,我也能添加自己的方法和属性

能做到以上两点,夫复何求呢!介绍了这么多方法,只是那你有一个了解,都不是完美的,你完全可以都参考一遍然后组合一个适合自己业务逻辑的。行动吧骚年!

Javascript: 从prototype漫谈到继承(2)的更多相关文章

  1. 深入了解JavaScript中基于原型(prototype)的继承机制

    原型 前言 继承是面向对象编程中相当重要的一个概念,它对帮助代码复用起到了很大的作用. 正文 Brendan Eich在创建JavaScript时,没有选择当时最流行的类继承机制,而是借鉴Self,用 ...

  2. 【转】JavaScript中的原型和继承

    请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans ...

  3. javascript 之 prototype 浅析

    prototype 原型 javascript 是一种 prototype based programming 的语言, 而与我们通常的 class based programming 有很大 的区别 ...

  4. JavaScript 笔记 ( Prototype )

    这阵子实在好忙 ( 这样说好像也不是一两个月了... ),然后因为工作伙伴都是 JavaScript 神之等级的工程师,从中也学到不少知识,毕竟就是要和强者工作才会成长呀!为了想好好瞭解他们写的程式码 ...

  5. 【javascript基础】7、继承

    前言 由于本人水平有限,所以有些高手觉得现在写的内容偏容易,要一点点来嘛,今天和大家学习或者复习一下javascript的继承.我也就是尽量写吧······ 继承 javascript的继承其实主要就 ...

  6. 明白JavaScript原型链和JavaScrip继承

    原型链是JavaScript的基础性内容之一.其本质是JavaScript内部的设计逻辑. 首先看一组代码: <script type="text/javascript"&g ...

  7. Javascript中prototype属性详解 (存)

    Javascript中prototype属性详解   在典型的面向对象的语言中,如java,都存在类(class)的概念,类就是对象的模板,对象就是类的实例.但是在Javascript语言体系中,是不 ...

  8. ExtJS学习------Ext.define的继承extend,用javascript实现相似Ext的继承

    (1)Ext.define的继承extend 详细实例: Ext.onReady(function(){ //Sup Class 父类 Ext.define('Person',{ config:{ n ...

  9. JavaScript面向对象之Prototypes和继承

    本文翻译自微软的牛人Scott Allen Prototypes and Inheritance in JavaScript ,本文对到底什么是Prototype和为什么通过Prototype能实现继 ...

随机推荐

  1. Dom4j 对XMl解析 新手学习,欢迎高手指正

    废话不多,先看代码 public static void main(String args[]){ ReaderXml("d:/example.xml");//读取XML,传入XM ...

  2. Ubuntu iptables配置

    sudo su sudo apt-get install iptables-persistent modprobe ip_tables #启动iptable   #删除原有iptables规则 ipt ...

  3. Asp.Net Web API 2第十课——使用OWIN自承载Web API

    详情请查看http://aehyok.com/Blog/Detail/71.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:ht ...

  4. Boyer-Moore 字符串匹配算法

    字符串匹配问题的形式定义: 文本(Text)是一个长度为 n 的数组 T[1..n]: 模式(Pattern)是一个长度为 m 且 m≤n 的数组 P[1..m]: T 和 P 中的元素都属于有限的字 ...

  5. 实战-Fluxion与wifi热点伪造、钓鱼、中间人攻击、wifi破解

    原作者:PG     整理:玄魂工作室-荣杰 目录: 0x00-Fluxion是什么 0x01-Fluxion工作原理 0x02-Kali上安装fluxion 0x03-Fluxion工具使用说明+实 ...

  6. [.net 面向对象程序设计进阶] (8) 托管与非托管

    本节导读:虽然在.NET编程过程中,绝大多数内存垃圾回收由CLR(公共语言运行时)自动回收,但也有很多需要我们编码回收.掌握托管与非托管的基本知识,可以有效避免某些情况下导致的程序异常. 1.什么是托 ...

  7. VS2012编译的Windows服务启动后立即停止的解决方案

    ATL中的BUG,在没有COM的服务中,使用_ATL_NO_COM_SUPPORT. 并在服务中添加下面的代码 #if defined(_ATL_NO_COM_SUPPORT) HRESULT Pre ...

  8. python--批量下载豆瓣图片

    溜达豆瓣的时候,发现一些图片,懒得一个一个扒,之前写过c#和python版本的图片下载,因此拿之前的Python代码来改了改,折腾出一个豆瓣版本,方便各位使用 # -*- coding:utf8 -* ...

  9. PyQt5应用与实践

    一个典型的GUI应用程序可以抽象为:主界面(菜单栏.工具栏.状态栏.内容区域),二级界面(模态.非模态),信息提示(Tooltip),程序图标等组成.本篇根据作者使用PyQt5编写的一个工具,介绍如何 ...

  10. SQL Server内存理解的误区

    SQL Server内存理解 内存的读写速度要远远大于磁盘,对于数据库而言,会充分利用内存的这种优势,将数据尽可能多地从磁盘缓存到内存中,从而使数据库可以直接从内存中读写数据,减少对机械磁盘的IO请求 ...