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

这篇继续说js的现代复用模式:混入、借用方法和绑定。

混入

可以针对前面提到的通过属性复制实现代码复用的想法进行一个扩展,就是混入(mix-in)。混入并不是复制一个完整的对象,而是从多个对象中复制出任意的成员并将这些成员组合成一个新的对象。

混入的实现并不难,只需要遍历每个参数,并且复制出传递给这个函数的每个对象中的每个属性。

function mix(){
    var arg,prop,child={};
    for(arg=0:arg<arguments.length;arg++){
        for(arguments in arg){
            if(arguments(arg).hasOwnProperty(prop)){
                child[prop] = arguments[arg][prop];
            }
        }
    }
    return child;
}

现在,有了一个通用的mix-in函数,可以向它传递任意数量的对象,其结果将获得一个具有所有源对象属性的新对象,一个调用的例子:

var cake = mix(
    {eggs : 2,large : true},
    {butter : 1,sorted : true},
    {flour : "3 cups"},
    {suger : "sure!"}
);
console.dir(cake);

下面是控制台的输出:

      butter          1
     eggs            2
     flour           "3 cups"
     large           true
     sorted          true
     sugar           "sure!"

借用方法

有时可能恰好仅需要现有对象其中的一个或两个方法,在想要重用方法的同时,又不希望和源对象是父子的继承关系,也就是只想使用所需要的方法,而不需要那些永远用不到的其他方法。这种情况下,可以使用借用方法(borrowing method)来实现,即使用call()和apply(),区别就是传参的区别。

下面是一个例子,借用了数组的方法:

function f(){
    var args = [].slice.call(arguments,1,3);
    return args;
}

f(1,2,3,4,5,6);//[2,3]

其中创建空数组是为了使用数组的slice方法,也可以从Array的原型中借用方法,即Array.prototype.slice.call,这个需要输更长的字符,但是可以节省创建一个空数组的工作。
借用方法,不是通过call()和apply()就是通过简单的赋值,在借用方法的内部,this所指向的对象是基于调用表达式而确定的,但更多时候,最好可以锁定this的值,或者把它绑定到特定对象并预先确定该对象。

参考下面的例子,one对象有一个say()的方法:

var one = {
    name : "object",
    say : function(greet){
        return greet+","+this.name;
    }
};

one.say("hi");//"hi,object"

另一个对象two中没有say方法,但是可以从one那里借用:

var two = {
    name : "another object"
};
one.say.apply(two,["hello"]);//"hello,another object"

上面借用的say()方法中的this指向了two,所以this.name是"another object".但是在什么场景中,应该给函数指针赋值一个全局变量,或者将函数作为回调函数传递?在程序中有这样的应用,并且出现了问题。

var say = one.say;
say("hoho");//"hoho,undefined"

var yetanother = {
    name : "Yet another object",
    method : function(callback){
       return callback("Hola");
    }
};
yetanother.method(one.say);//"Hola,undefined"

在上面两种情况下this都指向了全局对象,并且代码都没有按预期运行。为了绑定对象与方法之间的关系,可以用下面的一个简单的函数:

function bind(o,m){
    return function(){
        return m.apply(o,arguments);
    };
}

bind()接受了一个对象o和一个方法m,并将它们绑定起来,然后返回另一个函数。返回的函数可以通过闭包来访问o和m。所以在bind()返回后仍然可以访问o和m.可以使用bind()创建一个新函数:

var twosay = bind(two,one.say);
twosay("yo");//"yo,another object"

无论怎么调用twosay(),这个方法总是绑定到对象two上。

ES5中的bind()

ES5将bind()添加到Function.prototype,使得bind()像call()apply()一样易用。可以执行下面的表达式:

var newFunc = obj.someFunc.bind(myobj,1,2,3);

就是将someFunc()与myobj绑定到一起,并填充someFunc()的前3个参数。
在不支持ES5的环境下面运行的时候,看看怎么实现Function.prototype.bind():

if (typeof Function.prototype.bind === "undefined"){
    Function.prototype.bind = function(thisArg){
        var fn = this,
             slice = Array.prototype.slice,
             args = slice.call(arguments,1);

        return function(){
            return fn.apply(thisArg,args.concat(slice.call(arguments)));
        };
    };
}

它拼接了参数列表,即传给bind()的参数(第一个除外),以及那些传给由bind()返回新函数的参数,新函数将在后面调用。一个调用例子:

var twosay2 = one.say.bind(two);
twosay2("Bonjour");//"Bonjour,another object"

也可以传递一个参数:

var twosay3 = one.say.bind(two,"Nihao");
twosay3();//"Nihao,another object"

小结

在javascript中可能并不会像C#或Java一样经常面临继承的问题,一些原因是js库用一些方法解决了这个问题,另一些原因是在js中很少需要建立长而且复杂的继承链。在静态强类型语言中,继承可能是唯一复用代码的方法,但在js中经常有更简洁并且优雅的方法,包括借用方法,绑定,复制属性,及从多个对象中混入属性等方法。毕竟,代码重用才是最终目的,继承只是实现这个目标的方法之一。

--end--

时间: 2024-10-11 10:39:07

javascript代码复用(四)-混入、借用方法和绑定的相关文章

编写高质量JavaScript代码的68个有效方法

简介: <Effective JavaScript:编写高质量JavaScript代码的68个有效方法>共分为7章,分别涵盖JavaScript的不同主题.第1章主要讲述最基本的主题,如版本.类型转换要点.运算符注意事项和分号局限等.第2章主要讲解变量作用域,介绍此方面的一些基本概念,以及一些最佳实践经验.第3章主要讲解函数的使用,深刻解析函数.方法和类,并教会读者在不同的环境下高效使用函数.第4章主要讲解原型和对象,分析JavaScript的继承机制以及原型和对象使用的最佳实践和原则.第5章

javascript代码复用模式

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

javascript代码复用模式(二)

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

Effective JavaScript: 编写高质量JavaScript代码的68个有效方法(目录)

本书赞誉译者序序前言第 1 章 让自己习惯 JavaScript 1第 1 条: 了解你使用的 JavaScript版本 1第 2 条:理解 JavaScript 的浮点数 6第 3 条:当心隐式的强制转换 8第 4 条:原始类型优于封装对象 13第 5 条: 避免对混合类型使用== 运算符 14第 6 条:了解分号插入的局限 16第 7 条: 视字符串为 16 位的代码单元序列 21第 2 章 变量作用域 25第 8 条:尽量少用全局对象 25第 9 条:始终声明局部变量 27第 10 条:避

《编写高质量JavaScript代码的68个有效方法》

第1章 让自己习惯JavaScript 第1条:了解你使用的JavaScript版本 决定你的应用程序支持JavaScript的哪些版本. 确保你使用的JavaScript的特性对于应用程序将要运行的所有环境都是支持的. 第2条:理解JavaScript的浮点数 JavaScript中的数字都是作为双精度的64位浮点数来储存的:而JavaScript的整数都仅仅是双精度浮点数的一个子集,不是单独的数据类型.而小数和小数相加有时候是不精确的,如果你用作货币计算,最好换算成最小的货币来计算. 第3条

javascript代码复用模式(三)

前面谈到了javascript的类式继承.这篇继续部分类式继承,及一些现代继承. 类式继承模式-代理构造函数 这种模式通过断开父对象与子对象之间原型之间的直接链接关系,来解决上次说到的共享一个原型所带来的问题,而且同时可以继续原型链带来的好处. 代码: function inherit(C,P){ var F = function(){}; F.prototype = P.prototype; C.prototype = new F(); } 可以看到,在这里面有一个空构造函数F(),充当了子对

常见的JavaScript代码(四)

文章来自"PHP100中文网" 18.改变按钮的图片. <style type="text/css"> <!-- .style3 { font-size: 12px; background: url(image/buttonbg1.gif); border: 0px; width: 60px; height: 22px} .style4 { font-size: 12px; font-weight: bold; background: url(im

javaScript DOM编程经常使用的方法与属性

DOM是Document Object Model文档对象模型的缩写.依据W3C DOM规范,DOM是一种与浏览器,平台,语言无关的接口,使得你能够訪问页面其它的标准组件. Node接口的特性和方法 特性/方法 类型/放回类型 说明 nodeName String 节点的名字:依据节点的类型而定义 nodeValue String 节点的值.依据节点的类型而定义 nodeType Number 节点的类型常量值之中的一个 ownerDocument Document 指向这个节点所属的文档 fi

深入理解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