接上一節迭代器的話題,在打造underscore系列的第三篇中,咱們引入了迭代器的感念,並實現了reduce, map, times等經常使用的迭代器方法,因爲篇幅過長,咱們將其餘迭代器的實現放在了這一小節中。算法
_.each 和 _.foreach 方法本質上是定義的同一個方法,在打造屬於本身的underscore系列 ( 一 )的框架原理中,咱們對 _.each的方法作了實現,其中call改變this指向的代碼咱們是這樣實現的。數組
if (_.isArray(target)) {
var length = target.length;
for (; i < length; i++) {
callback.call(target, target[i], i);
}
} else {
for (key in target) {
callback.call(target, key, target[key]);
}
}
複製代碼
而在系列三小節內容中,咱們優化了this指向這部分代碼,具體封裝爲optimizeCb 函數,詳見打造屬於本身的underscore系列 ( 三 ),這裏不作重複贅述。通過迭代器優化後,each的實現以下, 其中迭代器參數類型爲3,即 func.call(context, value, index)
bash
// 遍歷 數組 對象
_.each = _.forEach = function (target, callback, context) {
iteratee = optimizeCb(callback, context, 3) // 迭代器優化
var key, i = 0;
if (_.isArray(target)) {
var length = target.length;
for (; i < length; i++) {
iteratee(target[i], i);
}
} else {
for (key in target) {
iteratee(key, itarget[key])
}
}
}
複製代碼
與原生js中some的方法相同,some的迭代器會遍歷元素,同時進行真值檢測,一旦有元素經過真值檢測,則返回true。所以,咱們首先須要瞭解什麼是真值檢測? 簡單的理解,真值檢測就是條件判斷,即將檢測的值轉換Boolean類型,判斷轉換後的值是否爲true。而在條件判斷中,咱們作了一個總結,除了 undefined, null, false, NaN, '', 0, -0
,其餘值都會轉換爲true, 包括空對象。eg框架
if(null) {console.log('null')}
if(0) {console.log(2)}
if('222'){ console.log('string')}
if(false) { console.log(false)}
if(NaN) { console.log('nan')}
if(undefined) { console.log('undefined')}
if('') { console.log('空')}
if(-0) { console.log('-0')}
if({}) { console.log('object')}
// 'string', 'object'
複製代碼
一樣遍歷的list能夠爲對象或者數組,所以相似的_some的實現能夠類比以前的例子。ide
_.some = _.any = function(list, iteratee, context) {
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
for(var index = 0; index < length; index++ ) {
var currentKey = keys? keys[index] : index;
if(currentKey) return true
}
return false
}
複製代碼
some方法和map方法在使用方式上的相同點在於,函數的第二個參數iteratee不單單侷限於函數,還能夠不傳值,或者傳遞對象等,所以一樣的處理方式咱們能夠用前一節總結的回調函數進行優化。函數
// 系列的第三節總結的cb方法
var cb = function (iteratee, context, args) {
if (iteratee == null) return _.identity;
if (_.isFunction(iteratee)) return optimizeCb(iteratee, context, args); //optimizeCb優化迭代器
if (_.isObject(iteratee) && !_.isArray(value)) return _.matcher(value);
//其餘
}
_.some = _.any = function(list, iteratee, context) {
predicate = cb(iteratee, context, 3)
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
for(var index = 0; index < lengths; index++ ) {
var currentKey = keys? keys[index] : index;
if(predicate(list[currentKey], currentKey, list)) return true // 只要某一項的真值檢測經過,則中止遍歷,返回true,全部都不經過時,結果才爲false
}
return false
}
複製代碼
和some方法相反,every方法會遍歷list並進行真值檢測,只有當全部檢測值返回true時,結果纔會返回true。有了some實現爲基礎,every方法的實現顯得格外簡單post
_.every = _.all = function(list, iteratee, context) {
predicate = cb(iteratee, context, 3)
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
for(var index = 0; index < lengths; index++ ) {
var currentKey = keys? keys[index] : index;
if(!predicate(list[currentKey], currentKey, list)) return false;// 當某一項真值檢測不經過時,則中止遍歷,返回false,只有全部值都經過時,結果才爲true
}
return true
}
複製代碼
filter方法一樣會對list進行遍歷,並對每一個遍歷元素進行真值判斷,和every,some不一樣的是,filter會將全部經過真值檢測的元素組成一個新的數組返回。所以,只要簡單的處理真值判斷後的語句便可完成filter的功能優化
_.filter = _.select = function(list, iteratee, context) {
predicate = cb(iteratee, context, 3)
var keys = !isArrayLike(list) && _.keys(list);
var lengths = (keys || list).length;
var results = [];
for(var index = 0; index < lengths; index++ ) {
var currentKey = keys? keys[index] : index;
if(predicate(list[currentKey], currentKey, list)) results.push(list[currentKey])
}
return results
}
複製代碼
至此,underscore中關於迭代器相關方法的應用場景和實現思路基本介紹完畢。咱們知道,迭代器在平常代碼邏輯編寫中必不可少,且使用頻率較高。熟練掌握迭代器的應用場景並深刻理解其中實現思路有利於現實複雜的業務功能的實現。ui