
1 real_newbie OP 是不是CommonJS中的所谓Module和Yuiblog里所说的Module其实不是同一个概念? 前者只是单纯的把不同的function放到了不同的文件里, 加入了require的机制; 后者可以进行一些类似数据封装的工作? |
2 kuno 2011-04-28 19:38:12 +08:00 commonJS的module只是社区的一个proposal, 目前只有node.js部分实现了它。 不过,目前看起来下一个版本的ecmascript的module系统会不太一样, 兰州可以参考这个视频里的内容, |
3 kuno 2011-04-28 19:42:55 +08:00 刚看了commonJS的wiki,原来还有很多的其他系统也实现了。 更正一下, :( |
4 real_newbie OP @kuno, 感谢回复, CommonJS的实现并不是只有node.js, 像SproutCore, CouchDB之类的也有实现. (CommonJS的Spec有实现的列表) 你给出的那个视频好像是介绍下一代Javascript中如何实现module的吧?(抱歉, 没仔细看视频, 只看了标题和开头). 我目前在做的东西是基于CouchDB的, 所以我想知道是关于CommonJS的, 而不是下一代Javascript的实现. 不管怎么样, 还是非常感谢你~ |
5 aligo 2011-04-28 19:47:06 +08:00 数据封装是什么意思? 一般控制访问的话,return返回的是一个匿名函数或者对象,作为闭包,可以访问scope的东西,但是调用那个module的时候就用不了了 yui的module就是基于这个基本原理包装起来的,这样就不会因为随便操作一般对象的prototype产生问题 |
6 real_newbie OP |
7 aligo 2011-04-28 19:56:22 +08:00 CommonJS的那个require应该就是 function(jsfile){ var exports = {}; 然后这里执行js文件里的代码 return exports; } |
8 real_newbie OP |
9 aligo 2011-04-28 20:08:05 +08:00 @real_newbie 一样是闭包,不过如果那玩意要在客户端使用require,必须是像jsonp一样,完整的代码是: do_export(function(){ var exports = {}; 然后这里执行js文件里的代码 return exports; }); 不过这样的动态载入无故增加http请求数量,实在多余,一般是把所有js文件打包成一个,压缩之后要求客户端缓存 |
10 real_newbie OP @aligo, 我觉得不一样, 就拿主帖子中CommonJS Spec里的例子来讲. 我觉得如果是和yuiblog里一样的话, math.js应该改成下面这样样子, 才是一样的: exports.add = fuction() { return function() { var sum = 0, i = 0, args = arguments, l = args.length; while (i < l) { sum += args[i++]; } return sum; }; }; 然后再require的时候要这个样子: var add = require('math').add(); |
11 aligo 2011-04-28 20:28:32 +08:00 恩,你这个意思我明白,是要在exports.add有似有变量吧,不过你这个也不对,应该是: exports.add = fuction() { var args = arguments; return (function(arguments) { var sum = 0, i = 0, l = args.length; while (i < l) { sum += args[i++]; } return sum; })(arguments); }; 但是我上面说的是,你要给前端使用的话,只有在上面这段代码的东西的基础上,再包裹 do_export(function(){ var exports = {}; 上面的代码 return exports; }); 同时预先定义好do_export,用以接受require发出的http jsonp请求,但是这里有一个问题,浏览器的js貌似要blocking i/o等待require发起的异步请求返回是一件相当麻烦的事情了 不知道你看懂了我说的没有。。。 |
12 aligo 2011-04-28 20:29:11 +08:00 订正: exports.add = fuction() { var args = arguments; return (function(args) { var sum = 0, i = 0, l = args.length; while (i < l) { sum += args[i++]; } return sum; })(args); }; |
13 real_newbie OP @aligo, 抱歉, 我一开始就没讲明白, 我并不是要在前端使用的. 是用在Server端的. exports.add = fuction() { var args = arguments; return (function(arguments) { var sum = 0, i = 0, l = args.length; while (i < l) { sum += args[i++]; } return sum; })(arguments); }; 这个不和我的require('math').add(); 只是你这一版本的应该还是require('math).add, 不用加"()"进行调用, 对吧? |
14 aligo 2011-04-28 20:42:03 +08:00 @real_newbie 恩,我只是和你地址里的那个代码实现一样的东西var add = require('math').add;而不是var add = require('math').add(); 不过扯远了,我要说的是module这东西,用来实现通过scope进行访问控制是可以的,就像yui那样 然后如果前端使用它是为了分开文件就没有必要了,而这里的require实现的包括exports,对于每个文件是相互独立的,实现原理应该就是#7的那样,对于不同环境当然有不同实现,不过你这样去理解就够了 |
15 real_newbie OP |
16 aligo 2011-04-28 20:52:50 +08:00 @real_newbie 恩,其实就是前端要实现require动态载入js的话,就是动态插入script标签,必然事先得提供一个do_export函数用来接受闭包 |
17 aligo 2011-04-28 20:56:04 +08:00 @real_newbie 主要是你提到了yui,我以为你要在前端实现这个require,于是劝你放弃 |
18 real_newbie OP @aligo, 恩, 提到yui, 主要是因为我理解yui的这种方式, 所以想进行对比下. 另外off topic, 前端的话, 好像有这个玩意: https://github.com/jrburke/requirejs 不过似乎压缩以后还是显的有点过大了. |
19 aligo 2011-04-28 21:13:25 +08:00 @real_newbie 恩,这个东西well-scoped module就是用define()包裹新加入的js,类似我刚才说的do_export() 不过一般用用直接插入script标签就够了,不过意义不大,因为这增加了http请求次数,一般需求都是把js合并后各种压缩成一个 |
20 kuno 2011-04-29 10:14:46 +08:00 根据我使用node.js的经验,commonJS的实现并没有使用closure. 在node里面,module是一个特别的全局对象,有一个exports的属性。所有能够被调用的对象又都是exports的成员。当一个这个module通过require('MODULE_NAME')被调用之后,exports包含的成员就被载入了当前客户端global scope, 成为一个全局对象了。 function require(jsfile) { var module = eval(do_some_io(jsfile)); return moduel.exports; } |
21 aligo 2011-04-29 11:53:03 +08:00 @kuno 我试了一下 bar.js: var foo = 'bar' exports.bar = function () { return foo } foo.js: require.paths.unshift('.') var bar = require('bar') console.log(typeof bar.foo == 'undefined') console.log(typeof bar.bar == 'function') console.log(bar.foo) console.log(bar.bar()) node foo.js结果是: true true undefined bar 这里新require进来的js并不是一个global scope,如果拿来和ruby比较的话 module Bar BAR = 'bar' def ... end 然后在别的地方是可以Bar::BAR的 |
22 real_newbie OP |
23 aligo 2011-04-29 11:57:51 +08:00 @kuno 并且如果再加入一个rab.js如下 exports.rab = function () { return exports.bar() } 然后在原来的的foo.js var rab = require('rab') console.log(rab.rab()) 这样是会出错的 |
24 aligo 2011-04-29 12:02:53 +08:00 @kuno 最后,再创建一个oof.js: exports.oof = function () { return oof_text } exports.test_oof = function () { return typeof oof_text == 'undefined' } foo.js改为: var oof_text = 'oof!' require.paths.unshift('.') var oof = require('oof') console.log(oof.test_oof()) console.log(oof.oof()) 你会看到true然后出错 |
25 real_newbie OP 我不明白这个所谓global scope是什么意思. 如果要复用bar()似乎应该是这样子做: function bar() {} exports.bar = function() { return bar(); } exports.rab = function() { return bar(); } |
26 aligo 2011-04-29 12:06:17 +08:00 总之我要说的是这里require进来的东西就是module,每个js都是自己的scope,只有通过exports.的东西才会被require返回,但是这个exports在次require之间也是互相独立的 |
27 aligo 2011-04-29 12:13:13 +08:00 然后再来玩个新的bar.js: var foo = 'b' var_foo = 'ar' Bar = function () { return foo + var_foo } 然后这是新foo.js require.paths.unshift('.') require('bar') console.log(typeof bar == 'undefined') console.log(typeof var_bar == 'undefined') console.log(typeof Bar == 'function') console.log(Bar) console.log(Bar()) console.log(bar) console.log(var_bar) 结果是 true true true [Function] bar 到最后2个就出错了 |
28 n2n3 2011-04-29 12:25:47 +08:00 node.js 就是如 #7 @aligo 所说用 closure 实现的吧 https://github.com/joyent/node/blob/master/src/node.js#L420 https://github.com/joyent/node/blob/master/lib/module.js |
29 real_newbie OP |
30 real_newbie OP @n2n3, 果然还是源代码最高啊. module.js里的"Module._load"很清楚了. |
31 aligo 2011-04-29 15:06:03 +08:00 恩,所以继续离题一下,如果要在前端里实现这样的效果,就需要像你上面提到的那个requirejs那样,在每个js中用一个define()包裹,像#9那样 |