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. * <input type="text" id="aaa" value="aaa">   
  70. * <input type="text" id="bbb" value="bbb">   
  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. * 去掉字符串中的<html>标签   
  219. */    
  220. stripTags: function() {    
  221. return this.replace(/</?[^>]+>/gi, '');    
  222. },    
  223. /**   
  224. * 这个方法很常见,通常的实现都是用正则表达式替换特殊字符为html规范定义的命名实体或者十进制编码,比如:   
  225. * string.replace(/&/g, "&").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. }   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值