前言
Zeta.js 是一款为node打造的轻量级后端框架,引入了许多angular的概念,可以让你以一种不同于express的更有层次的方式编写后端代码。这里是Zeta的中文文档。
概览
从某种程度上来说,工厂完成了和Provider相似的工作,但区分于后者的是对于每个新请求工厂都会提供一个新对象而非共享同一个对象。另外,工厂也必须是一个一个返回对象的函数而非对象本身。在许多情形下,你可以任意选用这两种完成任务,但确实还有一些情况引入工厂会更方便合理。
开始
定义工厂
同Provider一样,工厂是由module注册的,例子如下。
js
demo.factory('$sayhi',function(){ return {content:'hi,world'}; }); //demo is a module
现在,你得到了一个名叫$sayhi的返回一个带有值为‘hi,world’的属性content的对象的工厂,字母“$” 并非必要的,但我们推荐你把它放在你所选定的工厂名字的开头以将工厂与普通的变量区分开来。
工厂能返回你要的任何东西,包括函数。
js
demo.factory('$plus',function(){ return function(a,b){ return a+b; }; });
并且你可以在工厂中使用$scope,只要把它加到函数参数列表里。关于$scope的详细事宜,你可以在接下来的章节里看到。
使用工厂
如同Provider,工厂也是被设计用在请求处理函数里的。
js
//$plus is defined in the example before demo.get('/',function($scope,$plus){ $scope.send($plus(1,2).toString()).end(); }); //the client will get '3'
我们所使用的$plus 函数并非所定义的工厂本身而是工厂所返回的对象,同时不要忘了将工厂名追加到请求处理函数的参数列表里。
取出工厂
就像Provider一样,已经定义过 的工厂也可以被取到。
js
var plus=demo.factory('$plus')(); console.log(plus(1,2)); //3 will be got var dus=demo.factory('$wel'); dus==undefined//true
简单的例子
下面是一个稍微复杂的例子,照例忽略您阅读到的不能理解的地方。
js
//main.js file var zeta=require('zeta'); var demo=zeta.module('demo',['demoFactory']); demo.load(); demo.get('/',function($scope,$plus){ $scope.send($plus(1,2).toString()); });
js
//Factory.js var zeta=require('zeta'); var demoFactory=zeta.module('demoFactory',[]); demoFactory.provider('$plus',function(){ return function(a,b){return a+b;}; });
工厂的继承也是由module的依赖注入实现的。
工厂进阶
工厂使用Provider
使用Provider毫无困难,将Provider的名字加到定义的工厂函数的参数列表里即可。
js
demo.provider('$wel',{ a:1, b:2, c:3 }); demo.factory('$sum',function($wel){ var sum=0; for(var i in $wel){sum+=$wel[i];} return sum; });
工厂使用工厂
同上面相似,一样用例子说明吧。
js
demo.factory('$wel',function(){ return { a:1, b:2 }; }); demo.factory('$sum',function($wel){ return function(){ var tmp=$wel(); return tmp.a+tmp.b; }; });
工厂的意义
既然已经有了Provider,你可能会问为什么还要引入工厂的概念。首先让我们考虑对于处理表单提交的两种实现方式。
js
//Use Provider var formidable=require('formidable'); demo.provider('$form',formidable); demo.post('/upload',function($scope,$form){ var tmp=new $form.IncomingForm(); tmp.on('file',function(file){...}); });
js
//Use Factory var formidable=require('formidable'); demo.factory('$form',function(){ return new formidable.IncomingForm(); }); demo.post('/upload',function($scope,$form){ $form.on('file',function(file){...}); });
通过上面的例子,你应该已经非常清楚两者之间的区别,而上文的服务并未对两种方式显示出偏爱,但考虑一下处理cookie的情形。如果我们要用一个对象表示每个请求附带的cookie,由于每次请求cookie都不一致,而把parse的过程留到handler里显然不那么美,有了工厂,这就不足为虑了。
js
demo.factory('$cookie',function($scope){ var cook={}; cook._val=parse($scope.req.headers['cookie']); cook._option={}; cook.getValue=function(name,optName){...}; cook.setCookie=function(obj){..}; ... });
可以看出来,对于cookie这类每次请求都需要一个全新的对象的情况,工厂是一个更明智的选择。
注意事项
- 工厂可以通过重定义覆盖。
js
demo.factory('$compute',function(){ return function(a,b){return a+b;}; }); demo.factory('$compute')()(1,2)==3//true demo.factory('$compute',function(){ return function(a,b){return b-a;}; }); demo.factory('$compute')()(1,2)==1//true
- 不要忘了在请求处理函数的参数列表加入工厂名。
- 工厂必须是一个函数,其参数可以包含其所依赖的。并且你在请求处理函数中用到的是工厂所返回的而非其本身。
- 同一个请求的多个处理函数共享同一个工厂返回的对象
js
demo.factory('$count',function(){ return {num:0}; }); demo.handler('h0',function($scope,$count){ $count.num++; $scope.go('next'); }); demo.handler('h1',function($scope,$count){ $scope.send($count.num.toString()).end(); }); demo.get('/',['h0','h1']); //the client will get 1 instead of 0
- 对于每个新到来的请求工厂返回新的对象
js
demo.factory('$count',function(){ return {num:0}; }); demo.get('/',function($scope,$count){ $scope.send($count.num.toString()); $count.num++; $scope.end(); }); //the client will get 0 every time requesting path '/'