前面说到,javascript的代码复用模式,可分为类式继承和非类式继承(现代继承)。这篇就继续类式继承。

类式继承模式-借用构造函数

使用借用构造函数的方法,可以从子构造函数得到父构造函数传任意数量的参数。这个模式借用了父构造函数,它传递子对象以绑定到this,并转发任意数量的参数:

function Child(a,b,c,d){
Parent.apply(this,arguments);
}

在这种方式中,只能继承在父构造函数中添加到this的属性,并不能继承添加到原型中的成员。

使用借用函数构造模式的时候,子对象获得了继承成员的副本,这个与默认原型继承中的仅获取引用的方式是不同的。可以参考下面的例子:

funciton Article(){
this.tags = ['js','css'];
}
var article = new Article();
function BlogPost(){
}
BlogPost.prototype = article;
var blog = new BlogPost(); function StaticPage(){
Article.call(this);
}
var page = new StaticPage(); console.log(article.hasOwnProperty('tags'));//true
console.log(blog.hasOwnProperty('tags'));//false
console,log(page.hasOwnProperty('tags'));//true

上面用两种方式继承了Article(),默认原型模式导致blog对象通过原型来获得他对tags属性的访问,所以blog对象没有将article作为自身的属性,所以当调用hasOwnProperty()时会返回false.而page对象本身具有一个tags属性,是因为他在调用父构造函数时,新对象会获得父对象中tags成员的副本,而不是引用。
可以通过下面的代码看到这个差异:

blog.tags.push('html');
page.tag.push('php');
console.log(article.tags.join(','));//"js,css,html"

上面代码中,子对象blog修改了其tags属性,而这种方式也会修改父对象article,因为本质上blog.tags和article.tags指向了同一个数组,但修改page的tags不会影响父对象article,是由于在继承过程中page.tags是独立创建的一个副本。
关于原型链的工作流程:

function Parent(name){
this.name = name||"Adam";
}
Parent.prototype.say = {
return this.name;
};
function Child(name){
Parent.apply(this,arguments);
}
var kid = new Child("Patrick");
console.log(kid.name);//"Patric"
console.log(typeof kid.say);//undefined

上面代码里面没有使用Child.prototype,它只是指向一个空对象,借用父构造函数时,kid获得了自身属性name,没有继承过say()方法。继承是一次性完成的,仅会复制父对象的属性并将其作为自身的属性,所以也不会保留_proto_链接。

使用借用构造函数时,可以通过借用多个构造函数实现多重继承:

function Cat(){
this.legs = 4;
this.say = function(){
return "meaowww";
};
}
function Bird(){
this.wings = 2;
this.fly = true;
}
function CatWings(){
Cat.apply(this);
Bird.apply(this);
} var jane = new CatWings();
console.dir(jane);

运行结果:

    fly        true
legs 4
wings 2
say function()

借用构造函数的缺点,很明显,不能从原型中继承任何属性和方法,如上面Parent和Child的例子。对于父构造函数上使用this定义的方法也会创建多个副本。优点可以获得父对象自身成员的真实副本,不会存在子对象覆盖父对象的风险,可以传参数,可以多重继承。

类式继承模式-借用和设置原型

这个模式就是结合前面两种,先借用构造函数,也设置子构造函数的原型使其指向一个构造函数创建的实例。代码如下:

function Child(a,b,c,d){
Parent.apply(this,arguments);
}
Child.prototype = new Parent();

这样的优点,是代码运行后的结果对象能够获得父对象本身的成员副本以及指向父对象中可复用功能(以原型方式实现的功能),同时,子对象也可以将任意参数传递到父构造函数中,
这个是最接近Java或者C#的实现方式。可以继承父对象的一切,同时也可以安全的修改自身的属性,不会带来修改父对象的风险。

这样的缺点,是父构造函数被调用了两次,这会导致效率低下,自身的属性会被继承两次,如下面的name:

function Parent(name){
this.name = name||"Adam";
}
Parent.prototype.say = {
return this.name;
};
function Child(name){
Parent.apply(this,arguments);
}
Child.prototype = new Parent();
var kid = new Child("Patrick");
kid.name;//"Patrick"
kid.say();//"Patrick"
delete kid.name;
kid.say();//"Adam"

上面代码中。say()被继承了。可以看到,name属性被继承了两次,在删除了kid本身的name属性的副本之后,可以看到输出的是原型链所引出的name.
原型链示意图:

类式继承模式-共享原型

和前面的模式需要调用两次父构造函数不同,下面的模式不涉及调用父构造函数。

这个模式的法则在于,可复用成员应该转移到原型中而不是放在this中,所以,出于继承的目的,任何需要继承的属性和方法都应该放在原型中,所以可以仅将子对象的原型和父对象的设置成相同。代码如下:

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

这种模式可以提供简短而迅速的原型链查询,这是因为所有的对象实际上共享了一个原型。但这也同时是一个缺点,因为如果在继承链下方的某处存在的一个子对象中修改了原型,他会影响到所有父对象和祖先对象。
如下图,下面的子对象和父对象共享了同一个原型,并且可以同等访问say()方法,但子对象没有继承name属性。

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

  1. javascript代码复用模式

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

  2. javascript代码复用模式(三)

    前面谈到了javascript的类式继承.这篇继续部分类式继承,及一些现代继承. 类式继承模式-代理构造函数 这种模式通过断开父对象与子对象之间原型之间的直接链接关系,来解决上次说到的共享一个原型所带 ...

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

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

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

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

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

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

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

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

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

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

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

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

  9. javascript代码复用--继承

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

随机推荐

  1. (C# 基础) Datatable

    增加一行: DataTable measurements = new DataTable(); measurements.Columns.Add("StationTestName" ...

  2. mysql5.6优化建议

    这篇文章主要介绍了MySQL5.6基本优化配置,详细分解了MySQL5.6需要优化的配置项,最终给出了一个优化案例,需要的朋友可以参考下     随着 大量默认选项的改进, MySQL 5.6比以前版 ...

  3. linuc c 代码示例

    fork的应用: #include "stdio.h" #include "string.h" #include <sys/types.h> #in ...

  4. 很好用的在线markdown编辑器

    # 欢迎使用 Cmd Markdown 编辑阅读器 基本符号 *,-,+ 3个符号效果都一样,这3个符号被称为 Markdown符号 空白行表示另起一个段落 `是表示inline代码,tab是用来标记 ...

  5. Linux自启动

    linux 下tomcat开机自启动修改Tomcat/bin/startup.sh 为:export JAVA_HOME=/usr/java/j2sdk1.4.2_08export CLASSPATH ...

  6. [Flex] ButtonBar系列——如何给ButtonBar添加一个ViewStack

    <?xml version="1.0" encoding="utf-8"?> <!--如何给ButtonBar添加一个ViewStack--& ...

  7. Intellisense in Visual Studio for Microsoft Dynamics CRM 2016

    Intellisense in Visual Studio for Microsoft Dynamics CRM 2016 posted by dynamicsnick on may 18, 2016 ...

  8. 类似 go get –u 的命令行参数实现

    我们可能需要类似 go get –u -. 这样的方式来实现我们的应用,这时候我们无法简单地使用 flag.Parse 了,而是要用 FlagSet 了, 使用例子如下:   package main ...

  9. Hibernate注解:一对多外键关联

    情形:两个表,cms_mode是主表,cms_model_field是子表,cms_model_field的model_id字段关联到cms_model的主键. # # Source for tabl ...

  10. Codeforces 450D Jzzhu and Cities [heap优化dij]

    #include<bits/stdc++.h> #define MAXN 100050 #define MAXM 900000 using namespace std; struct st ...