本文同时也发表在我另一篇独立博客 《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. centos7 memcached+memagent 集群

    1. 安装libevent wget https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libe ...

  2. 使用SQLIO测试磁盘性能

    SQLIO 是一个用于测试存储系统能力的命令行工具,用以获取存储系统相关的性能指标,以判断系统的 I/O 处理能力. 在微软的网站可以下载 SQLIO 的安装包,安装后目录中会出现如下文件: EULA ...

  3. Javascript事件模型系列(二)事件的捕获-冒泡机制及事件委托机制

    一.事件的捕获与冒泡 由W3C规定的DOM2标准中,一次事件的完整过程包括三步:捕获→执行目标元素的监听函数→冒泡,在捕获和冒泡阶段,会依次检查途径的每个节点,如果该节点注册了相应的监听函数,则执行监 ...

  4. JS闭包中的this对象

    我们知道,当函数被作为某个对象的方法调用时,this等于那个对象,不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window. 下面代码很好的说明了这一点: var name = 'T ...

  5. 欢迎访问我的快站fbengine.kuaizhan.com

    欢迎访问我的快站 fbengine.kuaizhan.com

  6. [安卓] 16、ListView和GridView结合显示单元实现自定义列表显示效果

    List在各种手机应用中都有体现,是安卓UI设计的必修课. 本文将介绍在开发中如何利用ListView和GridView设计自定义列表. 下面分别是用ListView和GridView做的效果: 上面 ...

  7. 跨站脚本攻击XSS

    跨站脚本攻击(Cross Site Script为了区别于CSS简称为XSS)指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到 ...

  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明

    紧接上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明>,这里专讲OAuth2.0. 理解OAuth2.0 首先我们通过一张图片来了解一下OAu ...

  9. Git学习笔记(8)——标签管理

    本文主要记录的Git标签的作用.标签的多种创建方式,以及标签的删除,与推送,和使用GitHub的Fork参与别人的项目. 标签的作用 发布版本时,通常先在版本库中打一个标签,这样,就唯一确定了打标签时 ...

  10. 容器使用的12条军规——《Effective+STL中文版》试读

    容器使用的12条军规——<Effective+STL中文版>试读     还 记的自己早年在学校学习c++的时候,老师根本就没有讲STL,导致了自己后来跟人说 起会C++的时候总是被鄙视, ...