前面谈到了javascript的类式继承。这篇继续部分类式继承,及一些现代继承。

类式继承模式-代理构造函数

这种模式通过断开父对象与子对象之间原型之间的直接链接关系,来解决上次说到的共享一个原型所带来的问题,而且同时可以继续原型链带来的好处。

代码:

function inherit(C,P){
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
}

可以看到,在这里面有一个空构造函数F(),充当了子对象和父对象之间的一个代理,F()的原型指向父对象的原型。子对象的原型是一个空白函数实例。
这个模式与默认原型继承模式有一些不同,是因为这里的子对象仅继承了原型的属性。这个情况比较理想,也更加可取,因为原型是放置复用功能的位置。在这种模式中,父构造函数添加到this的成员都不会被继承。示意图如下:

可以创建一个子对象:

var kid = new Child();

访问kid.say()时,在上图对象3中没有找到方法,需查询原型链。对象4也没有,对象1中有,可以调用这个方法。
在实际应用中,可以添加一个指向父构造函数原型对象的引用以备使用:

function inherit(C,P){
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
}

下面还需要重置构造函数指针,以便使用:

function Parent(){}
function Child(){}
inherit(Child,Parent); var kid = new Child();
kid.constructor.name;//"Parent
kid.constructor === Parent;//true
function inherit(C,P){
var F = function(){};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
C.prototype.constructor = C;
}

这里还可以做的一个优化是避免每次需要继承时都创建代理构造函数,仅创建一次代理函数,并修改它的原型,这个已经可以达到目的了。在具体实现上,可以使用立即调用函数并且在闭包中存储代理函数:

var inherit =  (fucntion(){
var F = function(){};
return function(C,P){
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
C.prototype.constructor = C;
};
})();

现代继承-对象的原型继承

这个是一种无类继承模式,不涉及类,这里的对象都是继承子其它对象。可以用这个方式考虑:有一个想要复用的对象,并且创建的第二个对象要从第一个对象中获取内容。

代码:

var parent = {
name:"Papa"
}; var child = object(parent);
child.name;//"Papa"

parent是以对象字面量方式创建的一个父对象,需要创建一个与parent有相同属性和方法的child对象,其中的object()函数实现如下,与类式继承的代理构造函数类似:

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

下图显示了使用这个模式的原型链。child最初是一个空对象,没有自身的属性,同时又通过_proto_链接而具有父对象的全部功能。

在这个模式中,不需要使用对象字面量来创建父对象,其实也可以使用构造函数创建,如果使用构造函数,自身的属性和构造函数的原型属性都将被继承:

function Person(){
this.name = "Adam";//own property
}
Person.prototype.getName = function(){
return this.name;
};
var papa = new Person();
var kid = object(papa);
kid.getName();//"Adam"

其实可以选择只继承现有构造函数的原型对象,如下:

function Person(){
this.name = "Adam";//own property
}
Person.prototype.getName = function(){
return this.name;
};
var kid = object(Person.prototype);
typeof kid.getName();//function
typeof kid.name;//undefined

在ES5中,原型继承模式已成为该语言的一部分,是通过Object.create()来实现的:

var child = Object.create(parent);

Object.create()接受另外一个参数,可传入一个对象,这个对象的属性将作为新对象的自身属性:

var child = Object.create(parent,{
age:{value:2}
)};
child.hasOwnProperty("age");//true

现代继承-通过复制属性实现继承

下面是extend()的一个例子:

function extend(parent,child){
var i;
child = child||{};
for (i in parent){
if(parent.hasOwnProperty(i)){
child[i] = parent[i];
}
}
return child;
}

使用方法如下:

var dad = {name:"Adam"};
var kid = extend(dad);
kid.name;//"Adam"

上面实现的是浅复制,深复制需要进行属性检查,如果复制的是一个对象或一个数组,在使用浅复制的时候,如果改变了子对象的属性,且该属性是一个对象,父对象也会被修改,这个情况可能会导致意外:

var dad = {
counts:[1,2,3],
reads:{paper:true}
};
var kid = extend(dad);
kid.counts.push(4);
dad.counts.toString();//"1,2,3,4"
dad.reads === kid.reads;//true

实现深复制,需要修改extend(),判断每个属性是对象还是数组:

function extendDeep(parent,child){
var i,
toStr = Object.prototype.toString,
astr = "[object Array]";
child = child||{};
for (i in parent){
if(parent.hasOwnProperty(i)){
if(typeof parent[i] === "object"){
child[i] = (toStr.call(parent[i]) === astr?) [] : {};
extendDeep(parent[i],child[i]);
}else{
child[i] = parent[i];
}
}
}
return child;
}

测试:

var dad = {
counts:[1,2,3],
reads:{paper:true}
};
var kid = extendDeep(dad);
kid.counts.push(4);
kid.counts.toString();//"1,2,3,4"
dad.counts.toString();//"1,2,3" dad.reads === kid.reads;//false
kid.reads.paper = false;
dad.reads.paper;//true

这种模式应用广泛,例如FireBug有一个extend()方法可以实现浅复制,Jquery中的extend()可以实现深复制。

javascript代码复用模式(三)的更多相关文章

  1. javascript代码复用模式(二)

    前面说到,javascript的代码复用模式,可分为类式继承和非类式继承(现代继承).这篇就继续类式继承. 类式继承模式-借用构造函数 使用借用构造函数的方法,可以从子构造函数得到父构造函数传任意数量 ...

  2. javascript代码复用模式

    代码复用有一个著名的原则,是GoF提出的:优先使用对象组合,而不是类继承.在javascript中,并没有类的概念,所以代码的复用,也并不局限于类式继承.javascript中创建对象的方法很多,有构 ...

  3. 《JavaScript 模式》读书笔记(6)— 代码复用模式2

    上一篇讲了最简单的代码复用模式,也是最基础的,我们普遍知道的继承模式,但是这种继承模式却有不少缺点,我们下面再看看其它可以实现继承的模式. 四.类式继承模式#2——借用构造函数 本模式解决了从子构造函 ...

  4. 《JavaScript模式》第6章 代码复用模式

    @by Ruth92(转载请注明出处) 第6章:代码复用模式 GoF 在其著作中提出的有关创建对象的建议原则: -- 优先使用对象组合,而不是类继承. 传统模式:使用类继承: 现代模式:"类 ...

  5. 深入理解JavaScript系列(46):代码复用模式(推荐篇)

    介绍 本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用. 模式1:原型继承 原型继承是让父对象作为子对象的原型,从而达到继承的目的: function object(o) { fun ...

  6. 深入理解JavaScript系列(45):代码复用模式(避免篇)

    介绍 任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量 ...

  7. javascript代码复用(四)-混入、借用方法和绑定

    这篇继续说js的现代复用模式:混入.借用方法和绑定. 混入 可以针对前面提到的通过属性复制实现代码复用的想法进行一个扩展,就是混入(mix-in).混入并不是复制一个完整的对象,而是从多个对象中复制出 ...

  8. 《JavaScript 模式》读书笔记(6)— 代码复用模式3

    我们之前聊了聊基本的继承的概念,也聊了很多在JavaScript中模拟类的方法.这篇文章,我们主要来学习一下现代继承的一些方法. 九.原型继承 下面我们开始讨论一种称之为原型继承(prototype ...

  9. javascript代码复用--继承

    由于javascript没有类的概念,因此无法通过接口继承,只能通过实现继承.实现继承是继承实际的方法,javascript中主要是依靠原型链要实现. 原型链继承 原型链继承是基本的继承模式,其本质是 ...

随机推荐

  1. Embedded System.

    Soc ( System on Chip) Soc is an integrated circuit (IC) that integrates all components of a computer ...

  2. NeHe OpenGL教程 第四十四课:3D光晕

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  3. A^B问题

    题目:输入包括两个正整数 A 和 B(1 <= A, B <= 10000),A 和 B 之前用空格隔开 ★数据输出 输出一行,若 A ^ B < 10000,则直接输出结果(不包含 ...

  4. UEditor使用有感(黄色)

    UEditor 介绍 UEditor 是由百度「FEX前端研发团队」开发的所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码. 1 入门部署 ...

  5. java的内省(introspector)

    package com.wzh.test.introspector; import java.beans.BeanInfo; import java.beans.IntrospectionExcept ...

  6. 8 个 Git 的小技巧

    git 已经成为了我日常必备工具之一,我总结我几乎每天使用的8个有用(且简洁)的git技巧.   使用-p选择性添加 当你想提交内容时,你可以通过使用 git commit -am 来选择所有文件或使 ...

  7. Nginx 配置指令location 匹配符优先级和安全问题【转】

    Nginx配置指令location匹配符优先级和安全问题 使用nginx 很久了,它的性能高,稳定性表现也很好,得到了很多人的认可.特别是它的配置,有点像写程序一样,每行命令结尾一个";&q ...

  8. [ActionScript 3.0] as3.0加载as2.0的swf时获取as2.0的实际舞台尺寸

    var loader:Loader = new Loader(); loader.contentLoaderInfo.addEventListener(Event.INIT, initHandler) ...

  9. sikuli+java实例

      新建java工程,导入sikuli-script.jar包 public class TestSikuli { public static void openPage() throws FindF ...

  10. javascript自执行匿名函数

    1)自执行匿名函数: 常见格式:(function() { /* code */ })(); 2)作用:       function中的code代码在解释时就已经在运行了.因此可以用它创建命名空间, ...