本文同时也发表在我另一篇独立博客 《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. php学习笔记2016.1

    基本类型    PHP是一种弱类型语言.      PHP类型检查函数   is_bool()    is_integer()  is_double()  is_string()   is_objec ...

  2. Building Apps for Windows Phone 8.1教程下载地址整理

    官方教程地址http://channel9.msdn.com/Series/Building-Apps-for-Windows-Phone-8-1http://media.ch9.ms/ch9/8db ...

  3. 【Win10 UWP】微信SDK基本使用方法和基本原理

    上回讲到,作为一个长期散播温暖,散播希望的小清新无公害WP开发者,继QQ SDK之后,又把UWP微信SDK这茬了结了,仅供学习交流. 1.安装微信SDK for UWP 微信官方此前明确说明短时间内暂 ...

  4. android知识杂记(二)

    记录项目中的android零碎知识点,用以备忘. AsyncQueryHandler 继承与handler,可以用于处理增删改(ContentProvider提供的数据) 例如:query = new ...

  5. Windows Phone App的dump 文件分析

    前言 我们在发布了自己的App以后,Windows Phone的Error Report机制会帮助我们收集程序的崩溃信息并发送到微软的服务器上,这可以辅助开发者提高App的稳定性. 那么如何利用这些d ...

  6. sublime 2/3 for mac link to command

    Sublime command for mac : execute those command: subl 2: sudo ln -s /Applications/Sublime\ Text\ 2.a ...

  7. Senparc.Weixin.MP SDK 微信公众平台开发教程(九):自定义菜单接口说明

    上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(八):通用接口说明>介绍了如何通过通用接口获取AccessToken,有了AccessToken,我们就可以来操作 ...

  8. IOS UIView 01-View开始深入 绘制像素到屏幕上

    注:本人是翻译过来,并且加上本人的一点见解. 前言 一个像素是如何绘制到屏幕上去的?有很多种方式将一些东西映射到显示屏上,他们需要调用不同的框架.许多功能和方法的结合体.这里我们大概的看一下屏幕之后发 ...

  9. Atitit   图像处理 平滑 也称 模糊, 归一化块滤波、高斯滤波、中值滤波、双边滤波)

    Atitit   图像处理 平滑 也称 模糊, 归一化块滤波.高斯滤波.中值滤波.双边滤波) 是一项简单且使用频率很高的图像处理方法 用途 去噪 去雾 各种线性滤波器对图像进行平滑处理,相关OpenC ...

  10. Atitit.eclipse 4.3 4.4  4.5 4.6新特性

    Atitit intellij idea的使用总结attilax 1. ideaIC-2016.2.4.exe1 1.1. Ij vs eclipse市场份额1 1.2. Ij的优点(方便的支持gro ...