Why underscore
最近開(kāi)始看 underscore.js 源碼氏淑,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中。
閱讀一些著名框架類(lèi)庫(kù)的源碼,就好像和一個(gè)個(gè)大師對(duì)話,你會(huì)學(xué)到很多爽蝴。為什么是 underscore?最主要的原因是 underscore 簡(jiǎn)短精悍(約 1.5k 行)纫骑,封裝了 100 多個(gè)有用的方法蝎亚,耦合度低,非常適合逐個(gè)方法閱讀先馆,適合樓主這樣的 JavaScript 初學(xué)者发框。從中,你不僅可以學(xué)到用 void 0 代替 undefined 避免 undefined 被重寫(xiě)等一些小技巧 煤墙,也可以學(xué)到變量類(lèi)型判斷梅惯、函數(shù)節(jié)流&函數(shù)去抖等常用的方法宪拥,還可以學(xué)到很多瀏覽器兼容的 hack,更可以學(xué)到作者的整體設(shè)計(jì)思路以及 API 設(shè)計(jì)的原理(向后兼容)铣减。
之后樓主會(huì)寫(xiě)一系列的文章跟大家分享在源碼閱讀中學(xué)習(xí)到的知識(shí)她君。
- underscore-1.8.3 源碼全文注釋 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/underscore-1.8.3-analysis.js
- underscore-1.8.3 源碼解讀項(xiàng)目地 https://github.com/hanzichi/underscore-analysis
- underscore-1.8.3 源碼解讀系列文 https://github.com/hanzichi/underscore-analysis/issues
歡迎圍觀~ (如果有興趣,歡迎 star & watch~)您的關(guān)注是樓主繼續(xù)寫(xiě)作的動(dòng)力
類(lèi)型判斷
第一篇跟大家簡(jiǎn)單地聊了下為什么 underscore.js 用 void 0 代替了 undefined葫哗,意外地收到了不錯(cuò)的反響缔刹,有朋友私信我說(shuō)以前還真不知道這回事,也有人催促我趕緊繼續(xù)下一篇解讀文章劣针。今天就跟大家聊一聊 underscore.js 中一些 JavaScript 常用類(lèi)型檢查方法校镐,以及一些工具類(lèi)的判斷方法。
我們先說(shuō)個(gè)老生常談的問(wèn)題捺典,JavaScript 中數(shù)組類(lèi)型的判斷方法鸟廓,事實(shí)上,我在 Javascript中判斷數(shù)組的正確姿勢(shì) 一文中已經(jīng)詳細(xì)分析了各種判斷方式的優(yōu)缺點(diǎn)襟己,并給出了正確的判斷代碼:
function isArray(a) {
Array.isArray ? Array.isArray(a) : Object.prototype.toString.call(a) === '[object Array]';
}
而 underscore 其實(shí)也正是這么做的:
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
// 判斷是否為數(shù)組
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) === '[object Array]';
};
nativeIsArray 正是 ES5 中 Array.isArray 方法肝箱,如果支持則優(yōu)先調(diào)用;而 toString 變量就保存了 Object.prototype.toString稀蟋。
如何判斷對(duì)象煌张?underscore 把類(lèi)型為 function 和 object 的變量都算作對(duì)象,當(dāng)然得除去 null退客。
// Is a given variable an object?
// 判斷是否為對(duì)象
// 這里的對(duì)象包括 function 和 object
_.isObject = function(obj) {
var type = typeof obj;
return type === 'function' || type === 'object' && !!obj;
};
再看 'Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error' 這些類(lèi)型的判斷骏融,其實(shí)都可以用 Object.prototype.toString.call 來(lái)判斷,所以寫(xiě)在了一起:
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
// 其他類(lèi)型判斷
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) === '[object ' + name + ']';
};
});
但是看 isArguments 方法萌狂,在 IE < 9 下對(duì) arguments 調(diào)用 Object.prototype.toString.call档玻,結(jié)果是 [object Object],而并非我們期望的 [object Arguments]茫藏。咋整误趴?我們可以用該元素是否含有 callee 屬性來(lái)判斷,眾所周時(shí)务傲,arguments.callee 能返回當(dāng)前 arguments 所在的函數(shù)凉当。
// Define a fallback version of the method in browsers (ahem, IE < 9), where
// there isn't any inspectable "Arguments" type.
// _.isArguments 方法在 IE < 9 下的兼容
// IE < 9 下對(duì) arguments 調(diào)用 Object.prototype.toString.call 方法
// 結(jié)果是 [object Object]
// 而并非我們期望的 [object Arguments]。
// so 用是否含有 callee 屬性來(lái)判斷
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return _.has(obj, 'callee');
};
}
工具類(lèi)判斷方法
接下來(lái)看下一些常用的工具類(lèi)判斷方法售葡。
判斷一個(gè)元素是否是 DOM 元素看杭,非常簡(jiǎn)單,只需要保證它不為空挟伙,且 nodeType 屬性為 1:
// Is a given value a DOM element?
// 判斷是否為 DOM 元素
_.isElement = function(obj) {
// 確保 obj 不是 null
// 并且 obj.nodeType === 1
return !!(obj && obj.nodeType === 1);
};
如何判斷一個(gè)元素為 NaN楼雹?NaN 其實(shí)是屬于 Number 類(lèi)型,Object.prototype.toString.call(NaN) 返回的是 "[object Number]",而且 NaN 不等于本身贮缅,利用這兩點(diǎn)即可進(jìn)行判斷:
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
// 判斷是否是 NaN
// NaN 是唯一的一個(gè) `自己不等于自己` 的 number 類(lèi)型
_.isNaN = function(obj) {
return _.isNumber(obj) && obj !== +obj;
};
當(dāng)然榨咐,underscore 還有很多其他的有用的工具類(lèi)判斷方法,具體可以看源碼 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L1192-L1263 這部分谴供。
如果您覺(jué)得我分享的東西對(duì)您有所幫助块茁,請(qǐng)關(guān)注我的 Repo https://github.com/hanzichi/underscore-analysis