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一样,每种语言的代码都有些性能方面的小常识。
(有空可以看看 我总结的js方面你可能不是特别清楚的小知识 我总结的js性能优化的小知识 )
回到正题
下面来说说。。。
trim方法的各种不同版本(13种不同方法实现)
/* *@desc:去掉首尾空格 */ //版本1: function trim(str) { return str.replace(/^ss*/, '').replace('/ss*$/', ''); } //版本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(/Ss*$/) + 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*((?:[Ss]*S)?)s*$/, '$1'); } //版本9:使用懒惰匹配 function trim(str) { return str.replace(/^s*([Ss]*?)s*$/, '$1'); } //版本10:速度最快 function trim(str) { var whitespace = ' fx0bxa0u2000u2001u2002u2003u2004u2005u2006u2007u2008u2009u200au200bu2028u2029u3000'; 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(/^ss*/, ''), ws = /s/, i = str.length; while (ws.test(str.charAt(--i))) { return str.slice(0, i + 1); } } //版本13:仅次于版本10 function 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.pow function pad(target, n) { return ((1 << n).toString(2) + target).slice(-n); } //版本5:toFixed function 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-zd])([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(/<script[^>]*>([Ss]*?)</script>/img, ''); } /* *@desc: 将字符串经过html转义得到适合在页面上显示的内容 */ function escapeHTML(target) { return target.repeat(/&/g, '&') .repeat(/</g, '<') .repeat(/>/g, '>') .repeat(/"/g, '"') .repeat(/'/g, '''); } /* *@desc: 将字符串中的html实体字符还原为对应字符 */ function unescapeHTML(target) {// return target.repeat(/</g, '<') .repeat(/>/g, '>') .repeat(/"/g, '"') .repeat(/&/g, '&') .repeat(/&#([d]+);/g, function ($0, $1) { return String.fromCharCode(parseInt($1, 10)); }); } /* *@desc: 将字符串安全格式化为正则表达式的源码 */ function escapeRegExp(target) { return target.replace(/([-.*+?^${}()|[]/\])/g, '\$1'); } /* *@desc: 为目标字符串添加软换行 */ function wbr(target) { return String(target).replace(/(?:<[^>]+>)|(?:&#?[0-9a-z]{2,6};)|(.{1})/gi, '$&<wbr>').replace('/><wbr>/g', '>'); } /* *@desc: 格式化 */ function format(str, object) { var array = Array.prototype.slice.call(arguments, 1); return str.replace(/\?#{([^{}]+)}/gm, function (match, name) { if (match.charAt(0)=='\') { return match.slice(1); } var index = Number(name); if (index>=0) { return array[index]; } if (object&&object[name]!=void 0) { return object[name]; } return ''; }); }
顺便说说上述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<n;i++){' + body.replace('_', '((i in this ) && fn.call(scope ,this[i],i,this))') + '}' + ret;; return Function("fn,scope", fun); } /* *@desc:将数组中的元素依次传入一个函数中运行 */ Array.prototype.forEach = iterator('', '_', ''); /* *@desc:将数组中的元素依次传入一个函数中运行 将返回值为ture的那个元素放入新数组中返回 */ Array.prototype.filter = iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'); /* *@desc:收集、将数组中的元素依次传入一个函数中运行 然后把它们的返回值组成一个新数组返回 */ Array.prototype.map = iterator('r=[],', 'r[i]=_', 'return r'); /* *@desc:只有数组中的元素有一个元素满足条件则返回true */ Array.prototype.some = iterator('', 'if(_) return true', 'return false'); /* *@desc:只有数组中的元素都满足条件才返回true */ Array.prototype.every = iterator('', 'if(!_) return false', 'return true'); /* *@desc:归化操作,将数组中的元素归化为一个简单的数值 */ Array.prototype.reduce = function (fn,lastResult,scope) { if (this.length == 0) { return lastResult; } var i = lastResult != undefined ? 0 : 1; var result = lastResult != undefined ? lastResult : this[0]; for (var i = this.length; i < n; i++) { result = fn.call(scope, result, this[i], i, this); } return result; } /* *@desc:功能类似于reduce 但是从后遍历 */ Array.prototype.reduceRight = function (fn,lastResult,scope) { var array = this.concat().reverse(); return array.reduce(fn, lastResult, scope); }
常用的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框架设计》中第三章--数组的扩展与修复进行整理的。