本文同时也发表在我另一篇独立博客 《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. .NET开发的大型网站列表、各大公司.NET职位精选,C#王者归来

    简洁.优雅.高效的C#语言,神一样的C#创始人Anders Hejlsberg,async/await编译器级异步语法,N年前就有的lambda表达式,.NET Native媲美C++的原生编译性能, ...

  2. 排序 via F#

    冒泡排序: let rec bsort_asc s = let rec _bsort_asc = function |x1::x2::xs when x1 > x2 -> match _b ...

  3. [后端人员耍前端系列]KnockoutJs篇:快速掌握KnockoutJs

    一.引言 之前这个系列文章已经介绍Bootstrap.由于最近项目中,前端是Asp.net MVC + KnockoutJs + Bootstrap来做的.所以我又重新开始写这个系列.今天就让我们来看 ...

  4. 图解:Arcgis Server 安装

    必须保证IIS配置正常,否则arcserver安装不会成功. 选择安装路径,还是尽量不要在有括号的文件夹下. 设置服务名,最好使用默认的. 点击完成后会要求进行服务配置. 在windows serve ...

  5. 自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药

    自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ...

  6. Spring-Context之七:使用p-namesapce和c-namespace简化bean的定义

    在Spring中定义bean的方式多种多样,即使使用xml的方式来配置也能派生出很多不同的方式. 比如如下的bean定义: 1 2 3 4 5 6 7 8 9 10 11 12 <beans x ...

  7. 看stackoverflow大牛如何回答何时在ASP.NET中使用异步控制器?

    转载自博客园:http://farb.cnblogs.com/ 今天无意中看到stackoverflow上一个很好的问答,个人觉得很有价值,所以翻译过来和大家共享!希望大家能相互交流. 在ASP.NE ...

  8. Node.js与Sails~方法拦截器policies

    回到目录 policies sails的方法拦截器类似于.net mvc里的Filter,即它可以作用在controller的action上,在服务器响应指定action之前,对这个action进行拦 ...

  9. Atitit 团队建设的知识管理

    Atitit 团队建设的知识管理 1.1. 要遵循"知识积累--创造--应用--形成知识平台--再积累--再创造--再应用--形成新的知识平台"的循环过程.1 1.2. 显性知识包 ...

  10. 发现一个百度的密码。。。记最近一段时间的php感想

    请看图. 突然想看一下百度的cookie. 最近百度一年真是多攒多难,我一直挺百度啊.百度文化就是程序员文化,但是收到中国其他文化的侵蚀,不得不变, 任何人重构系统,都会有大概百分三十左右的性能提升. ...