本着学习的目的添加的注释,本人js水平很差,有不对的地方请各位一定多多指教。
/* Prototype JavaScript framework, version 1.5.1.1 * (c) 2005-2007 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ * /*--------------------------------------------------------------------------*/ /*一个新建对象,包含当前Prototype的版本以及当前浏览器信息*/ var Prototype = { Version: '1.5.1.1', /*这个方法用的很巧,将一个NaN或者undefined对象通过两次!运算,得到false*/ Browser: { IE: !!(window.attachEvent && !window.opera), Opera: !!window.opera, WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1 }, /*IE6.0和Firefox2.0都不支持XPath*/ /*IE6.0不支持ElementExtensions,Firefox则支持*/ BrowserFeatures: { XPath: !!document.evaluate, ElementExtensions: !!window.HTMLElement, SpecificElementExtensions: (document.createElement('div').__proto__ !== document.createElement('form').__proto__) }, /*正则,用来抽取出页面上用<script ...> ... </script>标记的代码段*/ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>', JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, /*定义一个空函数*/ emptyFunction: function() { }, /*暂时不明白定义这个函数的用意*/ K: function(x) { return x } } /*定义了一个对象Class,抵用Class.create会返回一个function,返回的function代码为 function() { this.initialize.apply(this.arguments); } 当调用返回的这个function或者使用new关键字的时候会调用function中定义的initialize方法 */ var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var Abstract = new Object(); /*定义了一个方法叫做extend,意思是把source中的属性,函数等都复制到destination这个对象里面去, 有点儿ruby中mixin的味道*/ Object.extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; } /* 给Object类新增加一个新的类方法,名叫inspect 用法Object.inspect(obj); 输出为一个字符串,代表obj的类新名称 */ Object.extend(Object, { inspect: function(object) { try { if (object === undefined) return 'undefined'; if (object === null) return 'null'; /*object.inspect? 意思是如果object定义了inspect方法,则执行inspect 和ruby中的duck type有异曲同工之妙,只不过js中更加直接,不需要ruby中的respond_to?了*/ return object.inspect ? object.inspect() : object.toString(); } catch (e) { if (e instanceof RangeError) return '...'; throw e; } }, toJSON: function(object) { var type = typeof object; switch(type) { case 'undefined': case 'function': case 'unknown': return; case 'boolean': return object.toString(); } if (object === null) return 'null'; if (object.toJSON) return object.toJSON(); if (object.ownerDocument === document) return; var results = []; for (var property in object) { var value = Object.toJSON(object[property]); if (value !== undefined) results.push(property.toJSON() + ': ' + value); } return '{' + results.join(', ') + '}'; }, /*把对象当哈希使唤。。。 当然js中这是再常用不过的了*/ keys: function(object) { var keys = []; for (var property in object) keys.push(property); return keys; }, values: function(object) { var values = []; for (var property in object) values.push(object[property]); return values; }, /*建立对象的浅拷贝,仅仅复制字段和引用*/ clone: function(object) { return Object.extend({}, object); } }); /*给function添加一个bind方法,以便把某个方法的执行绑定到一个对象上,也就是说在一个指定对象的上下文中执行本方法*/ Function.prototype.bind = function() { /*注意此处__method变量和this的用法,这是为了避免this指向错误的上下文环境*/ var __method = this, args = $A(arguments), object = args.shift(); return function() { return __method.apply(object, args.concat($A(arguments))); } } /*把function绑定到一个对象上作为事件处理函数*/ Function.prototype.bindAsEventListener = function(object) { var __method = this, args = $A(arguments), object = args.shift(); return function(event) { /*为了兼容IE,使用[event || window.event]*/ return __method.apply(object, [event || window.event].concat(args)); } } /*为数值类型增加了 toColorPart, succ, times, toPaddedString, toJSON 方法*/ Object.extend(Number.prototype, { toColorPart: function() { return this.toPaddedString(2, 16); }, succ: function() { return this + 1; }, times: function(iterator) { $R(0, this, true).each(iterator); return this; }, toPaddedString: function(length, radix) { var string = this.toString(radix || 10); return '0'.times(length - string.length) + string; }, toJSON: function() { return isFinite(this) ? this.toString() : 'null'; } }); /*为日期类型增加toJSON方法*/ Date.prototype.toJSON = function() { return '"' + this.getFullYear() + '-' + (this.getMonth() + 1).toPaddedString(2) + '-' + this.getDate().toPaddedString(2) + 'T' + this.getHours().toPaddedString(2) + ':' + this.getMinutes().toPaddedString(2) + ':' + this.getSeconds().toPaddedString(2) + '"'; }; /*Try.these(meth1,meth2,meth3...) 会依次尝试执行meth1,meth2,meth3....,第一个成功执行的方法的结果被返回,剩下的方法不再执行*/ var Try = { these: function() { var returnValue; for (var i = 0, length = arguments.length; i < length; i++) { var lambda = arguments[i]; try { returnValue = lambda(); break; } catch (e) {} } return returnValue; } } /*--------------------------------------------------------------------------*/ /*一个定时器*/ /*usage: var timer = new PeriodicalExecuter(callback_function, frequency) 顾名思义,第一个参数是定时器的回调函数,第二个参数是时间间隔,生成定时器的实例后,定时器自动开始执行 timer.registerCallback; //启动定时器 timer.stop; //终止定时器 */ var PeriodicalExecuter = Class.create(); PeriodicalExecuter.prototype = { initialize: function(callback, frequency) { this.callback = callback; this.frequency = frequency; this.currentlyExecuting = false; this.registerCallback(); }, registerCallback: function() { this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); }, stop: function() { if (!this.timer) return; clearInterval(this.timer); this.timer = null; }, onTimerEvent: function() { if (!this.currentlyExecuting) { try { this.currentlyExecuting = true; /*这里暂时不太理解,为什么callback要加上一个this参数?*/ this.callback(this); } finally { this.currentlyExecuting = false; } } } } /*给String类增加了一个类方法interpret和一个静态哈希specialChar*/ Object.extend(String, { interpret: function(value) { /*String(value)相当于value.toString(); */ return value == null ? '' : String(value); }, /*一些转义字符*/ specialChar: { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\\': '\\\\' } }); /*String类的实例增加一些方法*/ /* gsub, sub, scan, truncate, strip, stripTags, */ Object.extend(String.prototype, { /*gsub方法,replacement参数可以是一方法,也可是一个元素*/ /*usage: var str = somestring; str.gsub(param1,param2); 其中params是用来匹配的字符串,也可是一个正则表达式,param2可以是一个用来替换的字符串,也可以是一个方法,这个方法一般定义为 function(match){...} match为每一个匹配到的子字符串,在函数内部可以对子字符串进行处理,然后返回处理后的结果,举一个实际例子: var str = "hello world!"; alert(str.gsub("o",function(match){ return match[0].succ(); } )); 弹出窗口显示"hellp,wprld!" */ gsub: function(pattern, replacement) { var result = '', source = this, match; /*假如某个字符串变量str调用了gsub方法,则在此会调用str的prepareReplacement方法 此方法接收gsub的replacement变量作为参数*/ replacement = arguments.callee.prepareReplacement(replacement); /*source就是调用gsub方法的字符串对象本身*/ while (source.length > 0) { /*调用match方法进行匹配,找到第一个匹配的对象*/ if (match = source.match(pattern)) { /*截取开头到匹配位置的字符串,添加到result中*/ result += source.slice(0, match.index); /*替换匹配的字符串,然后加到result结尾*/ result += String.interpret(replacement(match)); /*删除第一个被匹配元素以及之前的部分,进行下一个循环*/ source = source.slice(match.index + match[0].length); } else { result += source, source = ''; } } return result; }, /*同gsub类似*/ /*只替换前count个匹配对象*/ sub: function(pattern, replacement, count) { replacement = this.gsub.prepareReplacement(replacement); count = count === undefined ? 1 : count; return this.gsub(pattern, function(match) { if (--count < 0) return match[0]; return replacement(match); }); }, /*找到所有匹配对象,在每个对象上执行iterator中定义的方法*/ scan: function(pattern, iterator) { this.gsub(pattern, iterator); return this; }, /*截断函数,如果没有定义length就默认截断长度为30,没有定义truncation就默认为...*/ /*usage: var str = "hello world"; str.truncate(7,".."); 此时str的内容变为"hello.." 注意它直接修改了当前字符串对象的内容 truncate的长度也计算到了length中 */ truncate: function(length, truncation) { length = length || 30; truncation = truncation === undefined ? '...' : truncation; return this.length > length ? this.slice(0, length - truncation.length) + truncation : this; }, /*去掉字符串开头和结尾的空格*/ strip: function() { return this.replace(/^\s+/, '').replace(/\s+$/, ''); }, /*去除所有<xxxx>和</xxxx>这样的字符串*/ stripTags: function() { return this.replace(/<\/?[^>]+>/gi, ''); }, /*这三个函数还不是很懂,等到以后功力长进一点儿了再看*/ stripScripts: function() { return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); }, extractScripts: function() { var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); return (this.match(matchAll) || []).map(function(scriptTag) { return (scriptTag.match(matchOne) || ['', ''])[1]; }); }, evalScripts: function() { return this.extractScripts().map(function(script) { return eval(script) }); }, /*prototype给function添加了两个属性,一个是div,一个是text,这里似乎是利用了浏览器本身的功能来做escape 等到后面看到的时候再说吧*/ escapeHTML: function() { var self = arguments.callee; self.text.data = this; return self.div.innerHTML; }, unescapeHTML: function() { var div = document.createElement('div'); div.innerHTML = this.stripTags(); return div.childNodes[0] ? (div.childNodes.length > 1 ? $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : div.childNodes[0].nodeValue) : ''; }, toQueryParams: function(separator) { /* 把类似于param1=value1¶m2=value2...¶mn=valuen#dock这样的uri拆分 返回一个对象,对象包含n个属性,属性名分别为param1,param2....paramn,对应的值分别为value1,value2...valuen */ var match = this.strip().match(/([^?#]*)(#.*)?$/); if (!match) return {}; return match[1].split(separator || '&').inject({}, function(hash, pair) { if ((pair = pair.split('='))[0]) { var key = decodeURIComponent(pair.shift()); /*主要是为了防止value值本身中包含等号,例如某个param名叫expression,他的value为"x=10*10-y;" 若不考虑这种情况可能会导致params截断出错*/ var value = pair.length > 1 ? pair.join('=') : pair[0]; if (value != undefined) value = decodeURIComponent(value); /*如果字符串中包含了多个同名的param,则这这个param对应的values会放入一个数组*/ /*例如有一个查询字符串name=jacobo&sex=male&hobit=soccer&hobit=music 经过toQueryParams后会返回一个对象{name:jacobo,sex:male,hobit:[soccer,music]}*/ if (key in hash) { if (hash[key].constructor != Array) hash[key] = [hash[key]]; hash[key].push(value); } else hash[key] = value; } return hash; }); }, toArray: function() { return this.split(''); }, /*succ方法和ruby中的几乎一样 var str = "1" str.succ 输出结果为"2" var str = "abcd" str.succ 输出结果为"abce" */ succ: function() { return this.slice(0, this.length - 1) + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); }, /*times不知道是不是也是来源于ruby,用法有差别不过效果一样 var str = "ab"; str.times(5);输出结果为"ababababab"五次"ab" */ times: function(count) { var result = ''; for (var i = 0; i < count; i++) result += this; return result; }, /*将类似于"ab-cd-ef-gh"这样的字符串转化为"abCdEfGh"*/ camelize: function() { var parts = this.split('-'), len = parts.length; if (len == 1) return parts[0]; /*不明白这句话的意思,如果字符串第一个字符为'-',直接忽略掉parts[0]不就ok了么,高人写的代码就是不明白*/ var camelized = this.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0]; for (var i = 1; i < len; i++) camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); return camelized; }, /*大写首字母*/ capitalize: function() { return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); }, /*将大小写混杂的字符串按照大小写分割,然后再用下划线连接起来 例如 var str = "aBCd-eFg"; str.underscore;输出结果为"a_BC_d_e_F_g */ underscore: function() { return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); }, /*将下划线替换为'-' 例如 var str = "a_b_c"; str.dasherize; 输出"a-b-c" */ dasherize: function() { return this.gsub(/_/,'-'); }, /*暂时不明白,留到最后看,不过ms和ruby中的inspect功能是一致的*/ inspect: function(useDoubleQuotes) { var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { var character = String.specialChar[match[0]]; return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); }); if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; return "'" + escapedString.replace(/'/g, '\\\'') + "'"; }, toJSON: function() { return this.inspect(true); }, unfilterJSON: function(filter) { return this.sub(filter || Prototype.JSONFilter, '#{1}'); }, isJSON: function() { var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); }, evalJSON: function(sanitize) { var json = this.unfilterJSON(); try { if (!sanitize || json.isJSON()) return eval('(' + json + ')'); } catch (e) { } throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); }, /*确定字符串是否包含某个子字符串*/ include: function(pattern) { return this.indexOf(pattern) > -1; }, /*是否以某个字符串开头*/ startsWith: function(pattern) { return this.indexOf(pattern) === 0; }, /*是否以某个字符串结尾*/ endsWith: function(pattern) { var d = this.length - pattern.length; return d >= 0 && this.lastIndexOf(pattern) === d; }, /*判断字符串是否为空字符串(也就是是否为"")*/ empty: function() { return this == ''; }, /*是否为仅包含空格的字符串*/ blank: function() { /*此处用到了javascript正则的test方法*/ return /^\s*$/.test(this); } }); /*IE里面处理escapeHTML和unescapeHTML的方式和在firefox中不同,此处重新定义了一遍 就是用正则查找然后几个关键字"&","<",">"转义 */ if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { escapeHTML: function() { return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }, unescapeHTML: function() { return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); } }); String.prototype.gsub.prepareReplacement = function(replacement) { if (typeof replacement == 'function') return replacement; var template = new Template(replacement); return function(match) { return template.evaluate(match) }; } /*给toQueryParams起了一个别名,叫做parseQuery*/ String.prototype.parseQuery = String.prototype.toQueryParams; /*给String类型实例的escapeHTML方法添加了两个方法 div:创建一个层 text:创建了一个Text节点 */ Object.extend(String.prototype.escapeHTML, { div: document.createElement('div'), text: document.createTextNode('') }); var Template = Class.create(); /*这个正则实在不明白。。。*/ Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; Template.prototype = { initialize: function(template, pattern) { this.template = template.toString(); this.pattern = pattern || Template.Pattern; }, evaluate: function(object) { return this.template.gsub(this.pattern, function(match) { var before = match[1]; /*如果replacement字符串以'\'开头,则直接返回后面的"#{some words here}" 否则,返回replacement的第一个字符+"#some words here",注意这里会去掉{} */ /*不明白道理*/ if (before == '\\') return match[2]; return before + String.interpret(object[match[3]]); }); } } var $break = {}, $continue = new Error('"throw $continue" is deprecated, use "return" instead'); var Enumerable = { /*对枚举类型进行扩展,添加each方法,each方法的参数为一个两个参数的函数 函数第一个参数接收每一轮枚举时的枚举对象,第二个参数指示该枚举值位于原枚举类型中的第几项,当然,这两个参数也是可以省略的 若函数执行中抛出异常,则枚举过程立刻停止 usage: var arr = new Array; arr.each(function(value,index){...}); 实际的例子: var arr = [0,1,2,3,4,5]; arr.each(function(value,index){ alert("value is "+String(value)+",index is "+String(index)+".");}); */ /*iterator的返回值被忽略*/ each: function(iterator) { var index = 0; try { /*_each可枚举类型自带的一个方法,可枚举类型包括Array*/ this._each(function(value) { iterator(value, index++); }); } catch (e) { if (e != $break) throw e; } return this; }, /* var arr = [1,2,3,4,5]; alert(arr.eachSlice(2).inspect()); 输出结果为[[1,2],[3,4],[5]] */ eachSlice: function(number, iterator) { var index = -number, slices = [], array = this.toArray(); while ((index += number) < array.length) slices.push(array.slice(index, index+number)); return slices.map(iterator); }, /*对可枚举类型中的每一个元素调用传入的iterator方法进行处理,最后返回true或者false,如果不传入iterator方法,则比较可枚举类型中的每一个枚举项, 若枚举项中有NaN,undefined,null,则停止枚举,结果返回false*/ /*iterator的返回值被忽略*/ all: function(iterator) { var result = true; this.each(function(value, index) { /*注意这里的用法,js的弱类型特性一目了然*/ result = result && !!(iterator || Prototype.K)(value, index); if (!result) throw $break; }); return result; }, /*任意一项执行成功则枚举停止*/ /*iterator的返回值被忽略*/ any: function(iterator) { var result = false; this.each(function(value, index) { if (result = !!(iterator || Prototype.K)(value, index)) throw $break; }); return result; }, /*对枚举中所有元素进行处理,并将处理结果保存并且返回 此时需要iterator将单个元素的处理结果返回*/ /* var arr = [1,2,3,4,5]; arr.collect(function(value){ return value.succ(); } ).each(function(value) { alert(value); }) 弹出窗口分别显示2,3,4,5,6 */ collect: function(iterator) { var results = []; this.each(function(value, index) { results.push((iterator || Prototype.K)(value, index)); }); return results; }, /*对枚举类型中所有元素依次处理,若iterator函数返回true,则终止枚举,并返回当前枚举项的值*/ /* var arr = [1,2,3,4,5]; alert(arr.detect(function(value){return value>3; })); 弹出窗口显示4 */ detect: function(iterator) { var result; this.each(function(value, index) { if (iterator(value, index)) { result = value; throw $break; } }); return result; }, /*对枚举类型中所有元素一次处理,若iterator函数返回非NaN,null,undefined,false 则将当前枚举元素的值存放到一个新的枚举类型中,最后返回此新的枚举类新*/ findAll: function(iterator) { var results = []; this.each(function(value, index) { if (iterator(value, index)) results.push(value); }); return results; }, /*加强版的findAll,先用正则匹配出符合pattern的元素,然后使用iterator进行处理,最后将处理后的结果存放在数组中返回*/ grep: function(pattern, iterator) { var results = []; this.each(function(value, index) { var stringValue = value.toString(); if (stringValue.match(pattern)) results.push((iterator || Prototype.K)(value, index)); }) return results; }, /*判断数组中是否包含某个元素*/ /*包含则返回true,否则返回false*/ /*注意这里用==来进行比较,也就是说如果经过转换相等的,也会返回true var arr = [1,2,3,4,5]; alert(arr.include("3")); */ include: function(object) { var found = false; this.each(function(value) { if (value == object) { found = true; throw $break; } }); return found; }, /*把数组分为指定大小的子数组,如果不够的话,用指定的fillwith进行填充*/ /* var arr = [1,2,3]; arr.inGroupsOf(2,0); 返回结果为[[1,2],[3,0]] */ inGroupsOf: function(number, fillWith) { fillWith = fillWith === undefined ? null : fillWith; return this.eachSlice(number, function(slice) { while(slice.length < number) slice.push(fillWith); return slice; }); }, /*inject,用法和ruby里面的inject一模一样*/ /*此处的interator需要三个参数function(memo,value,index){...}*/ /* var arr = [1,2,3,4,5]; alert(arr.inject(0,function(memo,value){ return memo+value; } )); */ inject: function(memo, iterator) { this.each(function(value, index) { memo = iterator(memo, value, index); }); return memo; }, /* 对数组中所有元素调用指定的方法,参数为方法名和参数列表 var arr = [1,2,3,4,5]; alert(arr.invoke("succ").inspect()); */ invoke: function(method) { var args = $A(arguments).slice(1); return this.map(function(value) { return value[method].apply(value, args); }); }, /*找到最大的元素,如果iterator为一个函数,则比较经过此函数处理后的每一个元素,若不定义iterator, 则直接找到最大的元素并返回*/ /*这里有个问题,如果数组中包含一个NaN,或者undefined,或者null,或者iterator的返回值不小心为NaN/undefined/null了,那么最大值岂不是成NaN/undefined/null了?*/ max: function(iterator) { var result; this.each(function(value, index) { value = (iterator || Prototype.K)(value, index); if (result == undefined || value >= result) result = value; }); return result; }, min: function(iterator) { var result; this.each(function(value, index) { value = (iterator || Prototype.K)(value, index); if (result == undefined || value < result) result = value; }); return result; }, /*对数组项进行分组,经过iterator处理后返回true的分为一组,false的分为一组*/ /*结果为一个包含数组的数组*/ partition: function(iterator) { var trues = [], falses = []; this.each(function(value, index) { ((iterator || Prototype.K)(value, index) ? trues : falses).push(value); }); return [trues, falses]; }, /*取出每一个元素的property属性的值,返回一个包含这些值的数组*/ /*Luciano学英语: pluck: [ pl?k ] n. 勇气,猛拉,动物内脏 v. 摘,猛拉,拔 词形变化: 名词:plucker 动词过去式:plucked 过去分词:plucked 现在分词:plucking 第三人称单数:plucks */ pluck: function(property) { var results = []; this.each(function(value, index) { results.push(value[property]); }); return results; }, /*和ruby里面用法一样,结果iterator处理,返回false的元素将不会包含在返回的数组中*/ /* var arr = [1,2,3,4,5]; arr.reject(function(value){ return(value>2 && value<5) }).each(function(value){alert(value);}); */ reject: function(iterator) { var results = []; this.each(function(value, index) { if (!iterator(value, index)) results.push(value); }); return results; }, /*sortBy传入一个函数,此参数不能省略,用来处理数组中的每一个元素,并且将处理结果保存起来,用来作为排序的依据(升序排列)*/ /*luciano学英语*/ /* criteria: [ krai'ti?ri? ] n. 标准 例句与用法: 1. What are the criteria for deciding (ie How do we decide) who gets the prize? 评定获奖者以什麽作标准? 2. An experiment was made to check up on the reliability of certain criteria. 已经进行了一项实验以检查某些标准的可靠性。 3. Success in making money is not always a good criterion of success in life. 能挣钱并不一定是衡量人生幸福的可靠标准. */ sortBy: function(iterator) { return this.map(function(value, index) { return {value: value, criteria: iterator(value, index)}; }).sort(function(left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }).pluck('value'); }, /*toArray = map = collect()*/ toArray: function() { return this.map(); }, zip: function() { var iterator = Prototype.K, args = $A(arguments); if (typeof args.last() == 'function') iterator = args.pop(); var collections = [this].concat(args).map($A); return this.map(function(value, index) { return iterator(collections.pluck(index)); }); }, size: function() { return this.toArray().length; }, inspect: function() { return '#<Enumerable:' + this.toArray().inspect() + '>'; } } Object.extend(Enumerable, { map: Enumerable.collect, find: Enumerable.detect, select: Enumerable.findAll, member: Enumerable.include, entries: Enumerable.toArray }); /* $A方法将返回一个数组*/ /*如果对象有toArray()方法的话会直接调用对象的toArray方法*/ var $A = Array.from = function(iterable) { if (!iterable) return []; if (iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i = 0, length = iterable.length; i < length; i++) results.push(iterable[i]); return results; } } /*因为js是脚本语言,所以后设置的会覆盖先前设置的*/ if (Prototype.Browser.WebKit) { $A = Array.from = function(iterable) { if (!iterable) return []; if (!(typeof iterable == 'function' && iterable == '[object NodeList]') && iterable.toArray) { return iterable.toArray(); } else { var results = []; for (var i = 0, length = iterable.length; i < length; i++) results.push(iterable[i]); return results; } } } Object.extend(Array.prototype, Enumerable); /*reverse是js Array对象的内部方法*/ if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; /*nnd原来_each方法也是扩展上去的*/ Object.extend(Array.prototype, { _each: function(iterator) { for (var i = 0, length = this.length; i < length; i++) iterator(this[i]); }, /*敢情这样就能清空数组元素,长见识了*/ clear: function() { this.length = 0; return this; }, first: function() { return this[0]; }, last: function() { return this[this.length - 1]; }, /*压缩数组,剔除其中所有值为null的元素*/ compact: function() { return this.select(function(value) { return value != null; }); }, /*同ruby中的flatten用法一致*/ /* var arr=[[1,2],3,[5,6]]; alert(arr.flatten().inspect()); 输出结果[1,2,3,5,6] */ flatten: function() { return this.inject([], function(array, value) { return array.concat(value && value.constructor == Array ? value.flatten() : [value]); }); }, /*得到一个不包含参数中指定元素的新数组*/ /* var arr=[1,2,3,4,5,6]; alert(arr.without(2,3,6).inspect(); 输出结果为[1,4,5] */ without: function() { var values = $A(arguments); return this.select(function(value) { return !values.include(value); }); }, /*得到指定元素在数组中的索引*/ indexOf: function(object) { for (var i = 0, length = this.length; i < length; i++) if (this[i] == object) return i; return -1; }, reverse: function(inline) { return (inline !== false ? this : this.toArray())._reverse(); }, reduce: function() { return this.length > 1 ? this : this[0]; }, uniq: function(sorted) { return this.inject([], function(array, value, index) { if (0 == index || (sorted ? array.last() != value : !array.include(value))) array.push(value); return array; }); }, /*这个太巧妙了,经典*/ clone: function() { return [].concat(this); }, size: function() { return this.length; }, inspect: function() { return '[' + this.map(Object.inspect).join(', ') + ']'; }, toJSON: function() { var results = []; this.each(function(object) { var value = Object.toJSON(object); if (value !== undefined) results.push(value); }); return '[' + results.join(', ') + ']'; } }); Array.prototype.toArray = Array.prototype.clone; /*就是ruby中的%w方法*/ /* alert($w(1 2 3 4 5)); 输出结果为[1,2,3,4,5] */ function $w(string) { string = string.strip(); return string ? string.split(/\s+/) : []; } if (Prototype.Browser.Opera){ Array.prototype.concat = function() { var array = []; for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); for (var i = 0, length = arguments.length; i < length; i++) { if (arguments[i].constructor == Array) { for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) array.push(arguments[i][j]); } else { array.push(arguments[i]); } } return array; } } /*哈希类型的构造函数*/ var Hash = function(object) { if (object instanceof Hash) this.merge(object); else Object.extend(this, object || {}); }; /*扩展了Hash类型,添加了toQueryString()和toJSON方法*/ /*usage: var hash = new Hash({name:'jacobo',age:17}); alert(hash.toQueryString()); 输出结果name=jacobo&age=17 */ Object.extend(Hash, { toQueryString: function(obj) { var parts = []; parts.add = arguments.callee.addPair; this.prototype._each.call(obj, function(pair) { if (!pair.key) return; var value = pair.value; if (value && typeof value == 'object') { if (value.constructor == Array) value.each(function(value) { parts.add(pair.key, value); }); return; } parts.add(pair.key, value); }); return parts.join('&'); }, /*usage: var hash = new Hash({name:'jacobo',age:17}); alert(hash.toJSON()); 输出结果{"name":"jacobo","age":"17"} */ toJSON: function(object) { var results = []; this.prototype._each.call(object, function(pair) { var value = Object.toJSON(pair.value); if (value !== undefined) results.push(pair.key.toJSON() + ': ' + value); }); return '{' + results.join(', ') + '}'; } }); Hash.toQueryString.addPair = function(key, value, prefix) { key = encodeURIComponent(key); if (value === undefined) this.push(key); else this.push(key + '=' + (value == null ? '' : encodeURIComponent(value))); } /*把Enumerable中的方法mixin到Hash.prototype中去*/ Object.extend(Hash.prototype, Enumerable); Object.extend(Hash.prototype, { /*给Hash.prototype添加_each方法,在Enumerable中,实际上each方法方法内部调用的就是_each方法*/ _each: function(iterator) { for (var key in this) { var value = this[key]; if (value && value == Hash.prototype[key]) continue; var pair = [key, value]; pair.key = key; pair.value = value; iterator(pair); } }, keys: function() { return this.pluck('key'); }, values: function() { return this.pluck('value'); }, /*利用Enumerable的inject方法对两个哈希进行合并 inject: function(memo, iterator) { this.each(function(value, index) { memo = iterator(memo, value, index); }); return memo; } */ merge: function(hash) { return $H(hash).inject(this, function(mergedHash, pair) { mergedHash[pair.key] = pair.value; return mergedHash; }); }, /*可以传入多个参数,会从object中删除相应的属性 如果传入一个参数,会返回此参数对应属性的值, 如果传入多个参数,会返回包含这些参数对应属性值的数组*/ /* var hash = new Hash({name:'jacobo',age:17,sex:"male",hobit:['football','rock']}); alert(hash.remove("age","hobit").inspect()); 弹出提示[17,['football','rock']] */ remove: function() { var result; for(var i = 0, length = arguments.length; i < length; i++) { var value = this[arguments[i]]; if (value !== undefined){ if (result === undefined) result = value; else { if (result.constructor != Array) result = [result]; result.push(value) } } delete this[arguments[i]]; } return result; }, toQueryString: function() { return Hash.toQueryString(this); }, inspect: function() { return '#<Hash:{' + this.map(function(pair) { return pair.map(Object.inspect).join(': '); }).join(', ') + '}>'; }, toJSON: function() { return Hash.toJSON(this); } }); function $H(object) { if (object instanceof Hash) return object; return new Hash(object); }; // Safari iterates over shadowed properties /*为了消除浏览器的差异,针对于Safari浏览器重新定义了_each方法,主要差别是Safari中的对象可能存在同名的key*/ /*这段代码就是为了进行测试 function() { var i = 0, Test = function(value) { this.key = value }; Test.prototype.key = 'foo'; for (var property in new Test('bar')) i++; return i > 1; }() */ if (function() { var i = 0, Test = function(value) { this.key = value }; Test.prototype.key = 'foo'; for (var property in new Test('bar')) i++; return i > 1; }()) Hash.prototype._each = function(iterator) { var cache = []; for (var key in this) { var value = this[key]; if ((value && value == Hash.prototype[key]) || cache.include(key)) continue; cache.push(key); var pair = [key, value]; pair.key = key; pair.value = value; iterator(pair); } }; /*ObjectRange对象,看起来非常类似ruby中的Range*/ /*ObjectRange操作的对象必须实现了succ()方法*/ ObjectRange = Class.create(); Object.extend(ObjectRange.prototype, Enumerable); Object.extend(ObjectRange.prototype, { /*exclusive指示是否包含下边界(总是包含上边界)*/ initialize: function(start, end, exclusive) { this.start = start; this.end = end; this.exclusive = exclusive; }, _each: function(iterator) { var value = this.start; while (this.include(value)) { iterator(value); value = value.succ(); } }, include: function(value) { if (value < this.start) return false; if (this.exclusive) return value < this.end; return value <= this.end; } }); /*ObjectRange的快捷命名*/ var $R = function(start, end, exclusive) { return new ObjectRange(start, end, exclusive); }