Why underscore
最近開(kāi)始看 underscore.js 源碼迷扇,并將 underscore.js 源碼解讀 放在了我的 2016 計(jì)劃中薯酝。
閱讀一些著名框架類(lèi)庫(kù)的源碼,就好像和一個(gè)個(gè)大師對(duì)話(huà),你會(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 源碼解讀項(xiàng)目地址 https://github.com/hanzichi/underscore-analysis
- 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 源碼解讀系列文章 https://github.com/hanzichi/underscore-analysis/issues
歡迎圍觀(guān)~ (如果有興趣,歡迎 star & watch~)您的關(guān)注是樓主繼續(xù)寫(xiě)作的動(dòng)力
Main
趁著今天是工作日的最后一天番甩,把源碼解讀部分 Object Functions 更新完畢。
如果你有心届搁,可能就會(huì)發(fā)現(xiàn)樓主之前的解讀系列文章說(shuō)的都是 Object 上的擴(kuò)展方法缘薛,也就是源碼中 Object Functions 部分窍育。underscore 為 5 種類(lèi)型添加了擴(kuò)展方法,分別是 Object -> Array -> Collection -> Function -> Utility宴胧,這也正是樓主的源碼解讀順序(并不是源碼順序)漱抓。其中,Object 上的擴(kuò)展方法多達(dá) 38 個(gè)恕齐,方法多并不代表代碼多乞娄,比如類(lèi)型檢測(cè),兩行代碼就可以搞定好幾個(gè)方法显歧,而上一篇中說(shuō)的 _.isEqual 方法仪或,卻要百來(lái)行去實(shí)現(xiàn)。今天是 Object Functions 部分的最后一篇士骤,我們來(lái)看看樓主認(rèn)為的幾個(gè)沒(méi)被解讀過(guò)的但是卻有意思的方法的源碼范删。(其實(shí)很多方法使用簡(jiǎn)單,實(shí)現(xiàn)也非常簡(jiǎn)單拷肌,有興趣的同學(xué)可以自己扒下源碼)
_.pick
首先來(lái)看看 _.pick 方法到旦,該方法傳入一個(gè)對(duì)象,然后刪選對(duì)象的鍵值對(duì)巨缘,返回一個(gè)對(duì)象副本添忘。
直接來(lái)看例子:
_.pick({name: 'moe', age: 50, userid: 'moe1'}, 'name', 'age');
=> {name: 'moe', age: 50}
_.pick({name: 'moe', age: 50, userid: 'moe1'}, ['name', 'age']);
=> {name: 'moe', age: 50}
_.pick({name: 'moe', age: 50, userid: 'moe1'}, function(value, key, object) {
return _.isNumber(value);
});
=> {age: 50}
一目了然,第一個(gè)參數(shù) obj 是對(duì)象若锁,第二個(gè)參數(shù)可以是一系列的 key 值搁骑,也可以是數(shù)組(數(shù)組中含 key),也可以是迭代函數(shù)拴清,我們根據(jù) key 值靶病,或者迭代函數(shù)來(lái)過(guò)濾 obj 中的鍵值對(duì),返回新的對(duì)象副本口予。
如果讓我來(lái)設(shè)計(jì)娄周,估計(jì)會(huì)根據(jù)參數(shù)來(lái)判斷類(lèi)型,然后寫(xiě)幾個(gè) if-else沪停,每個(gè) if-else 分支里的內(nèi)容毫無(wú)關(guān)聯(lián)煤辨。但是 underscore 的寫(xiě)法簡(jiǎn)直美妙,將幾種情況轉(zhuǎn)為了一種木张。
// 如果第二個(gè)參數(shù)是函數(shù)
if (_.isFunction(oiteratee)) {
keys = _.allKeys(obj);
iteratee = optimizeCb(oiteratee, context);
}
首先 if-else 是不可避免的众辨,如果傳入的第二個(gè)參數(shù)是 function,那么就是傳入迭代函數(shù)了舷礼,根據(jù) context(this)返回新的迭代函數(shù)(optimizeCb 我以后會(huì)講鹃彻,就是規(guī)定了迭代函數(shù)中的 this 指向,不是很重要妻献,這里可以選擇性忽略)蛛株。
如果第二個(gè)參數(shù)不是函數(shù)团赁,則后面的 keys 可能是數(shù)組,也可能是連續(xù)的幾個(gè)并列的參數(shù)谨履。這里我們要用到 underscore 中另一個(gè)重要的內(nèi)部方法欢摄,flatten,它的作用是將嵌套的數(shù)組展開(kāi)笋粟,這個(gè)方法我以后會(huì)分析怀挠,這里知道它的作用就可以了。
else {
// 如果第二個(gè)參數(shù)不是函數(shù)
// 則后面的 keys 可能是數(shù)組
// 也可能是連續(xù)的幾個(gè)并列的參數(shù)
keys = flatten(arguments, false, false, 1);
// 也轉(zhuǎn)為 predicate 函數(shù)判斷形式
// 將指定 key 轉(zhuǎn)化為 predicate 函數(shù)
iteratee = function(value, key, obj) { return key in obj; };
obj = Object(obj);
}
也轉(zhuǎn)為和傳入迭代函數(shù)一樣的形式害捕,就可以用一個(gè)方法判斷了绿淋,而且 keys 變量在兩種情況下的意義是不同的,真的非常巧妙吨艇。這點(diǎn)令我思考良多躬它,很多時(shí)候的代碼冗余,其實(shí)大概是自己代碼能力太差了吧东涡,打死我也想不到這樣做冯吓。
_.create
_.create 方法非常簡(jiǎn)單,根據(jù)你給的原型(prototype)疮跑,以及一些 own properties组贺,構(gòu)造新的對(duì)象返回。
舉個(gè)簡(jiǎn)單的例子:
var Person = function() {};
Person.prototype = {
show: function() {
alert(this.name);
}
};
var me = _.create(Person.prototype, {name: 'hanzichi'});
console.log(me);
// Object {name: "hanzichi"}
// name: "hanzichi"
// __proto__: Object
// show: function()
其實(shí) me 變量就是一個(gè)擁有 name 作為 own properties祖娘,且用 Person 函數(shù)構(gòu)造的對(duì)象失尖。
如果瀏覽器支持 ES5,我們可以用 Object.create():
var Person = function() {};
Person.prototype = {
show: function() {
alert(this.name);
}
};
var me = Object.create(Person.prototype);
_.extendOwn(me, {name: 'hanzichi'});
console.log(me);
如果不支持 ES5渐苏,我們可以新定義一個(gè)構(gòu)造函數(shù)掀潮,將該構(gòu)造函數(shù)的 prototype 賦值為已知的 prototype 變量,然后用 new 運(yùn)算符來(lái)獲取實(shí)例:
var Person = function() {};
Person.prototype = {
show: function() {
alert(this.name);
}
};
var _Person = function() {};
_Person.prototype = Person.prototype;
var me = new _Person();
_.extendOwn(me, {name: 'hanzichi'});
console.log(me);
undercore 的實(shí)現(xiàn)思路也大抵如此琼富。
_.tap
本來(lái)打算把 _.tap 講掉仪吧,突然覺(jué)得跟鏈?zhǔn)秸{(diào)用一起講比較好,挖個(gè)坑鞠眉。
小結(jié)
關(guān)于 Objects Function 部分的源碼剖析就到這了薯鼠,具體這部分代碼可以參考 https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L901-L1269。雖然說(shuō)看完了這部分代碼械蹋,但是要真正理解消化還是需要時(shí)間的出皇,我只能說(shuō)自己理解了 90% 左右,歡迎探討交流哗戈。
接下去應(yīng)該會(huì)著手看 Array 擴(kuò)展方法相關(guān)代碼郊艘,繼續(xù)為大家整理有意思的東西,各種奇淫怪巧,敬請(qǐng)期待暇仲。