js将字符串重复N次的repeat方法的8个版本
/**@desc: 将一个字符串重复自身N次*///版本1:利用空数组的join方法function repeat(target, n) { return (new Array(n + 1)).join(target);}//版本2:之所以要创建一个带length属性的对象 是因为要调用数据的原型方法,需要指定call的第一个参数为类数组对象//类数组对象的必要条件是其length属性的值为非负数function repeat(target, n) { return Array.prototype.join.call({ length: n + 1 }, target);}//版本3:利用闭包将类数组对象与数组原型的join方法缓存起来var repeat = (function () { var join = Array.prototype.join, obj = {}; return function (target, n) { obj.length = n + 1; return join.call(obj, target); }})();//版本4:使用二分法function repeat(target, n) { var s = target, total = []; while (n > 0) { if (n % 2 == 1) { total[total.length] = s;//如果是奇数 } if (n == 1) { break; } s += s; n = n >> 1;//相当于将n除以2取其商,或者说是开2次方 } return total.join('');}//版本5:版本4的改良版本function repeat(target, n) { var s = target, total = ""; while (n > 0) { if (n % 2 == 1) { total += s; } if (n == 1) { break; } s += s; n = n >> 1;//相当于将n除以2取其商,或者说是开2次方 } return total;}//版本6:版本4的变样版本 免去创建数组与使用join方法 但在循环中创建字符串比要求的还长 所以...function repeat(target, n) { var s = target, c = s.length * n; do { s += s; } while (n = n >> 1); s = s.substring(0, c); return s;}//版本7:版本5的优化版本function repeat(target, n) { if (n == 1) { return target; } var s = repeat(target, Math.floor(n / 2)); s += s; if (n % 2) { s += target; } return s;}//版本8:反例function repeat(target, n) { return (n <= 0) ? "" : target.concat(repeat(target, --n));}
大家可以猜猜哪个运行速度最快。事实上应该是版本5.
事实上业余时间一直都在关注一些js性能方面的东西,跟.net一样,每种语言的代码都有些性能方面的小常识。
(有空可以看看 )
回到正题
下面来说说。。。
trim方法的各种不同版本(13种不同方法实现)
/**@desc:去掉首尾空格*///版本1:function trim(str) { return str.replace(/^\s\s*/, '').replace('/\s\s*$/', '');}//版本2:比版本1稍微慢些function trim(str) { return str.replace(/^\s+/, '').replace('/\s+$/', '');//比版本1慢的原因在于它最先假设至少存在一个空白符}//版本3:运用等巧妙的function trim(str) { return str.substring(Math.max(str.search(/\S/), 0), str.search(/\S\s*$/) + 1);}//版本4:jQuery类库就是使用这种方法 但是它相对之前三个都要慢些function trim(str) { return str.replace(/^\s+|\s+$/g, '');}//版本5:function trim(str) { str = str.match(/\S+(?:\s+\S+)*/);//使用非捕获性分组(?:expr) return str ? str[0] : '';}//版本6:效率挺差function trim(str) { return str.replace(/^\s*(\S*(\s+\S+)*)\s*$/, '$1');}//版本7:比版本6来说使用了非捕获性分组function trim(str) { return str.replace(/^\s*(\S*(?:\s+\S+)*)\s*$/, '$1');}//版本8:效果秒杀function trim(str) { return str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');}//版本9:使用懒惰匹配function trim(str) { return str.replace(/^\s*([\S\s]*?)\s*$/, '$1');}//版本10:速度最快function trim(str) { var whitespace = '\n\r\t\f\x0b\xa0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000'; for (var i = 0; i < str.length; i++) { if (whitespace.indexOf(str.charCodeAt(i)) === -1) { str = str.substring(i); break; } } for (i = str.length - 1; i >= 0; i--) { if (whitespace.indexOf(str.charCodeAt(i)) === -1) { str = str.substring(0, i + 1); break; } } return whitespace.indexOf(str.charAt(0)) === -1 ? str : '';}//版本11:function trim(str) { str = str.replace('^\s+', ''); for (var i = str.length - 1 ; i >= 0; i--) { if (/\S/.test(str.charAt(i))) { str = str.substring(0, i + 1); break; } } return str;}//版本12:function trim(str) { var str = str.replace(/^\s\s*/, ''), ws = /\s/, i = str.length; while (ws.test(str.charAt(--i))) { return str.slice(0, i + 1); }}//版本13:仅次于版本10function trim(str) { var m = str.length; for (var i = -1; str.charCodeAt(++i) <= 32;) for (var j = 0; j > i && str.charCodeAt(j) <= 32; j--) { return str.slice(i, j + 1); }}
与trim相反,下面说说为字符串的某一端填充字符串,其实最常见的场景就是日期中的月份前补零
pad:这字符串的某一端填充字符串
/**@desc:给字符串的某一端填充字符串*///版本1:创建数组来放置填充物,然后再在右边起截取function pad(target, n) { var zero = new Array(n).join('0'), str = zero + target, result = str.substr(-n); return result;}//版本2:function pad(target, n) { return Array((n + 1) - target.toString().split('').length).join('0') + target;}//版本3:二进制法function pad(target, n) { return (Math.pow(10, n) + "" + target).slice(-n);}//版本4:Math.powfunction pad(target, n) { return ((1 << n).toString(2) + target).slice(-n);}//版本5:toFixedfunction pad(target, n) { return (0..toFixed(n) + target).slice(-n);}//版本6:创建一个超大数,在常规情况下截不完function pad(target, n) { return (1e20 + '' + target).slice(-n);}//版本7:质朴长存法function pad(target, n) { var len = target.toString().length; while (len < n) { target = '0' + target; len++; } return target;}//版本8:支持更多参数function pad(target, n, filling, right, radix) { var num = target.toString(radix || 10); filling = filling || '0'; while (num.length < n) { if (!right) { num = filling + num; } else { num += filling; } } return num;}
取得字符串所有字节的长度
大家都知道,一个中文字符占两个字节,而一个英文字符只占一个字符,所以在前端就会免不了做字符长度的校验。
/**@desc:取得字符串所有字节的长度*///版本1:传统常规作法function byteLen(target) { var byteLength = target.length, i = 0; for (; i< target.length; i++) { if (target.charCodeAt(i)>255) { byteLength++; } } return byteLength;}//版本8:使用正则//param:fix 默认为2 可传入转换长度function byteLen(target,fix) { fix = fix ? fix : 2; var str = new Array(fix + 1).join('-'); return target.replace(/[^\x00-\xff]/g, str).length;}
再来说说我们最常使用的js字符串方法吧
常用的js字符串方法
/**@desc: 判断一个字符串是否包含另一个字符串*/function contains(target, str, separator) { return separator ? (separator + target + separator).indexOf(separator + str + separator) > -1 : target.indexOf(str) > -1;}/**@desc: 判断目标字符串是否位于原字符串的开始之处*@param:ignorecase 是否忽略大小写*/function startsWith(target, str, ignorecase) { var start_str = target.substr(0, str.length); return ignorecase ? start_str.toLowerCase() === str.toLowerCase() : start_str === str;}/**@desc: 判断目标字符串是否位于原字符串的末尾之处*/function endsWith(target, str, ignorecase) { var end_str = target.substr(0, str.length); return ignorecase ? end_str.toLowerCase() === str.toLowerCase() : end_str === str;}/**@desc: 对字符串进行截断处理,当超过限定长度,默认添加三个点号或者...*/function truncate(target, length, truncation) { length = length || 30; truncation = truncation === void (0) ? '...' : truncation; return target.length > length ? target.slice(0, length - truncation.length) + truncation : String(target);}/**@desc: 转换为下划线风格*/function underscored(target) { return target.replace(/([a-z\d])([A-Z])/g, '$1_$2').replace(/\-/g, '_').toLowerCase();}/**@desc: 转换为连字符风格*/function dasherize(target) { return underscored(target).replace(/_/g, '-');}/**@desc: 首字母大写*/function capitalize(target) { return target.charAt(0).toUpperCase() + target.substring(1).toLowerCase();}/**@desc: 移除字符串中的html标签*/function stripTags(target) { return String(target || '').replace(/<[^>]+>/g, '');}/**@desc: 移除字符串中所有的script标签*/function stripScripts(target) { return String(target || '').replace(/
顺便说说上述format方法的使用吧
var a = format('Result is #{0},#{1}', 22, 33); console.log(a); var b = format("#{name}is a #{sex}", { name: "Jhon", sex: 'man' }); console.log(b);
运行结果如下图:
js数组方法的扩展
大家都知道js数组没有像字符串一样的indexOf、lastIndexOf等方法,那我们先来造造轮子吧。先来扩展一下吧!
/**@desc:定位操作,返回数组中第一个等于给定参数的元素的索引值*/Array.prototype.indexOf = function (item, index) { var n = this.length, i = ~~index; if (i < 0) { i += n; } for (; i < n; i++) { if (this[i] === item) { return i; } } return -1;}/**@desc:与lastIndex功能类似 不过是从后遍历*/Array.prototype.lastIndexOf = function (item,index) { var n = this.length, i = index == null ? n - 1 : index; if (i<0) { i = Math.max(0, n + i); } for (; i > length; i--) { if (this[i]===item) { return i; } } return -1;}/**@desc:因为forEach、map、filter、some、every这几个方法结构相似 所以... 先造个轮子*/function iterator(vars,body,ret) { var fun = 'for(var ' + vars + 'i=0,n=this.length;i
常用的js数组方法
/**@desc:判定数组是否包含指定目标*/function contains(target, item) { return target.indexOf(item) > -1;}/**@desc:移除数组中指定位置的元素,返回布尔表示成功与否*/function removeAt(target, index) { return !!target.splice(index, 1).length;}/**@desc:移除数组中第一个匹配传参的那个元素*/function remove(target, item) { var index = target.indexOf(item); if (~index) { return removeAt(target, index); } return false;}/**@desc:对数组进行洗牌*/function shuffle(target) { var j, x, i = target.length; for (; i > 0; j = parseInt(Math.random() * i), x = target[--i], target[i] = target[j], target[j] = x) { } return target;}/**@desc:从数组中随机抽选一个元素出来*/function random(target) { return target[Math.floor(Math.random() * target.length)];}/**@desc:对数组进行平坦化处理,返回一个一维新数组*/function flatten(target) { var result = []; target.forEach(function (item) { if (Array.isArray(item)) { result = result.concat(flatten(item)); } else { result.push(item); } }); return result;}/**@desc:对数组进行去重操作,返回一个没有重复元素的新数组*/function unique(target) { var result = []; loop: for (var i = 0, n = target.length; i < n; i++) { for (var x = i + 1; x < n; x++) { if (target[x] === target[i]) { continue loop; } } result.push(target[i]); } return result;}/**@desc:过滤数组中的null和undefined 但不影响原数组*/function compact(target) { return target.filter(function (el) { return el != null; });}/**@desc:取得对象数组的每个元素的指定属性 组成数组返回*/function pluck(target, item) { var result = [], prop; target.forEach(function (item) { prop = item[name]; if (prop != null) { result.push(prop); } }); return result;}/**@desc:根据指定条件(如回调或对象的某个属性)进行分组,构成对象返回*/function groupBy(target, val) { var result = {}; var iterator = $.isFunction(val) ? val : function (obj) { return obj[val]; }; target.forEach(function (value, index) { var key = iterator(value, index); (result[key] || (result[key] = [])).push(value); }); return result;}/**@desc:根据指定条件进行排序*/function sortBy(target, fn, scope) { var array = target.map(function (item, index) { return { el: item, re: fn.call(scope, item, index) }; }).sort(function (left, right) { var a = left.re, b = right.re; return a < b ? -1 : a > b ? 1 : 0; }); return pluck(array, 'el');}/**@desc:对两个数组取并集*/function union(target, array) { return unique(target.concat(array));}/**@desc:对两个数组取交集*/function intersect(target, array) { return target.filter(function (n) { return ~array.indexOf(n); });}/**@desc:对两个数组取差集*/function diff(target, array) { var result = target.slice(); for (var i = 0; i < result.length; i++) { for (var j = 0; j < result.length; j++) { if (result[i] === array[j]) { result.splice(i, 1); i--; break; } } } return result;}/**@desc:返回数组中的最小值(用于数字数组)*/function min(target) { return Math.min.apply(0, target);}/**@desc:返回数组中的最大值(用于数字数组)*/function max(target) { return Math.max.apply(0, target);}
其实还有很多。。。
本文中的方法是根据《JavaScript框架设计》中第三章--数组的扩展与修复进行整理的。