最近在拜讀只有1700行(含註釋)代碼的Underscore.js 1.9.1,記錄一些東西node
(參考https://underscorejs.org/underscore.js,https://github.com/hanzichi/underscore-analysis)git
/* 1 */ if (a === b) return a !== 0 || 1 / a === 1 / b; /* 2 null undefined */ if (a == null || b == null) return false; /* 3 NaN */ if (a !== a) return b !== b; /* 4 primitive */ var type = typeof a; if (type !== 'function' && type !== 'object' && typeof b != 'object') return false; /* 5 正則 和 String */ return '' + a === '' + b; /* 6 Number */ if (+a !== +a) return +b !== +b; return +a === 0 ? 1 / +a === 1 / b : +a === +b; /* 7 Date Boolean */ return +a === +b; /* 8 Symbol */ var SymbolProto = typeof Symbol !== 'undefined' ? Symbol.prototype : null; return SymbolProto.valueOf.call(a) === SymbolProto.valueOf.call(b);
Object Functions(補)github
// 判斷偶數 var isEven = function(num) { return !(num & 1); };
數組去重數組
/* 1 一一比較 */ function removeSame(arr){ return arr.filter(function(item, index, arr){ return arr.indexOf(item) === index; }); } /* 2 */ [...new Set(arr)] /* 3 */ Array.from(new Set(arr));
// shallow爲false深度展開,當shallow strict都爲true可忽略非數組元素 var flatten = function(input, shallow, strict, output) { output = output || []; var idx = output.length; for (var i = 0, length = getLength(input); i < length; i++) { var value = input[i]; if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) { if (shallow) { var j = 0, len = value.length; while (j < len) output[idx++] = value[j++]; } else { flatten(value, shallow, strict, output); idx = output.length; } } else if (!strict) { output[idx++] = value; } } return output; };
_.compact _.difference _.without(補)app
isNaN = function(obj) { Number.isNaN(Number(obj)); } Number.isNaN = Number.isNaN || function(obj) { return typeof obj === "number" && isNaN(obj); } _.isNaN = function(obj) { return _.isNumber(obj) && isNaN(obj); };
// 優化(傳遞arguments給任何參數,將致使Chrome和Node中使用的V8引擎跳過對其的優化,這也將使性能至關慢) var args = new Array(arguments.length); for(var i = 0; i < args.length; ++i) { args[i] = arguments[i]; }
/* 1 O(n^2)*/ function shuffle(arr) { var result = []; while (arr.length) { var index = ~~(Math.random() * arr.length); // 兩次按位取反,不管正負,去掉小數點後面的數 result.push(arr[index]); arr.splice(index, 1); } return result; } /* 2 O(nlogn)*/ function shuffle(arr) { return arr.sort(function(a, b) { return Math.random() - 0.5; }); } /* 3 Fisher–Yates Shuffle 遍歷數組 將其與以前的元素交換 O(n)*/ function shuffle(arr){ var len = arr.length; var shuffled = Array(len); for(var i=0, rand; i < len; i++){ rand = ~~(Math.random()*(i+1)); if(rand !== i){ shuffled[i] = shuffled[rand]; } shuffled[rand] = arr[i]; } return shuffled; }
if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() { return fToBind.apply(this instanceof fNOP ? this : oThis, // 獲取調用時(fBound)的傳參.bind 返回的函數入參每每是這麼傳遞的 aArgs.concat(Array.prototype.slice.call(arguments))); }; // 維護原型關係 if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); return fBound; }; }
_.throttle = function(func, wait, options) { var timeout, context, args, result; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; var throttled = function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; throttled.cancel = function() { clearTimeout(timeout); previous = 0; timeout = context = args = null; }; return throttled; };
_.debounce = function(func, wait, immediate) { var timeout, result; var later = function(context, args) { timeout = null; if (args) result = func.apply(context, args); }; var debounced = restArguments(function(args) { if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(later, wait); if (callNow) result = func.apply(this, args); } else { timeout = _.delay(later, wait, this, args); } return result; }); debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; return debounced; };