Javascript 接口;设计模式的经典语录“针对接口而不是类编程”
Javascript是种弱类型语言,类型不匹配错误很难跟踪,使用接口可以让这种错误的查找变得更容易点;接口还可以让逻辑代码变得更稳固,因为接口添加一个操作,类必须实现它。
Javascript接口缺点:在某种程序上增加额外方法调用的开销(可以在开发完成后剔除接口代码);Javascript接口必须用手工的办法保证某个类实现某个接口,编码规范和辅助类可以提供一些帮助,但无法彻底根除这个问题,除非项目组的人员都同意并且强制使用接口并对其检查,否则接口很多价值都无法体现。
1、 在Javascript中模仿接口。使用注释来模仿其它语言的接口做法。代码如下:
/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem{
function save();
}
*/
上面的一段注释即为模仿接口,只是人为按照这种方式来实现这种接口。程序并不会真正继承,程序也不会检查错误。实现接口代码如下:
var CompositeForm=function(id,method,action){ //implements Composite,FormItem
…
};
//实现Composite接口
CompositeForm.prototype.add=function(child){
…
};
CompositeForm.prototype.remove=function(child){
…
};
CompositeForm.prototype.getChild=function(index){
…
};
//实现FormItem接口
CompositeForm.prototype.save=function(){
…
};
尽管以上如此,只是人为的模仿,但也有其优点。不需要额外的类或函数。可以提高代码的可重用性,因为现在那些类实现的接口都有说明。缺点就是不会提供错误消息,对测试和调试没有帮助。
2、 用属性检查模仿接口。
这一种方法要更严谨一点。但仍然只是注释,但现在可以通过检查一个属性得知某个类自称实现了什么接口。代码如下:
/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem{
function save();
}
*/
var CompositeForm=function(id,method,action){ //implements Composite,FormItem
this.implementsInterfaces=[‘Composite’,’FormItem’];
…
};
//实现Composite接口
CompositeForm.prototype.add=function(child){
…
};
CompositeForm.prototype.remove=function(child){
…
};
CompositeForm.prototype.getChild=function(index){
…
};
//实现FormItem接口
CompositeForm.prototype.save=function(){
…
};
function addForm(formInstance){
If(!implements(formInstance,’Composite’,’FormItem’)){
Throw new Error(“Object does not implement a require interface”);
}
}
function implements(object){
for(var i=1;i<arguments.length;i++){
var interfaceName=arguments[i];
var interfaceFound=false;
for(var j=0;j<object.implementsInterfaces.length;j++){
if(object.implementsInterfaces[j]==interfaceName){
interfaceFound=true;
break;
}
}
if(!interfaceFound){
return false;
}
}
Return true;
}
以上这种方法只是模仿接口的改进。可以检查是否实现某个接口,但并不真正的实现。
3、 用鸭式辨型模仿接口。
其实,类是否声明自己支持那些接口并不重要,只要它具有这些接口中的方法就行。把对象实现的方法集作为判断它是不是某个类的实例的唯一标准。这种技术在检查一个类是否实现了某个接口时也大显身手。这就是鸭式辨型模仿接口。可以用一个辅助函数来确保对象具有所有必需的方法。代码如下:
//接口.
var Composite=new Interface(‘Composite’,[‘add’,’remove’,’getChild’]);
var FormItem=new Interface(‘FormItem’,[‘save’]);
//实现类
var CompositeForm=function(id,method,action){ …
};
……
Function addForm(formInstance){
ensureImplements(formInstance,Composite,FormItem);
//如果没有实现某个方法这个函数将会抛出一个错误
}
ensureImplements函数至少需要两个参数,每一个是想要检查的对象,其余参数是据以对那个对象进行检查的接口。这种方法需要一个辅助类Interface和一个辅助函数ensureImplements。而且它只关心方法的名称,并不检查其参数的名称、数目或类型。些方式是上述三种方法中最有用的一种。
在现实中,我们可以综合使用第一和第三种方法。即可以提高代码的可重用性及其文档的完善,又可以用辅助类和方法来对对象实现的方法进行显式检查。
Interface 定义代码如下:
var Interface = function (name, methods) {
if (arguments.length != 2) {
throw new Error("the interface length is bigger than 2");
}
this.Name = name;
this.Method = [];
for (var i = 0; i < methods.length; i++) {
if(typeof methods[i]!== string) {
throw new Error("the method name is not string");
}
this.Method.push(methods[i]);
}
}
/* ensureImplement 方法实现*/
Interface.ensureImplement = function (object) {
if (arguments.length < 2) {
throw new Error("there is not Interface or the instance");
}
for (var i = 1; i < arguments.length; i++) {
var interface1 = arguments[i];
if (interface1.constructor !== Interface) {
throw new Error("the argument is not interface");
}
for (var j = 0; j < interface1.Method.length; j++) {
var method = interface1.Method[j];
if (!object[method] || typeof object[method] !== function) {
throw new Error("you instance doesnt implement the interface");
}
}
}
}
使用示例:
var DynamicMap=new Interface(‘DynamicMap’,[‘centerOnPoint’,’zoom’,’draw’]);
function displayRoute(mapInstance){
Interface.ensureImplements(mapInstance,DynamicMap);
mapInstance.centerOnPoint(12,34);
mapInstance.zoom(5);
mapInstance.draw();
…
}
下面这段代码简要的说明了其使用:
var Person = new Interface("Person", ["GetName", "GetAge"]);
var Man = function (name, age) {
this.Name = name;
this.
本系列文章列表和翻译进度,请移步:Node.js高级编程:用Javascript构建可伸缩应用(〇)
本文对应原文第二部分第六章:Node Core API Basics:Scheduling the Execution of Functions Using Timers
文章从Word复制到这里,排版与原文不太一致,可以点这里下载本文的PDF版。
第六章:使用计时器制定函数的执行计划本章内容:
- 函数的延迟执行
- 取消执行计划
- 制定函数的周期性执行计划
- 将函数执行延迟到事件循环的下一轮
如果你熟悉客户端JavaScript编程,你可能使用过setTimeout和setInterval函数,这两个函数允许延时一段时间再运行函数。比如下面的代码, 一旦被加载到Web页面,1秒后会在页面文档后追加“Hello there”:
var oneSecond = 1000 * 1; // one second = 1000 x 1 ms
setTimeout(function() {
document.write('<p>Hello there.</p>');
}, oneSecond);
而setInterval允许以指定的时间间隔重复执行函数。如果把下面的代码注入到Web页面,会导致每秒钟向页面文档后面追加一句“Hello there”:
var oneSecond = 1000 * 1; // one second = 1000 x 1 ms
setInterval(function() {
document.write('<p>Hello there.</p>');
}, oneSecond);
因为Web早已成为一个用来构建应用程序的平台,而不再是简单的静态页面,所以这种类似的需求日益浮现。这些任务计划函数帮助开发人员实现表单定期验证,延迟远程数据同步,或者那些需要延时反应的UI交互。Node也完整实现了这些方法。在服务器端,你可以用它们来重复或延迟执行很多任务,比如缓存过期,连接池清理,会话过期,轮询等等。
使用setTimeout延迟函数执行
setTimeout可以制定一个在将来某个时间把指定函数运行一次的执行计划,比如:
var timeout_ms = 2000; // 2 seconds
var timeout = setTimeout(function() {
console.log("timed out!");
}, timeout_ms);
和客户端JavaScript完全一样,setTimeout接受两个参数,第一个参数是需要被延迟的函数,第二个参数是延迟时间(以毫秒为单位)。
setTimeout返回一个超时句柄,它是个内部对象,可以用它作为参数调用clearTimeout来取消计时器,除此之外这个句柄没有任何作用。
使用clearTimeout取消执行计划
一旦获得了超时句柄,就可以用clearTimeout来取消函数执行计划,像这样:
var timeoutTime = 1000; // one second
var timeout = setTimeout(function() {
console.log("timed out!");
}, timeoutTime);
clearTimeout(timeout);
这个例子里,计时器永远不会被触发,也不会输出”time out!”这几个字。你也可以在将来的任何时间取消执行计划,就像下面的例子:
var timeout = setTimeout(function A() {
console.log("timed out!");
}, 2000);
setTimeout(function B() {
clearTimeout(timeout);
}, 1000);
代码指定了两个延时执行的函数A和B,函数A计划在2秒钟后执行,B计划在1秒钟后执行,因为函数B先执行,而它取消了A的执行计划,因此A永远不会运行。
制定和取消函数的重复执行计划
setInterval和setTimeout类似,但是它会以指定时间为间隔重复执行一个函数。你可以用它来周期性的触发一段程序,来完成一些类似清理,收集,日志,获取数据,轮询等其它需要重复执行的任务。
下面代码每秒会向控制台输出一句“tick”:
var period = 1000; // 1 second
setInterval(function() {
console.log("tick");
}, period);
如果你不想让它永远运行下去,可以用clearInterval()取消定时器。
setInterval返回一个执行计划句柄,可以把它用作clearInterval的参数来取消执行计划:
var interval = setInterval(function() {
console.log("tick");
}, 1000);
// …
clearInterval(interval);
使用process.nextTick将函数执行延迟到事件循环的下一轮
有时候客户端JavaScript程序员用setTimeout(callback,0)将任务延迟一段很短的时间,第二个参数是0毫秒,它告诉JavaScript运行时,当所有挂起的事件处理完毕后立刻执行这个回调函数。有时候这种技术被用来延迟执行一些并不需要被立刻执行的操作。比如,有时候需要在用户事件处理完毕后再开始播放动画或者做一些其它的计算。
Node中,就像 “事件循环”的字面意思,事件循环运行在一个处理事件队列的循环里,事件循环工作过程中的每一轮就称为
用nvm管理nodejs
公司开发机上用的node.js版本还是 v0.4.9,想升级到最新,于是用了nvm(https://github.com/creationix/nvm)。
按照官网说法,安装nvm,得先有git,自己用的是centos5.4时还没有,所以先在centos上安装git。由于centos上默认yum源是没有git的,所以想通过yum install简单安装的希望破灭。Google一把,找到了安装的命令:
wget http://git-core.googlecode.com/files/git-1.7.7.5.tar.gz
./configure –prefix=/usr/local/git
make
make install
可是又出现yum源错误,mirror里找不到 (没有问题的可以略过) 见最后解决方法
配好yum后,就可以再安装git所依赖的库了
然后再安装好git后,再安装nvm就很快了,可直接执行官网上提供的命令:
wget -qO- https://raw.github.com/appleboy/nvm/develop/install.sh | sh
完后,运行nvm, 提示nvm command not found,再次google和官网上搜,以及自己破天荒实验所得,得把. ~/.nvm/nvm.sh 加到 ~/.bashrc 下, 在 ~/.profile下要加上
mesg n
[[ -s /home/frontend/.nvm/nvm.sh ]] && . /home/frontend/.nvm/nvm.sh # This loads NVM
注:/home/frontend/ 为用户目录
之后运行nvm,就会出来
可以按照提示输入命令,比如,我想用最新的基础稳定版,则直接执行 nvm install stable,查看已安装的node版本,输入 nvm ls,非常方便。看下我目前装了两个版本:
保留了以前的v0.4.9,以免新版本出现问题,可以采用 npm use ,在版本间来回自由切换。为了保证每次打开新的shell运行窗口时,系统默认采用的node版本,可以使用nvm alias default 0.4 命令。
另: 我在centos上运行node的v0.8.17时,出现以下提示
这是由于CentOS 5自带的glibc库版本过低,我们无法用直接使用新版的Node
运行nodejs 0.8.17报错,具体可以看这里解决: (http://www.noanylove.com/2012/11/centos-5-install-node-js/)
第二步用yum安装git所需的包,出现yum源安装错误的解决方案:
file://media/CentOS_5.2_Final/repodata/repomd.xml:[Error 5] OSError:[Errno 2]
Trying other mirror.
Error: Cannot retrieve repository metadata(repomd.xml) for repository: c5-media.
Please verify its path and try again
继续google之,只能重新配理yum,修改/etc/yum.repos.d/CentOS-Base.repo:
内容为[base]
name=CentOS-5 - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever5&arch=$basearch&
repo=os
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
baseurl=http://ftp.sjtu.edu.cn/centos/5.3/os/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5
#released updates
[update]
name=CentOS-5 - Updates
#mirrorlist=http://mirrorlist.centos.org/?release=4&arch=$basearch&repo=updates
baseurl=http://ftp.sjtu.edu.cn/centos/5.3/updates/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5
#packages used/produced in the build but not released
[addons]
name=CentOS-5 - Addons
#mirrorlist=http://mirrorlist.centos.org/?release=4&arch=$basearch&repo=addons
baseurl=http://ftp.sjtu.edu.cn/centos/5.3/addons/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5
#additional packages that may be useful
[extras]
name=CentOS-5 - Extras
#mirrorlist=http://mirrorlist.centos.org/?release=4&arch=$basearch&repo=extras
baseurl=http://ftp.sjtu.edu.cn/centos/5.3/extras/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5
#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-5 - Plus
#mirrorlist=http://mirrorlist.centos.org/?release=4&arch=$basearch&repo=centosplus
baseurl=http://ftp.sjtu.edu.cn/centos/5.3/centosplus/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5
#contrib - packages by Centos Users
[contrib]
name=CentOS-5 - Contrib
#mirrorlist=http://mirrorlist.centos.org/?release=4&arch=$basearch&repo=contrib
baseurl=http://ftp.sjtu.edu.cn/centos/5.3/contrib/$basearch/
gpgcheck=1
enabled=0
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos5
话不择多,thanks all!!!
http://www.cnblogs.com/caozf/
新浪微博:http://weibo.com/caozf
本文链接