javascript代码复用模式(三)

前面谈到了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()可以实现深复制。

时间: 2024-07-30 10:09:02

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

javascript代码复用模式(二)

前面说到,javascript的代码复用模式,可分为类式继承和非类式继承(现代继承).这篇就继续类式继承. 类式继承模式-借用构造函数 使用借用构造函数的方法,可以从子构造函数得到父构造函数传任意数量的参数.这个模式借用了父构造函数,它传递子对象以绑定到this,并转发任意数量的参数: function Child(a,b,c,d){ Parent.apply(this,arguments); } 在这种方式中,只能继承在父构造函数中添加到this的属性,并不能继承添加到原型中的成员. 使用借用

javascript代码复用模式

原文链接:http://www.faceye.net/search/143351.html 代码复用有一个著名的原则,是GoF提出的:优先使用对象组合,而不是类继承.在中,并没有类的概念,所以代码的复用,也并不局限于类式继承.javascript中创建对象的方法很多,有构造函数,可以使用new创建对象,并且可以动态的修改对象.javascript的非类式继承(可称为现代继承模式)复用方法也很多,例如,利用其它对象组合成所需要的对象,对象混入技术,借用和复用所需要的方法. 类式继承模式-默认模式

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

介绍 本文介绍的四种代码复用模式都是最佳实践,推荐大家在编程的过程中使用. 模式1:原型继承 原型继承是让父对象作为子对象的原型,从而达到继承的目的: function object(o) { function F() { } F.prototype = o; return new F(); } // 要继承的父对象 var parent = { name: "Papa" }; // 新对象 var child = object(parent); // 测试 console.log(c

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

介绍 任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免使用这些模式,因为或多或少有带来一些问题:第二排是推荐篇,指的是推荐大家使用的模式,一般不会有什么问题. 模式1:默认模式 代码复用大家常用的默认模式,往往是有问题的,该模式使用Parent()的构造函数创建一个对象,并且将该对象赋值给Child()的原型.我们看一下代码: function i

JavaScript模式读书笔记 第6章 代码复用模式

主要使用代码继承来完成复用. 1,使用类式继承. -1,类式继承:按照类的方式来实现继承,即所谓的类式. -2,类式继承:通过构造函数(child)获取来自parent的属性,从而创建对象. <script> //parent function Parent(name){ this.name = name || 'Adam'; } Parent.prototype.say = function(){   return this.name; } //child function Child(na

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

这篇继续说js的现代复用模式:混入.借用方法和绑定. 混入 可以针对前面提到的通过属性复制实现代码复用的想法进行一个扩展,就是混入(mix-in).混入并不是复制一个完整的对象,而是从多个对象中复制出任意的成员并将这些成员组合成一个新的对象. 混入的实现并不难,只需要遍历每个参数,并且复制出传递给这个函数的每个对象中的每个属性. function mix(){ var arg,prop,child={}; for(arg=0:arg<arguments.length;arg++){ for(ar

Atitit 代码复用的理解attilax总结

1.1. 继承1 1.1.1. 模式1:原型继承1 1.1.2. 模式2:复制所有属性进行继承 拷贝继承1 1.1.3. 模式3:混合(mix-in)1 1.1.4. 模式4:借用方法1 1.2. 组合2 1.3. API提供了一种代码复用的机制.2 1.4. 类库机制2 1.5. Include import2 1.6. Cfg2 1.7. 良好的复用和不良的复用2 1.8. 可复用组件的 5 种类型  代码块 Code Block算法 模式   数据类型 库3 1.9. 2. 算法 Algo

javascript-代码复用模式

代码复用模式 1)使用原型继承 函数对象中自身声明的方法和属性与prototype声名的对象有什么不同: 自身声明的方法和属性是静态的, 也就是说你在声明后,试图再去增加新的方法或者修改已有的方法, 并不会由其创建的对象产生影响,也即继承失败. 而prototype可以动态地增加新的方法或者修改已有的方法, 从而是动态的,一旦父函数对象声明了相关的prototype属性,由其创建的对象会自动继承这些prototype的属性. 既然有函数对象本身的属性, 也有prototype的属性, 那么是由其

【转】使用Jasob混淆javascript代码

在平常的web开发中,我们时常需要写一些js的类库,当我们发布自己产品的时候,不得不把源代码分发出去:但是这样就会泄露自己的代码.今天使用了一下Jasob感觉不错: 使用Jasob,我们的JavaScript代码无法被任何人理解从而杜绝剽窃和泄密,别人再也不能在我们的代码上做点修改就成了他的作品了.使用Jasob混淆以后,JavaScript的代码大小将缩小为原来的30%,因此浏览器将用更短的时间下载并执行网页代码. Jasob分析JavaScript的代码并替换掉描述性的变量和函数名称,例如客