本文同时也发表在我另一篇独立博客 《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. toolControls添加工具项

    最近参考Arcengine的Samples做的功能,虽然简单,但是示例代码的确体现出了很好的封装性,值得学习,效果图如下: 闲话休絮,直入正题: 一.首先建立工具类,实现IMenuDef接口 clas ...

  2. C#执行外部程序之执行DOS命令和批处理

    在项目开发中,有时候要处理一些文件,比如视频格式的转换,如果用C开发一套算法,再用C#调用,未免得不偿失!有时候调用现有的程序反而更加方便.今天就来说一下C#中如何调用外部程序,执行一些特殊任务. 这 ...

  3. 基础调试命令 - .dump/.dumpcap/.writemem/!runaway

    Windbg是windows平台上强大的调试器,它相对于其他常见的IDE集成的调试器有几个重要的优势, Windbg可以做内核态调试 Windbg可以脱离源代码进行调试 Windbg可以用来分析dum ...

  4. 【面试必备】CSS盒模型的点点滴滴

    从接触CSS布局开始,就一直在听盒模型的概念了,网上的文章有很多,深浅不一.有些人会认为盒模型很简单,不就是border.margin.padding.content嘛,一个元素所占的空间就是把它们都 ...

  5. 为jQuery的$.ajax设置超时时间

    jQuery的ajax模块封装了非常强大的功能,有时候我们在发送一个ajax请求的时候希望能有一个超时的时间,想让程序在一段时间请求不到数据时做出一些反馈.幸运的是jQuery为我们提供了这样的参数: ...

  6. 浅谈Excel开发:三 Excel 对象模型

    前一篇文章介绍了Excel中的菜单系统,在创建完菜单和工具栏之后,就要着手进行功能的开发了.不论您采用何种方式来开发Excel应用程序,了解Excel对象模型尤其重要,这些对象是您与Excel进行交互 ...

  7. JavaScript思维导图—数据类型

    JavaScript思维导图-来自@王子墨http://julying.com/blog/the-features-of-javascript-language-summary-maps/

  8. 使用mybatis访问sql server

    原创文章转载请注明出处:@协思, http://zeeman.cnblogs.com mybatis作为一种半自动化的ORM工具,可以提供更大的灵活性,逐渐受到社区的欢迎. 官方下载地址是:https ...

  9. querySelector和querySelectorAll

    jQuery被开发者如此的青睐和它强大的选择器有很大关系,比起笨重的document.getElementById.document.getElementByName… ,查找元素很方便,其实W3C中 ...

  10. C++ std::multiset

    std::multiset template < class T, // multiset::key_type/value_type class Compare = less<T>, ...