我思故我强

prototype学习资料(一)


prototype学习资料(一)

关键字:   prototype学习资料    
js 代码
  1. prototype 1.3.1 版本和之前的 1.2.0 版本有了不少改进,并增加了新的功能:    
  2. 1. 增加了事件注册管理    
  3. 2. 增加了空间定位的常用函数    
  4. 3. 改善了 xmlhttp 的封装    
  5. 4. 移除了 Effect.js,交给 Rico 或者 script.aculo.us 这些扩展库类实现。    
  6. 5. bug 修复    
  7. 代码:    
  8. /**   
  9. * 定义一个全局对象, 属性 Version 在发布的时候会替换为当前版本号   
  10. */     
  11. var  Prototype = {    
  12. Version: '1.3.1',    
  13. // 一个空方法,其后的代码常会用到,先前的版本该方法被定义于 Ajax 类中。    
  14. emptyFunction:  function () {}    
  15. }    
  16. /**   
  17. * 创建一种类型,注意其属性 create 是一个方法,返回一个构造函数。   
  18. * 一般使用如下   
  19. * var X = Class.create(); 返回一个类型,类似于 java 的一个Class实例。   
  20. * 要使用 X 类型,需继续用 new X()来获取一个实例,如同 java 的 Class.newInstance()方法。   
  21.  
  22. * 返回的构造函数会执行名为 initialize 的方法, initialize 是 Ruby 对象的构造器方法名字。   
  23. * 此时initialize方法还没有定义,其后的代码中创建新类型时会建立相应的同名方法。   
  24.  
  25. */     
  26. var  Class = {    
  27. create:  function () {    
  28. return   function () {    
  29. this .initialize.apply( this , arguments);    
  30. }    
  31. }    
  32. }    
  33. /**   
  34. * 创建一个对象,从变量名来思考,本意也许是定义一个抽象类,以后创建新对象都 extend 它。   
  35. * 但从其后代码的应用来看, Abstract 更多是为了保持命名空间清晰的考虑。   
  36. * 也就是说,我们可以给 Abstract 这个对象实例添加新的对象定义。   
  37. */     
  38. var  Abstract =  new  Object();    
  39. Object.extend =  function (destination, source) {    
  40. for  (property  in  source) {    
  41. destination[property] = source[property];    
  42. }    
  43. return  destination;    
  44. }    
  45. /**   
  46. * 获取参数对象的所有属性和方法,有点象多重继承。但是这种继承是动态获得的。   
  47. * 如:   
  48. * var a = new ObjectA(), b = new ObjectB();   
  49. * var c = a.extend(b);   
  50. * 此时 c 对象同时拥有 a 和 b 对象的属性和方法。但是与多重继承不同的是,c instanceof ObjectB 将返回false。   
  51.  
  52. * 旧版本的该方法定义如下:   
  53. * Object.prototype.extend = function(object) {   
  54. * for (property in object) {   
  55. * this[property] = object[property];   
  56. * }   
  57. * return this;   
  58. * }   
  59.  
  60. * 新的形式新定义了一个静态方法 Object.extend,这样做的目的大概是为了使代码更为清晰   
  61. */     
  62. Object.prototype.extend =  function (object) {    
  63. return  Object.extend.apply( this , [ this , object]);    
  64. }    
  65. /**   
  66. * 这个方法很有趣,它封装一个javascript函数对象,返回一个新函数对象,新函数对象的主体和原对象相同,但是bind()方法参数将被用作当前对象的对象。   
  67. * 也就是说新函数中的 this 引用被改变为参数提供的对象。   
  68. * 比如:   
  69.    
  70.    
  71. * .................   
  72. * <script>   
  73. * var aaa = document.getElementById("aaa");   
  74. * var bbb = document.getElementById("bbb");   
  75. * aaa.showValue = function() {alert(this.value);}   
  76. * aaa.showValue2 = aaa.showValue.bind(bbb);   
  77. * </script>   
  78. * 那么,调用aaa.showValue 将返回"aaa", 但调用aaa.showValue2 将返回"bbb"。   
  79.  
  80. * apply 是ie5.5后才出现的新方法(Netscape好像很早就支持了)。   
  81. * 该方法更多的资料参考MSDN http://msdn.microsoft.com/library/e...6jsmthApply.asp   
  82. * 阅读其后的代码就会发现,bind 被应用的很广泛,该方法和 Object.prototype.extend 一样是 Prototype 的核心。   
  83. * 还有一个 call 方法,应用起来和 apply 类似。可以一起研究下。   
  84. */     
  85. Function.prototype.bind =  function (object) {    
  86. var  __method =  this ;    
  87. return   function () {    
  88. __method.apply(object, arguments);    
  89. }    
  90. }    
  91. /**   
  92. * 和bind一样,不过这个方法一般用做html控件对象的事件处理。所以要传递event对象   
  93. * 注意这时候,用到了 Function.call。它与 Function.apply 的不同好像仅仅是对参数形式的定义。   
  94. */     
  95. Function.prototype.bindAsEventListener =  function (object) {    
  96. var  __method =  this ;    
  97. return   function (event) {    
  98. __method.call(object, event || window.event);    
  99. }    
  100. }    
  101. /**   
  102. * 将整数形式RGB颜色值转换为HEX形式   
  103. */     
  104. Number.prototype.toColorPart =  function () {    
  105. var  digits =  this .toString(16);    
  106. if  ( this  < 16)  return  '0' + digits;    
  107. return  digits;    
  108. }    
  109. /**   
  110. * 典型 Ruby 风格的函数,将参数中的方法逐个调用,返回第一个成功执行的方法的返回值   
  111. */     
  112. var  Try = {    
  113. these:  function () {    
  114. var  returnValue;    
  115. for  ( var  i = 0; i < arguments.length; i++) {    
  116. var  lambda = arguments;    
  117. try  {    
  118. returnValue = lambda();    
  119. break ;    
  120. catch  (e) {}    
  121. }    
  122. return  returnValue;    
  123. }    
  124. }    
  125. /*--------------------------------------------------------------------------*/     
  126. /**   
  127. * 一个设计精巧的定时执行器   
  128. * 首先由 Class.create() 创建一个 PeriodicalExecuter 类型,   
  129. * 然后用对象直接量的语法形式设置原型。   
  130.  
  131. * 需要特别说明的是 rgisterCallback 方法,它调用上面定义的函数原型方法bind, 并传递自己为参数。   
  132. * 之所以这样做,是因为 setTimeout 默认总以 window 对象为当前对象,也就是说,如果 registerCallback 方法定义如下的话:   
  133. * registerCallback: function() {   
  134. * setTimeout(this.onTimerEvent, this.frequency * 1000);   
  135. * }   
  136. * 那么,this.onTimeoutEvent 方法执行失败,因为它无法访问 this.currentlyExecuting 属性。   
  137. * 而使用了bind以后,该方法才能正确的找到this,也就是PeriodicalExecuter的当前实例。   
  138. */     
  139. var  PeriodicalExecuter = Class.create();    
  140. PeriodicalExecuter.prototype = {    
  141. initialize:  function (callback, frequency) {    
  142. this .callback = callback;    
  143. this .frequency = frequency;    
  144. this .currentlyExecuting =  false ;    
  145. this .registerCallback();    
  146. },    
  147. registerCallback:  function () {    
  148. setInterval( this .onTimerEvent.bind( this ),  this .frequency * 1000);    
  149. },    
  150. onTimerEvent:  function () {    
  151. if  (! this .currentlyExecuting) {    
  152. try  {    
  153. this .currentlyExecuting =  true ;    
  154. this .callback();    
  155. finally  {    
  156. this .currentlyExecuting =  false ;    
  157. }    
  158. }    
  159. }    
  160. }    
  161. /*--------------------------------------------------------------------------*/     
  162. /**   
  163. * 这个函数就 Ruby 了。我觉得它的作用主要有两个   
  164. * 1. 大概是 document.getElementById(id) 的最简化调用。   
  165. * 比如:$("aaa") 将返回 aaa 对象   
  166. * 2. 得到对象数组   
  167. * 比如: $("aaa","bbb") 返回一个包括id为"aaa"和"bbb"两个input控件对象的数组。   
  168. */     
  169. function  $() {    
  170. var  elements =  new  Array();    
  171. for  ( var  i = 0; i < arguments.length; i++) {    
  172. var  element = arguments;    
  173. if  ( typeof  element == 'string')    
  174. element = document.getElementById(element);    
  175. if  (arguments.length == 1)    
  176. return  element;    
  177. elements.push(element);    
  178. }    
  179. return  elements;    
  180. }    
  181. /**   
  182. * 为兼容旧版本的浏览器增加 Array 的 push 方法。   
  183. */     
  184. if  (!Array.prototype.push) {    
  185. Array.prototype.push =  function () {    
  186. var  startLength =  this .length;    
  187. for  ( var  i = 0; i < arguments.length; i++)    
  188. this [startLength + i] = arguments;    
  189. return   this .length;    
  190. }    
  191. }    
  192. /**   
  193. * 为兼容旧版本的浏览器增加 Function 的 apply 方法。   
  194. */     
  195. if  (!Function.prototype.apply) {    
  196. // Based on code from http://www.youngpup.net/    
  197. Function.prototype.apply =  function (object, parameters) {    
  198. var  parameterStrings =  new  Array();    
  199. if  (!object) object = window;    
  200. if  (!parameters) parameters =  new  Array();    
  201.   
  202. for  ( var  i = 0; i < parameters.length; i++)    
  203. parameterStrings = 'parameters[' + i + ']';    
  204.   
  205. object.__apply__ =  this ;    
  206. var  result = eval('object.__apply__(' +    
  207. parameterStrings.join(', ') + ')');    
  208. object.__apply__ =  null ;    
  209.   
  210. return  result;    
  211. }    
  212. }    
  213. /**   
  214. * 扩展 javascript 内置的 String 对象   
  215. */     
  216. String.prototype.extend({    
  217. /**   
  218. * 去掉字符串中的标签   
  219. */     
  220. stripTags:  function () {    
  221. return   this .replace(/</?[^>]+>/gi,  '' );    
  222. },    
  223. /**   
  224. * 这个方法很常见,通常的实现都是用正则表达式替换特殊字符为html规范定义的命名实体或者十进制编码,比如:   
  225. * string.replace(/&/g, "&").replace(//g, ">");   
  226. * 而这里的实现借用浏览器自身的内部替换,确实巧妙。   
  227. */     
  228. escapeHTML:  function () {    
  229. var  div = document.createElement('div');    
  230. var  text = document.createTextNode( this );    
  231. div.appendChild(text);    
  232. return  div.innerHTML;    
  233. },    
  234. /**   
  235. * 同上   
  236. */     
  237. unescapeHTML:  function () {    
  238. var  div = document.createElement('div');    
  239. div.innerHTML =  this .stripTags();    
  240. return  div.childNodes[0].nodeValue;    
  241. }    
  242. });    
  243. /**   
  244. * 定义 Ajax 对象, 静态方法 getTransport 方法返回一个 XMLHttp 对象   
  245. */     
  246. var  Ajax = {    
  247. getTransport:  function () {    
  248. return  Try.these(    
  249. function () { return   new  ActiveXObject('Msxml2.XMLHTTP')},    
  250. function () { return   new  ActiveXObject('Microsoft.XMLHTTP')},    
  251. function () { return   new  XMLHttpRequest()}    
  252. ) ||  false ;    
  253. }    
  254. }    
  255. /**   
  256. * 我以为此时的Ajax对象起到命名空间的作用。   
  257. * Ajax.Base 声明为一个基础对象类型   
  258. * 注意 Ajax.Base 并没有使用 Class.create() 的方式来创建,我想是因为作者并不希望 Ajax.Base 被库使用者实例化。   
  259. * 作者在其他对象类型的声明中,将会继承于它。   
  260. * 就好像 java 中的私有抽象类   
  261. */     
  262. Ajax.Base =  function () {};    
  263. Ajax.Base.prototype = {    
  264. /**   
  265. * extend (见上) 的用法真是让人耳目一新   
  266. * options 首先设置默认属性,然后再 extend 参数对象,那么参数对象中也有同名的属性,那么就覆盖默认属性值。   
  267. * 想想如果我写这样的实现,应该类似如下:   
  268. setOptions: function(options) {   
  269. this.options.methed = options.methed? options.methed : 'post';   
  270. ..........   
  271.  
  272. 我想很多时候,java 限制了 js 的创意。   
  273. */     
  274. setOptions:  function (options) {    
  275. this .options = {    
  276. method: 'post',    
  277. asynchronous:  true ,    
  278. parameters:  ''     
  279. }.extend(options || {});    
  280. },    
  281. /**   
  282. * 如果 xmlhttp 调用返回正确的HTTP状态值,函数返回ture, 反之false。   
  283. * xmlhttp 的 readyState 属性不足以准确判断 xmlhttp 远程调用成功,该方法是readyState判断的一个前提条件   
  284. */     
  285. responseIsSuccess:  function () {    
  286. return   this .transport.status == undefined    
  287. ||  this .transport.status == 0    
  288. || ( this .transport.status >= 200 &&  this .transport.status < 300);    
  289. },    
  290. /**   
  291. * 如果 xmlhttp 调用返回错误的HTTP状态值,函数返回ture, 反之false。   
  292. */     
  293. responseIsFailure:  function () {    
  294. return  ! this .responseIsSuccess();    
  295. }    
  296. }    
  297. /**   
  298. * Ajax.Request 封装 XmlHttp   
  299. */     
  300. Ajax.Request = Class.create();    
  301. /**   
  302. * 定义四种事件(状态), 参考http://msdn.microsoft.com/workshop/...eadystate_1.asp   
  303. */     
  304. Ajax.Request.Events =    
  305. ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];    
  306. /**   
  307. * 相比先前的版本,对于 xmlhttp 的调用和返回值处理分离得更为清晰   
  308. */     
  309. Ajax.Request.prototype = ( new  Ajax.Base()).extend({    
  310. initialize:  function (url, options) {    
  311. this .transport = Ajax.getTransport();    
  312. this .setOptions(options);    
  313. this .request(url);    
  314. },    
  315.   /**   
  316. * 新增加 request 方法封装 xmlhttp 的调用过程。   
  317. */     
  318. request:  function (url) {    
  319. var  parameters =  this .options.parameters ||  '' ;    
  320. if  (parameters.length > 0) parameters += '&_=';    
  321. try  {    
  322. if  ( this .options.method == 'get')    
  323. url += '?' + parameters;    
  324. this .transport.open( this .options.method, url,    
  325. this .options.asynchronous);    
  326. if  ( this .options.asynchronous) {    
  327. this .transport.onreadystatechange =  this .onStateChange.bind( this );    
  328. setTimeout(( function () { this .respondToReadyState(1)}).bind( this ), 10);    
  329. }    
  330. this .setRequestHeaders();    
  331. var  body =  this .options.postBody ?  this .options.postBody : parameters;    
  332. this .transport.send( this .options.method == 'post' ? body :  null );    
  333. catch  (e) {    
  334. }    
  335. },    
  336. /**   
  337. * 新增加的 setRequestHeaders 方法允许添加自定义的http header   
  338. */     
  339. setRequestHeaders:  function () {    
  340. var  requestHeaders =    
  341. ['X-Requested-With', 'XMLHttpRequest',    
  342. 'X-Prototype-Version', Prototype.Version];    
  343. if  ( this .options.method == 'post') {    
  344. requestHeaders.push('Content-type',    
  345. 'application/x-www-form-urlencoded');    
  346. /* Force "Connection: close" for Mozilla browsers to work around   
  347. * a bug where XMLHttpReqeuest sends an incorrect Content-length   
  348. * header. See Mozilla Bugzilla #246651.   
  349. */     
  350. if  ( this .transport.overrideMimeType)    
  351. requestHeaders.push('Connection', 'close');    
  352. }    
  353. /**   
  354. * 其后的 apply 方法的调用有些奇技淫巧的意味   
  355. * 从上下文中我们可以分析出 this.options.requestHeaders 是调用者自定义的http header数组。   
  356. * requestHeaders 也是一个数组,将一个数组中的元素逐个添加到另一个元素中,直接调用   
  357. * requestHeaders.push(this.options.requestHeaders)   
  358. * 是不行的,因为该调用导致 this.options.requestHeaders 整个数组作为一个元素添加到 requestHeaders中。   
  359. * javascript的Array对象还提供一个concat 的方法表面上满足要求,但是concat实际上是创建一个新数组,将两个数组的元素添加到新数组中。   
  360. * 所以,下面的代码也可以替换为   
  361. * requestHeaders = requestHeaders.concat(this.options.requestHeaders);   
  362. * 很显然,作者不喜欢这样的代码方式   
  363. * 而 apply 方法的语法 apply([thisObj[,argArray]]) 本身就要求第二个参数是一个数组或者arguments对象。   
  364. * 所以巧妙的实现了 concat 函数的作用。   
  365. * 令人拍案叫绝啊!   
  366. */     
  367. if  ( this .options.requestHeaders)    
  368. requestHeaders.push.apply(requestHeaders,  this .options.requestHeaders);    
  369. for  ( var  i = 0; i < requestHeaders.length; i += 2)    
  370. this .transport.setRequestHeader(requestHeaders, requestHeaders[i+1]);    
  371. },    
  372. onStateChange:  function () {    
  373. var  readyState =  this .transport.readyState;    
  374. /**   
  375. * 如果不是 Loading 状态,就调用回调函数   
  376. */     
  377. if  (readyState != 1)    
  378. this .respondToReadyState( this .transport.readyState);    
  379. },    
  380. /**   
  381. * 回调函数定义在 this.options 属性中,比如:   
  382. var option = {   
  383. onLoaded : function(req) {...};   
  384. ......   
  385.  
  386. new Ajax.Request(url, option);   
  387. */     
  388. respondToReadyState:  function (readyState) {    
  389. var  event = Ajax.Request.Events[readyState];    
  390. /**   
  391. * 新增的回调函数处理,调用者还可以在options中定义 on200, onSuccess 这样的回调函数   
  392. * 在 readyState 为完成状态的时候调用   
  393. */     
  394. if  (event == 'Complete')    
  395. ( this .options['on' +  this .transport.status]    
  396. ||  this .options['on' + ( this .responseIsSuccess() ? 'Success' : 'Failure')]    
  397. || Prototype.emptyFunction)( this .transport);    
  398. ( this .options['on' + event] || Prototype.emptyFunction)( this .transport);    
  399. /* Avoid memory leak in MSIE: clean up the oncomplete event handler */     
  400. if  (event == 'Complete')    
  401. this .transport.onreadystatechange = Prototype.emptyFunction;    
  402. }    
  403. });    
  404. /**   
  405. * Ajax.Updater 用于绑定一个html元素与 XmlHttp调用的返回值。类似与 buffalo 的 bind。   
  406. * 如果 options 中有 insertion(见后) 对象的话, insertion 能提供更多的插入控制。   
  407. */     
  408. Ajax.Updater = Class.create();    
  409. Ajax.Updater.ScriptFragment = '(?:<script>)((n|.)*?)(?:</script>)';    
  410. Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({    
  411. initialize:  function (container, url, options) {    
  412. /**   
  413. * containers 就是被绑定的 html 对象,xmlhttp的返回值被赋给该对象的 innerHTML 属性。   
  414. * 相比新版本,containers 根据container参数定义 success 和 failure 引用,如果它们被定义的话,根据xmlhttp调用是否成功来选择   
  415. * 更新对象,假想调用可能如下:   
  416. * var c = {success: $("successDiv"), failure: $("failureDiv")};   
  417. * new Ajax.Updater(c, url, options);   
  418. * 那么调用成功则 successDiv 显示成功信息或者数据,反之 failureDiv 显示错误信息   
  419. */     
  420. this .containers = {    
  421. success: container.success ? $(container.success) : $(container),    
  422. failure: container.failure ? $(container.failure) :    
  423. (container.success ?  null  : $(container))    
  424. }    
  425. this .transport = Ajax.getTransport();    
  426. this .setOptions(options);    
  427. var  onComplete =  this .options.onComplete || Prototype.emptyFunction;    
  428. this .options.onComplete = ( function () {    
  429. this .updateContent();    
  430. onComplete( this .transport);    
  431. }).bind( this );    
  432. this .request(url);    
  433. },    
  434. updateContent:  function () {    
  435. var  receiver =  this .responseIsSuccess() ?    
  436. this .containers.success :  this .containers.failure;    
  437. var  match =  new  RegExp(Ajax.Updater.ScriptFragment, 'img');    
  438. var  response =  this .transport.responseText.replace(match,  '' );    
  439. var  scripts =  this .transport.responseText.match(match);    
  440. if  (receiver) {    
  441. if  ( this .options.insertion) {    
  442. new   this .options.insertion(receiver, response);    
  443. else  {    
  444. receiver.innerHTML = response;    
  445. }    
  446. }    
  447. if  ( this .responseIsSuccess()) {    
  448. if  ( this .onComplete)    
  449. setTimeout(( function () { this .onComplete(    
  450. this .transport)}).bind( this ), 10);    
  451. }   

posted on 2007-09-20 19:36 李云泽 阅读(331) 评论(0)  编辑  收藏 所属分类: Prototype(Ajax)


只有注册用户登录后才能发表评论。


网站导航: