先過濾掉underscore內(nèi)部各個工具函數(shù)的具體邏輯崭歧,只看源碼庫本身有什么內(nèi)容隅很。
構造函數(shù)
underscore有兩種調(diào)用方式:
- 風格對象
_.map([1, 2, 3], function(n){ return n * 2; });
- 函數(shù)風格
_([1, 2, 3]).map(function(n){ return n * 2; });
_
是一個函數(shù)對象,api中的函數(shù)全都掛載到_
上率碾,實現(xiàn)_.func
// 使用立即執(zhí)行函數(shù)
(function () {
// 定義全局對象
var root = typeof self == 'object' && self.self === self && self ||
typeof global == 'object' && global.global === global && global ||
this ||
{};
// 省略...
// 創(chuàng)建_對象
var _ = function(obj) {
// 如果參數(shù)是underscore的一個實例叔营,就直接返回該參數(shù)
if (obj instanceof _) return obj;
// 對應_(xxx)調(diào)用,如果new實例不是_的實例所宰,就返回實例化_對象
if (!(this instanceof _)) return new _(obj);
// 并將數(shù)據(jù)對象包裝在實例對象上
this._wrapped = obj;
};
// 省略...
//注冊在全局對象
root._=_;
})();
mixin
上一步中绒尊,我們創(chuàng)建了underscore實例,只能支持_.func
調(diào)用仔粥,如果要支持_(obj).func
婴谱,同時還要將func
注冊在實例的prototype
上。試想下躯泰,如果每聲明一個函數(shù)谭羔,就要綁定一次,那得用多…
在underscore中斟冕,使用mixin
將自定義函數(shù)添加到Underscore對象口糕。
// 用于返回排序后的數(shù)組,包含所有的obj中的函數(shù)名磕蛇。
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
_.mixin = function(obj) {
// 遍歷obj的函數(shù)景描,綁定在原型上
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
// this._wrapped作為第一個參數(shù)傳遞十办,其他用戶傳遞的參數(shù)放在后面。
var args = [this._wrapped];
push.apply(args, arguments);
return chainResult(this, func.apply(_, args));
};
});
return _;
};
// 執(zhí)行混入
_.mixin(_);
大致流程是:
- 獲取當前實例上注冊的函數(shù)數(shù)組
- 遍歷數(shù)組將函數(shù)注冊在實例原型上
- 將
_(args).func(argument)
參數(shù)進行合并
_
自身定義一系列函數(shù)超棺,通過_.mixin()
綁定在了_.prototype
上向族,提高了代碼的復用度。
鏈式調(diào)用
類似于 Java Stream 流式編程
在Javascript中棠绘,數(shù)據(jù)可以像是在管道中流通件相,我們稱之為,聲明式編程/鏈接式調(diào)用氧苍。
data.filter(...).unique(...).map(...)
既然要滿足鏈接式調(diào)用(Chaining)語法夜矗,需要滿足兩個條件
- 前一次調(diào)用返回數(shù)據(jù)對象,用來給下一個函數(shù)調(diào)用提供數(shù)據(jù)來源
- 返回調(diào)用當前函數(shù)的對象让虐,以保證可以調(diào)用下個函數(shù)
可能會想到紊撕,在每個函數(shù)結尾return this;
,對赡突,能用但是沒必要对扶,除了要手動添加外,我們也無法關閉鏈式惭缰。
在underscore中浪南,使用的是可選式實現(xiàn)鏈接編程,使用.chain()
來開啟鏈式調(diào)用漱受,然后使用.value()
獲取函數(shù)的最終值络凿。
關鍵函數(shù):
// 開啟鏈式調(diào)用
_.chain = function (obj) {
//返回一個封裝的對象. 在封裝的對象上調(diào)用方法會返回封裝的對象本身
var instance = _(obj)
instance._chain = true //檢測對象是否支持鏈式調(diào)用
return instance
}
// 輔助函數(shù):鏈式中轉(zhuǎn)
// 鏈式調(diào)用 將數(shù)據(jù)對象封裝在underscore實例中
// 非鏈式調(diào)用 返回數(shù)據(jù)對象
var chainResult = function(instance, obj) {
return instance._chain ? _(obj).chain() : obj;
};
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
_.prototype.value = function() {
return this._wrapped;
};
總結
此次學習目標不是為了學習api,而是通過將其設計思想轉(zhuǎn)換為自己的昂羡。通過以上幾點喷众,我們可以大概實現(xiàn)一個簡化版的underscore,
簡化版:
(function (root) {
var _ = function (obj) {
if (!(this instanceof _)) {
return new _(obj)
}
this.warp = obj
}
_.unique = function (arr, callback) {
var result = []
var item
for (var i = 0; i < arr.length; i++) {
item = callback ? callback(arr[i]) : arr[i]
if (result.indexOf(item) === -1) {
result.push(item)
}
}
return result
}
// 獲取對象上的函數(shù)
_.functions = function (obj) {
var result = []
for (let key in obj) {
result.push(key)
}
return result
}
// 執(zhí)行鏈式操作
_.chain = function (obj) { //數(shù)據(jù)源
var instance = _(obj)
instance._chain = true //檢測對象是否支持鏈式調(diào)用
return instance
}
//輔助函數(shù) 將數(shù)據(jù)包裝為underscore實例
var ret = function (instance, obj) {
if (instance._chain) {
instance.warp = obj
return instance
}
return obj
}
_.map1 = function (obj) {
obj.push('123', 'hello')
return obj
}
// 關閉鏈式調(diào)用 返回數(shù)據(jù)本身
_.prototype.value = function () {
return this.warp
}
_.each = function (arr, callback) {
var i = 0
for (; i < arr.length; i++) {
callback.call(arr, arr[i])
}
//console.log(arr)
}
// 檢測靜態(tài)方法 name 存放在數(shù)組中
// 遍歷數(shù)組 給_.prototype進行注冊
_.mixin = function (obj) {
_.each(_.functions(obj), function (key) {
var func = obj[key]
//console.log(key)
_.prototype[key] = function () {
//console.log(this.warp) //數(shù)據(jù)源
//console.log(arguments) //callback
// 進行參數(shù)合并
var args = [this.warp]
Array.prototype.push.apply(args, arguments)
return ret(this, func.apply(this, args))
}
})
}
_.mixin(_)
root._ = _
})(this)
調(diào)用:
console.log(_.unique([1,2,3,1,2,3,'a','A'],function (item) {
// 過濾大小寫
return typeof item ==='string'?item.toLowerCase():item
}))
console.log(_([4,5,6,4,5,6,'b','B']).chain().unique(function (item) {
// 過濾大小寫
return typeof item ==='string'?item.toLowerCase():item
}).map1().value())