/* *@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.html
事實上業餘時間一直都在關注一些js性能方面的東西,跟.net同樣,每種語言的代碼都有些性能方面的小常識。前端
(有空能夠看看 我總結的js方面你可能不是特別清楚的小知識 我總結的js性能優化的小知識 )正則表達式
回到正題api
下面來講說。。。數組
/* *@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:僅次於版本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相反,下面說說爲字符串的某一端填充字符串,其實最多見的場景就是日期中的月份前補零緩存
/* *@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字符串方法吧性能優化
/* *@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(/<script[^>]*>([\S\s]*?)<\/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);
運行結果以下圖:app
你們都知道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); }
/* *@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框架設計》中第三章--數組的擴展與修復進行整理的。