网上看到的一个ruby风格的javascript类库,其通过扩展了javascript原生对象,添加了一些类ruby风格的方法,写得很优秀。
Object.prototype.clone = function(deepClone) { var result = new this.constructor() for (var property in this) { if (deepClone && typeof(this[property]) == 'object') { result[property] = this[property].clone(deepClone) } else { result[property] = this[property] } } return (result)}Object.prototype.extend = function(other) { if (!this.mixins) this.mixins = [] this.mixins.push(other) for (var property in other) if (!this.hasOwnProperty(property)) this[property] = other[property]}Object.prototype.cmp = function(other) { if (this < other) return (-1) if (this > other) return (+1) return (0)}Object.prototype.valuesAt = function() { var obj = this return (arguments.toArray().map(function(index) { return (obj[index]) }))}Object.prototype.toArray = function() { if (!this.length) throw ("Can't convert") var result = [] for (var i = 0; i < this.length; i++) result.push(this[i]) return (result)}Object.prototype.hash = function() { return (this.toSource().hash())}Object.prototype.instanceOf = function(klass) { return (this.constructor == klass)}Object.prototype.isA = Object.prototype.kindOf = function(klass) { if (this.instanceOf(klass)) return (true) if (this["mixins"] != undefined && this.mixins.includes(klass)) return (true) return (false)}Object.prototype.methods = function() { var result = [] for (var property in this) if (typeof(this[property]) == "function") result.push(property) return (result)}Object.prototype.respondTo = function(method) { return (this.methods().includes(method))}Object.prototype.send = function(method) { var rest = arguments.toArray().last(-1) if (!this.respondTo(method)) throw ("undefined method") return (this[method].apply(this, rest))}Object.prototype.instanceEval = function(code) { if (code.isA(Function)) return (code.apply(this)) else return (eval(code.toString()))}Number.prototype.times = function(block) { for (var i = 0; i < this; i++) block(i)}Number.prototype.upto = function(other, block) { for (var i = this; i <= other; i++) block(i)}Number.prototype.downto = function(other, block) { for (var i = this; i >= other; i--) block(i)}Number.prototype.towards = function(other, block) { var step = this.cmp(other) for (var i = this; i !== other - step; i -= step) block(i)}Number.prototype.succ = function() { return (this + 1)}Number.prototype.pred = function() { return (this - 1)}Number.prototype.chr = function() { return (String.fromCharCode(this))}enumerable = new Object()enumerable.eachWindow = function(window, block) { if (!window.isA(Range)) window = range(0, window) elements = [], pushed = 0 this.each(function(item, index) { elements.push(item) pushed += 1 if (pushed % window.rend == 0) { start = [0, window.start - window.rend + pushed].max() end = [0, window.rend + pushed].max() block(elements.fetch(xrange(start, end)), index) } })}enumerable.collect = enumerable.map = function(block) { var result = [] this.each(function(item, index) { result.push(block(item, index)) }) return (result)}enumerable.toArray = enumerable.entries = function() { return (this.map(function(item) { return (item) }))}enumerable.inject = function(firstArg) { var state, block, first = true if (arguments.length == 1) { block = firstArg } else { state = firstArg block = arguments[1] } this.each(function(item, index) { if (first && typeof(state) == "undefined") state = item, first = false else state = block(state, item, index) }) return (state)}enumerable.find = enumerable.detect = function(block) { var result, done this.each(function(item, index) { if (!done && block(item, index)) { result = item done = true } }) return (result)}enumerable.findAll = enumerable.select = function(block) { return (this.inject([], function(result, item, index) { return (block(item, index) ? result.add(item) : result) }))}enumerable.grep = function(obj) { return (this.findAll(function(item) { return (obj.test(item)) }))}enumerable.reject = function(block) { return (this.select(function(item, index) { return (!block(item, index)) }))}enumerable.compact = function() { return (this.select(function(item) { return (typeof(item) != "undefined") }))}enumerable.nitems = function() { return (this.compact().length)}enumerable.sortBy = function(block) { return (this.map(function(item, index) { return ([block(item, index), item]) }).sort(function(a, b) { return (a[0].cmp(b[0])) }).map(function(item) { return (item[1]) }))}enumerable.all = function(block) { return (this.findAll(block).length == this.length)}enumerable.any = function(block) { return (typeof(this.find(block)) != "undefined")}enumerable.includes = function(obj) { return (this.any(function(item) { return (item === obj) }))}enumerable.index = function(obj) { var result this.find(function(item, index) { if (obj == item) { result = index return (true) } else { return (false) } }) return (result)}enumerable.uniq = function() { return (this.inject([], function(result, item) { return (result.includes(item) ? result : result.add(item)) }))}enumerable.max = function(block) { if (!block) block = function(a, b) { return (a.cmp(b)) } return (this.sort(block).last())}enumerable.min = function(block) { if (!block) block = function(a, b) { return (a.cmp(b)) } return (this.sort(block).first())}enumerable.partition = function(block) { var positives = [], negatives = [] this.each(function(item, index) { if (block(item, index)) positives.push(item) else negatives.push(item) }) return ([positives, negatives])}enumerable.zip = function() { var ary = arguments.toArray() ary.unshift(this) return (ary.transpose())}enumerable.flatten = function(depth) { if (depth == undefined) depth = -1 if (!depth) return (this) return (this.inject([], function(result, item) { var flatItem = item.respondTo("flatten") ? item.flatten(depth - 1) : [item] return (result.merge(flatItem)) }))}Array.fromObject = function(obj) { if (!obj.length) throw ("Can't convert") var result = [] for (var i = 0; i < obj.length; i++) result.push(obj[i]) return (result)}Array.prototype.transpose = function() { var result, length = -1 this.each(function(item, index) { if (length < 0) { /* first element */ length = item.length result = Array.withLength(length, function() { return (new Array(this.length)) }) } else if (length != item.length) { throw ("Element sizes differ") } item.each(function(iitem, iindex) { result[iindex][index] = iitem }) }) return (result)}Array.withLength = function(length, fallback) { var result = [null].mul(length) result.fill(fallback) return (result)}Array.prototype.each = function(block) { for (var index = 0; index < this.length; ++index) { var item = this[index] block(item, index) } return (this)}Array.prototype.extend(enumerable)Array.prototype.isEmpty = function() { return (this.length == 0)}Array.prototype.at = Array.prototype.fetch = function(index, length) { if (index.isA(Range)) { var end = index.rend + (index.rend < 0 ? this.length : 0) index = index.start length = end - index + 1 } if (length == undefined) length = 1 if (index < 0) index += this.length var result = this.slice(index, index + length) return (result.length == 1 ? result[0] : result)}Array.prototype.first = function(amount) { if (amount == undefined) amount = 1 return (this.at(xrange(0, amount)))}Array.prototype.last = function(amount) { if (amount == undefined) amount = 1 return (this.at(range(-amount, -1)))}Array.prototype.store = function(index) { var length = 1, obj arguments = arguments.toArray() arguments.shift() if (arguments.length == 2) length = arguments.shift() obj = arguments.shift() if (!obj.isA(Array)) obj = [obj] if (index.isA(Range)) { var end = index.rend + (index.rend < 0 ? this.length : 0) index = index.start length = end - index + 1 } if (index < 0) index += this.length this.replace(this.slice(0, index).merge(obj).merge(this.slice(index + length))) return (this)}Array.prototype.insert = function(index) { var values = arguments.toArray().last(-1) if (index < 0) index += this.length + 1 return (this.store(index, 0, values))}Array.prototype.update = function(other) { var obj = this other.each(function(item) { obj.push(item) }) return (obj)}Array.prototype.merge = Array.prototype.concatArray.prototype.add = function(item) { return (this.merge([item]))}Array.prototype.clear = function() { var obj = this this.length.times(function(index) { delete obj[index] }) this.length = 0}Array.prototype.replace = function(obj) { this.clear() this.update(obj)}Array.prototype.mul = function(count) { var result = [] var obj = this count.times(function() { result = result.merge(obj) }) return (result)}Array.prototype.fill = function(value) { var old_length = this.length var obj = this this.clear() var block if (typeof(value) != "function") block = function() { return (value) } else block = value old_length.times(function(i) { obj.push(block(i)) })}Array.prototype.removeAt = function(targetIndex) { var result = this[targetIndex] var newArray = this.reject(function(item, index) { return (index == targetIndex) }) this.replace(newArray) return (result)}Array.prototype.remove = function(obj) { this.removeAt(this.index(obj))}Array.prototype.removeIf = function(block) { this.replace(this.reject(block))}function Range(start, end, excludeEnd) { this.begin = this.start = start this.end = end this.excludeEnd = excludeEnd this.rend = excludeEnd ? end.pred() : end this.length = this.toArray().length}function range(start, end) { return (new Range(start, end))}function xrange(start, end) { return (new Range(start, end, true))}Range.prototype.toString = function() { return ("" + this.start + (this.excludeEnd ? "..." : "..") + this.end)}Range.prototype.each = function(block) { var index = 0 this.start.towards(this.rend, function(i) { return (block(i, index++)) })}Range.prototype.extend(enumerable)Range.prototype.includes = function(item) { return (this.start.cmp(item) == -1 && this.rend.cmp(item) == +1)}function Hash(defaultBlock) { this.defaultBlock = defaultBlock this.keys = [] this.values = [] this.length = 0}Hash.fromArray = function(array) { var result = new Hash() array.each(function(item) { var key = item[0], value = item[1] result.store(key, value) }) return (result)}Hash.prototype.at = Hash.prototype.fetch = function(key, block) { var result if (this.hasKey(key)) result = this["item_" + key.hash()] else { if (block) result = block(key) else result = defaultBlock(key) } return (result)}Hash.prototype.store = function(key, value) { this.keys.push(key) this.values.push(value) this.length++ return (this["item_" + key.hash()] = value)}Hash.prototype.toA = function() { return (this.keys.zip(this.values))}Hash.prototype.isEmpty = function() { return (this.length == 0)}Hash.prototype.has = Hash.prototype.includes = Hash.prototype.hasKey = function(key) { return (hasOwnProperty("item_" + key.hash()))}Hash.prototype.hasValue = function(value) { return (this.values.includes(value))}Hash.prototype.each = function(block) { this.toA().each(function(pair) { return (block(pair[1], pair[0])) })}Hash.prototype.extend(enumerable)Hash.prototype.merge = function(other) { other.each(function(value, key) { this.store(key, value) })}Hash.prototype.remove = function(key) { var valueIndex = this.keys.index(key) var value = this.values[valueIndex] this.keys.remove(key) this.values.removeAt(valueIndex) delete(this["item_" + key.hash()]) this.length-- return ([key, value])}Hash.prototype.removeIf = function(block) { this.each(function(value, key) { if (block(value, key)) this.remove(key) })}Hash.prototype.shift = function() { return (this.remove(this.keys[0]))}Hash.prototype.clear = function() { var obj = this this.length.times(function() { obj.shift() })}Hash.prototype.replace = function(obj) { this.clear() this.merge(obj)}Hash.prototype.invert = function() { return (Hash.fromArray(this.map(function(value, key) { return ([value, key]) })))}Hash.prototype.rehash = function() { var result = new Hash(this.defaultBlock) this.each(function(value, key) { result.store(key, value) }) this.replace(result)}function MatchData(matches, str, pos) { this.matches = matches, this.string = str this.begin = this.position = pos this.match = matches[0] this.captures = matches.slice(1) this.end = pos + this.match.length this.length = matches.length this.preMatch = str.substr(0, pos) this.postMatch = str.substr(this.end)}MatchData.prototype.toString = function() { return (this.match)}MatchData.prototype.at = function(index) { return (this.matches.at(index))}MatchData.prototype.toArray = function() { return (this.matches)}RegExp.prototype.match = function(str) { var matches if (matches = this.exec(str)) { var pos = str.search(this) return (new MatchData(matches, str, pos)) }}String.prototype.clone = function() { return (new String(this))}String.prototype.each = function(block) { this.split("\n").each(block)}String.prototype.extend(enumerable)String.prototype.toArray = function() { return (this.split("\n"))}String.prototype.towards = function(other, block) { var item = this while (item.cmp(other) <= 0) { block(item) item = item.succ() }}String.prototype.hash = function() { var result = 0 this.split("").each(function(item) { result += item.charCodeAt(0) result += (result << 10) result ^= (result >> 6) }) result += (result << 3) result ^= (result >> 11) result += (result << 15) return (result)}String.prototype.chars = function() { return (this.split(""))}String.prototype.at = String.prototype.fetch = function(index, length) { if (index.isA(Range)) { var end = index.rend + (index.rend < 0 ? this.length : 0) index = index.start length = end - index + 1 } if (length == undefined) length = 1 if (index < 0) index += this.length return (this.substr(index, length))}String.prototype.store = String.prototype.change = function(index) { var length = 1, obj arguments = arguments.toArray() arguments.shift() if (arguments.length == 2) length = arguments.shift() obj = arguments.shift() if (index.isA(Range)) { var end = index.rend + (index.rend < 0 ? this.length : 0) index = index.start length = end - index + 1 } if (index < 0) index += this.length return (this.substr(0, index) + obj + this.substr(index + length))}String.prototype.reverse = function() { return (this.split("").reverse().join(""))}String.prototype.scan = function(pattern) { var str = this, result = [], oldPos = -1, match, offset = 0 while (match = pattern.match(str)) { if (match.end == match.begin) throw ("Can't have null length matches with scan()") var newMatch = new MatchData(match.matches, match.string, match.position + offset) result.push(newMatch) str = match.postMatch offset += match.toString().length } return (result)}String.prototype.sub = function(what, by, global) { var block = typeof(by) == "function" ? by : function() { return (by) } var matches = this.scan(what), result = this, offset = 0 if (!global && !by.global) matches = matches.slice(0, 1) matches.each(function(match) { var replacement = block(match) offset += replacement.length - match.toString().length result = result.change(match.begin + offset, match.toString().length, replacement) }) return (result)}String.prototype.gsub = function(what, by) { return (this.sub(what, by, true))}String.prototype.tr = function(from, to) { var map = Hash.fromArray(from.chars().zip(to.chars())) return (this.chars().map(function(chr) { return (map.includes(chr) ? map.fetch(chr) : chr) }).join(""))}String.prototype.mul = function(other) { var result = "", str = this other.times(function() { result += str }) return (result)}String.prototype.isUpcase = function() { return (this == this.upcase())}String.prototype.isDowncase = function() { return (this == this.downcase())}String.prototype.isCapitalized = function() { return (this.fetch(0).isUpcase() && this.fetch(range(1, -1)).isDowncase())}String.prototype.upcase = String.prototype.toUpperCaseString.prototype.downcase = String.prototype.toLowerCaseString.prototype.capitalize = function() { return (this.fetch(0).upcase() + this.fetch(range(1, -1)).downcase())}String.prototype.swapcase = function() { return (this.chars().map(function(chr) { if (chr.isUpcase()) return (chr.downcase()) if (chr.isDowncase()) return (chr.upcase()) return (chr) }).join(""))}String.prototype.ord = function() { return (this.charCodeAt(0))}String.prototype.isEmpty = function() { return (this.length == 0)}String.prototype.succ = function() { if (this.isEmpty()) return (this) /* numerics */ if (/^\d+$/.test(this)) return ((Number(this) + 1).toString()) /* just one character */ if (this.length == 1) { /* letters */ if (/[A-Za-z]/.test(this)) { var lastLetter = this.isUpcase() ? 'Z' : 'z' var firstLetter = this.isUpcase() ? 'A' : 'a' return ((this == lastLetter) ? firstLetter.mul(2) : (this.ord() + 1).chr()) } else { return (this == (-1).chr() ? 0.0.chr().mul(2) : (this.ord() + 1).chr()) } /* multiple characters */ } else { var result = this for (var index = this.length; index >= 0; index--) { var chr = this.at(index) if (chr.succ().length == 1 || index == 0) return (result.change(index, chr.succ())) else result = result.change(index, chr.succ().at(-1)) } }}String.prototype.ljust = function(length, fill) { if (!fill) fill = " " if (fill.length > 1) throw ("TODO: Make fills with length > 1 work.") return (this + fill.mul(length / fill.length - this.length))} |