再入門JavaScript - 名前空間篇

おこんばんわ。ひろきのだいちです。
今日は、再入門JavaScriptと題して、Perlなどのサーバサイドの言語になれている方に向けて、
JavaScriptにおける大規模開発の勘所として重要な名前空間の実現方法の解説と、
それらを簡単に提供するライブラリJS.Namespaceを作成しましたので、そのドキュメントを織り交ぜてご紹介します。

JavaScriptを仕事で利用される方や、ライブラリのコピペのような書き方から一歩進みたい方に向けて記述しています。

JAVASCRIPT:
  1. (function(ownNamespace) {
  2. var cache = {};
  3. var MESSAGE ={
  4. NOREF : ':: namespace error ::not found\t',
  5. EXIST : ':: namespace error ::overiding\t',
  6. REQUIRE:':: namespace error ::require\t',
  7. DYNAMIC:':: namespace error ::dynamic'
  8. };
  9. var onwNamespace = _createOrUse(ownNamespace)
  10. Object.extend(ownNamespace),{
  11. createNamespace: export_createNamespace,
  12. isLoaded: export_isLoaded,
  13. depends : export_depends,
  14. dynamic : export_dynamic,
  15. using   : export_using,
  16. wait    : export_wait,
  17. INCLUDE : '/static/js/'
  18. });
  19.  
  20. function _truncateFQN(fqn, n) {
  21. var leaves = fqn.split(".");
  22. var ret = [];
  23. for (var i = 0; i <n ; i++) {
  24. ret.push(leaves[i]);
  25. }
  26. return ret.join('.');
  27. };
  28. function _createOrUse(fqn){
  29. try{
  30. return export_createNamespace(fqn);
  31. }catch(e){
  32. if(e.toString().match(new RegExp(MESSAGE.EXIST))){
  33. return export_using(fqn);
  34. }
  35. }
  36. }
  37. function _getNamespace(fqn) {
  38. if (cache[fqn]) return cache[fqn];
  39. var leaves = fqn.split(".");
  40. var headLeaf = leaves[0];
  41. var tmpTop = window;
  42. var leaveLength = leaves.length;
  43.  
  44. for (var i = 0; i <leaveLength; i++) {
  45. if (!tmpTop[headLeaf]) {
  46. throw new Error(MESSAGE.NOREF + fqn)
  47. }
  48. tmpTop = tmpTop[headLeaf];
  49. headLeaf = leaves[i + 1];
  50. }
  51. return tmpTop;
  52. };
  53. function export_wait(condition,func){
  54. var cond = null
  55. if(Object.isFunction(condition)){
  56. cond = condition;
  57. }
  58. if(Object.isString(condition)){
  59. cond = function(){
  60. try{
  61. return _getNamespace(condition);
  62. }catch(e){
  63. return false;
  64. }
  65. }
  66. }
  67. var check = function(sync){
  68. var obj = null;
  69. if( obj = cond() ){
  70.  
  71. if(!sync){
  72. func.apply(obj);
  73. clearInterval(id);
  74. }else{
  75. return true;
  76. }
  77. }
  78. };
  79. if(check(true))return true;
  80. var id = setInterval( check, 30 );
  81. }
  82. function export_createNamespace(fqn, func) {
  83. var leaves = fqn.split(".");
  84. var headLeaf = leaves[0];
  85. var tmpTop = window;
  86. var leaveLength = leaves.length;
  87. if (cache[fqn]) throw (MESSAGE.EXIST + fqn);
  88. for (var i = 0; i <leaveLength; i++) {
  89. if (tmpTop[headLeaf] && typeof tmpTop[headLeaf] != "object") {
  90. throw new Error(MESSAGE.EXIST  + typeof tmpTop[headLeaf] + ". : " + fqn);
  91. }
  92. tmpTop[headLeaf] = tmpTop[headLeaf] || {};
  93. cache[_truncateFQN(fqn, i)] = tmpTop;
  94. tmpTop = tmpTop[headLeaf];
  95. headLeaf = leaves[i + 1];
  96. }
  97. if (func) {
  98. return func.apply(tmpTop);
  99. } else {
  100. return tmpTop;
  101. }
  102. }
  103. //
  104. function export_dynamic(fqn,func){
  105. var ns;
  106. try{
  107. return export_using(fqn,func);
  108. }catch(e){
  109. var url = ownNamespace.INCLUDE +fqn.replace(/\./g,'::').underscore()+'.js';
  110. new Ajax.Request(url,{
  111. method :'get',
  112. onComplete:function(r){
  113. ns = export_using(fqn,func);
  114. },
  115. onException:function(r,a){
  116. throw new Error(  MESSAGE.DYNAMIC + fqn );
  117. }
  118.  
  119. })
  120. }
  121. }
  122. function _getExportedObject(list,ns){
  123. var obj = {};
  124. var flag = false;
  125. $A(list).each(function(arg){
  126. flag = true;
  127. if(ns[arg]){
  128. obj[arg] = ns[arg];
  129. }else{
  130. throw(new Error('cant export'));
  131. }
  132. });
  133. return (flag)?obj:ns;
  134. }
  135. function export_using(fqn,funcOrExport) {
  136. var ns = _getNamespace(fqn);
  137. if(funcOrExport){
  138. if(Object.isFunction(funcOrExport)){
  139. var func = funcOrExport;
  140. var obj  = _getExportedObject( func.argumentNames(),ns );
  141. return func.apply(obj,func.argumentNames().map(function(arg){return obj[arg];}));
  142. }
  143. if(Object.isArray(funcOrExport)){
  144. return _getExportedObject(funcOrExport,ns);
  145. }
  146.  
  147. }else{
  148. return ns;
  149. }
  150. };
  151. function export_isLoaded(){
  152. try{
  153. return export_depends.apply(this,arguments);
  154. }catch(e){
  155. return false;
  156. }
  157. }
  158. function export_depends() {
  159. var fqn = $A(arguments).flatten();
  160. var ret = [];
  161. try{
  162. for(var i=0,l = fqn.length;i
  163. if (_getNamespace(fqn[i])) {
  164. ret.push( _getNamespace(fqn[i]));
  165. }
  166. }
  167. }
  168. catch(e){
  169. throw new Error(  MESSAGE.REQUIRE + fqn.join(",")+"/"+e.message );
  170. }
  171. if(ret.length == 1){
  172. return ret[0];
  173. }else{
  174. return ret;
  175. }
  176. };
  177.  
  178. })('JS.Namespace');

« 再入門JavaScript - 動的ローディング – 再入門JavaScript -目次- »

1 Comment »

  1. [...] 名前空間とスコープ [...]

    Pingback: 日本野望の会-Yabooo.org » 再入門JavaScript -目次- | – 29. October 2008 @ 1:41 am

Leave a comment

 

WP-Design: Vlad -- Powered by WordPress -- XHTML 1.0