javascript代码复用(四)-混入、借用方法和绑定
这篇继续说js的现代复用模式:混入、借用方法和绑定。
混入
可以针对前面提到的通过属性复制实现代码复用的想法进行一个扩展,就是混入(mix-in)。混入并不是复制一个完整的对象,而是从多个对象中复制出任意的成员并将这些成员组合成一个新的对象。
混入的实现并不难,只需要遍历每个参数,并且复制出传递给这个函数的每个对象中的每个属性。
function mix(){
var arg,prop,child={};
for(arg=0;arg<arguments.length;arg++){
for(prop in arguments[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--
javascript代码复用(四)-混入、借用方法和绑定的更多相关文章
- 编写高质量JavaScript代码的68个有效方法
简介: <Effective JavaScript:编写高质量JavaScript代码的68个有效方法>共分为7章,分别涵盖JavaScript的不同主题.第1章主要讲述最基本的主题,如版 ...
- javascript代码复用模式
代码复用有一个著名的原则,是GoF提出的:优先使用对象组合,而不是类继承.在javascript中,并没有类的概念,所以代码的复用,也并不局限于类式继承.javascript中创建对象的方法很多,有构 ...
- javascript代码复用模式(二)
前面说到,javascript的代码复用模式,可分为类式继承和非类式继承(现代继承).这篇就继续类式继承. 类式继承模式-借用构造函数 使用借用构造函数的方法,可以从子构造函数得到父构造函数传任意数量 ...
- javascript代码复用模式(三)
前面谈到了javascript的类式继承.这篇继续部分类式继承,及一些现代继承. 类式继承模式-代理构造函数 这种模式通过断开父对象与子对象之间原型之间的直接链接关系,来解决上次说到的共享一个原型所带 ...
- javascript代码复用--继承
由于javascript没有类的概念,因此无法通过接口继承,只能通过实现继承.实现继承是继承实际的方法,javascript中主要是依靠原型链要实现. 原型链继承 原型链继承是基本的继承模式,其本质是 ...
- javascript第十四课,方法的扩展prototype
所谓扩展方法就是,在原函数的基础上我们往对象里面添加一些自己需要的方法,例如: string对象 string.prototype.checkEmail=function(){ //方法体 //在这里 ...
- 【JavaScript代码实现四】获取和设置 cookie
// 创建cookie function setCookie(name, value, expires, path, domain, secure) { var cookieText = encode ...
- 《JavaScript 模式》读书笔记(6)— 代码复用模式3
我们之前聊了聊基本的继承的概念,也聊了很多在JavaScript中模拟类的方法.这篇文章,我们主要来学习一下现代继承的一些方法. 九.原型继承 下面我们开始讨论一种称之为原型继承(prototype ...
- javaScript DOM编程经常使用的方法与属性
DOM是Document Object Model文档对象模型的缩写.依据W3C DOM规范,DOM是一种与浏览器,平台,语言无关的接口,使得你能够訪问页面其它的标准组件. Node接口的特性和方法 ...
随机推荐
- 黄聪:wordpress中PHP运行错最有效解决办法Fatal error: Out of memory (allocated 6029312)(转)
近日在升级wordpress 3.2.1和若干插件的过程中,发现了一个wordpress的错误:Allowed memory size of XXX bytes exhausted Fatal err ...
- 图片_ _优化Bitmap加载图片1
=========== 1 视图显示大量图片时的内存问题 setBackgroundResource 回去res 资源文件里面找适配手机当前屏幕的文件,所以消耗高,etBackgroundDra ...
- (转)ZooKeeper伪分布式集群安装及使用
转自:http://blog.fens.me/hadoop-zookeeper-intro/ 前言 ZooKeeper是Hadoop家族的一款高性能的分布式协作的产品.在单机中,系统协作大都是进程级的 ...
- dom4j-full.jar 解析 XML
dom4j-full.jar 解析 XML public Document getDocument() throws DocumentException { SAXReader read=new SA ...
- Xcode5.0使用iOS6.1SDK及模拟器
最近项目中,介于测试人员提出的问题,有些情况只在ios6的设备上才能显现,而本机的xcode已升级到最新的5.0,这可如何是好呢,在网上搜索了一番,找到如下方法解决此问题: 1.打开xcode5.0的 ...
- JVM加载class文件的原理机制
Java中的所有类,都需要由类加载器装载到JVM中才能运行.类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中.在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的 ...
- SQLServer 2008以上误操作数据库恢复方法——日志尾部备份(转)
问题: 经常看到有人误删数据,或者误操作,特别是update和delete的时候没有加where,然后就喊爹喊娘了.人非圣贤孰能无过,做错可以理解,但不能纵容,这个以后再说,现在先来解决问题. 遇到这 ...
- java学习___File类的查看和删除
一.查看目录下的子目录或文件 getName()获取文件或目录的名字 for(File file:files) 如果想看目录下的另外目录,引用递归调用,就是还要在获取的目录下再查看目录 二.如何删除一 ...
- mybatis模板
因为这里是说mybatis的,所以呢 servlet就不做多说了,代码也不在这里贴出来了. log4j.properties log4j.rootLogger=DEBUG,Console log4j. ...
- [kuangbin带你飞]专题十一 网络流
ID Origin Title 34 / 81 Problem A POJ 3436 ACM Computer Factory 92 / 195 Problem B POJ 3 ...