前言
一直以來想寫一篇關于es5中新增數組的使用方法與源碼實現的文章,拖了挺久了,趁著這夜深人靜,大腦清醒,又困意不在的時刻寫下來。也許有人會問,現如今es6都大行其道了,還學es5是不是有些過時了,??,溫故而知新,可以為師矣,而且我們是要自己實現這些方法嘛,知其然還要知其所以然,不光要會用,還要知道為什么是這樣用噠。
新增方法預覽
es5中給js的數組增添了許多實用的方法,利用這些方法可以幫助我們更加快速方便的寫js代碼,然后蛋疼的是低版本ie肯定是不支持的,所以..................自己動手豐衣足食。讓我們一步步看下如何使用與實現這些方法。
- forEach
- map
- filter
- some
- every
- indexOf
- lastIndexOf
- reduce
- reduceRight
forEach
這個方法作用是啥咧,就是循環,遍歷。比如一般我們在for循環做這樣的事的時候如下。
var arr = [1, 2, 3, 4, 5, 6];
for (var i = 0, len = arr.length; i < len; i++) {
console.log(arr[i], i, arr);
}
如果用forEach我們應該如何做呢?
var arr = [1, 2, 3, 4, 5, 6];
arr.forEach(function (e, i, array) {
console.log(e, i, array)
})
是不是覺得不用寫for循環了,瞬間逼格都高了
forEach函數中的回調函數支持三個參數,
1、數組的值
,2、值的索引
,3、數組本身
。這樣的調用方式是不是和jQuery中的$.each很像? 其實不然,jQuery和forEach回調函數的第一個和第二個參數正好是反著來的。
看看對比
var arr = [1, 2, 3, 4, 5];
// forEach
arr.forEach(function (e, i, array) {
console.log(e, i, array);
})
// output
1 0 [1, 2, 3, 4, 5]
2 1 [1, 2, 3, 4, 5]
3 2 [1, 2, 3, 4, 5]
4 3 [1, 2, 3, 4, 5]
5 4 [1, 2, 3, 4, 5]
// $.each
$.each(arr, function (i, e, array) { // 測試的時候發現array是undefined,查了文檔也發現沒有第三個參數
console.log(i, e, array);
})
// output
0 1 undefined
1 2 undefined
2 3 undefined
3 4 undefined
4 5 undefined
接著我們來看一下forEach的第二個參數,這個參數決定第一個回調函數的內部this指向
var arr = [1, 2, 3, 4, 5];
// 默認情況下,第二個參數不傳入時
arr.forEach(function (e, i, array) {
console.log(e, i, array, this);
})
// output
1 0 [1, 2, 3, 4, 5] window
2 1 [1, 2, 3, 4, 5] window
3 2 [1, 2, 3, 4, 5] window
4 3 [1, 2, 3, 4, 5] window
5 4 [1, 2, 3, 4, 5] window
// 傳入參數
arr.forEach(function (e, i, array) {
console.log(e, i, array, this);
}, {name: 'qianlong'})
// output
1 0 [1, 2, 3, 4, 5] {name: 'qianlong'}
2 1 [1, 2, 3, 4, 5] {name: 'qianlong'}
3 2 [1, 2, 3, 4, 5] {name: 'qianlong'}
4 3 [1, 2, 3, 4, 5] {name: 'qianlong'}
5 4 [1, 2, 3, 4, 5] {name: 'qianlong'}
最后接下來我們自己實現一下這個方法
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeArray = ObjPro.forEach;
Array.prototype.forEach = nativeArray || function (callBack, ctx) {
if (typeof callBack != 'function') return;
for (var i =0, len = this.length; i < len; i++) {
if (hasOwn.call(this, i)) {
callBack.call(ctx, this[i], i, this);
}
}
}
map
map是干嘛的! 其最主要的作用就是將原數組按照一定的規則映射成一個新的數組。再將其返回,
注意是返回一個新的數組,而不是將原數組直接改變
使用方式和forEach類似,也是接受一個回調函數,一個改變內部this指向的對象。
map
array.map(callback,[ thisObject])
callback
var arr = [1, 2, 3, 4, 5];
arr.map(function(value, index, array) {
});
舉個栗子
var arr = [1, 2, 3, 4, 5];
var newArr = arr.map(function (e, i, array) {
return 'hello ' + e;
})
// output
["hello 1", "hello 2", "hello 3", "hello 4", "hello 5"] // newArr
[1, 2, 3, 4, 5] // arr
注意上面的return,如果我們不寫return會怎樣呢?
var arr = [1, 2, 3, 4, 5];
var newArr = arr.map(function (e, i, array) {
'hello ' + e;
})
// output
[undefined, undefined, undefined, undefined, undefined] // newArr
[1, 2, 3, 4, 5] // arr
這一堆的undefined是啥情況,還記得一個函數執行完,如果沒有顯示的返回值,會返回什么嗎? 沒錯 就是undefined
,這就是原因所在,等會通過源碼,你就會更加明白。
最后我們自己實現一下map這個方法
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeMap = ObjPro.map;
Array.prototype.map = nativeMap || function (callBack, ctx) {
if (typeof callBack != 'function') return;
var returnArr = [];
for(var i = 0, len = this.length; i < len; i++) {
returnArr.push(callBack.call(ctx, this[i], i, this)); // 這就是為什么回調函數沒有返回值的情況下會得到一堆的undefined值,他將回調函數的返回值push到了一個數組里面,當你沒有顯示的返回值的時候,自然push進去的就是undefined了
}
return returnArr;
}
filter
接下來是
filter
,篩選,過濾的意思,給你一個數組,用一些你制定的條件,對其中的值進行過濾,最后得到你想要的新的數組。基本用法和map差不多
array.filter(callback,[ thisObject]);
但是和map也有差別的地方,filter需要你在callback處返回弱等于true
的值,才會將原數組中篩選出的值返回給你。
舉個栗子
var arr = [0, 1, 2, 3, 4, 5];
var newArr = arr.filter(function (e, i, array) {
return e;
})
// output
[1, 2, 3, 4, 5] // newArr
var newArr2 = arr.filter(function (e, i, array) {
if (e >= 2) return true;
})
// ouput
[2, 3, 4, 5] // newArr2
當然最后還有第二個參數改變內部this指向的參數可選,默認是window對象,你也可以傳一個對象進去, 最后我們自己來實現一下這個api
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeFilter = ObjPro.filter;
Array.prototype.filter = nativeFilter || function (callBack, ctx) {
if (typeof callBack != 'function') return;
var returnArr = [];
for(var i = 0, len = this.length; i < len; i++) {
if (callBack.call(ctx, this[i], i, this)) {
returnArr.push(this[i]);
}
}
return returnArr;
}
some vs every
some與接下里的every正好相對,
some
是只要數組中的某個值,符合你給定的判斷條件就返回true,而every
則是數組中的所有值都符合你給定的判斷條件的時候才會返回true,否則就返回false,也就是說兩個方法最后得到的都是true or false
舉個栗子
var arr = [0, 1, 2, 3, 4, 5];
var result = arr.some(function (e, i, array) {
if (e === 3) {return true};
});
// output
true // result;
var arr = [0, 1, 2, 3, 4, 5];
var result2 = arr.every(function (e, i, array) {
if (e > 3) {return true};
});
// output
false // result;
some 和 every使用起來非常簡單,接下來我們自己實現一把
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeSome = ObjPro.some,
nativeEvery = ObjPro.every;
// some
Array.prototype.some = nativeSome || function (callBack, ctx) {
if (typeof callBack != 'function') return;
var resultValue = false;
for(var i = 0, len = this.length; i < len; i++) {
if (resultValue) {
break;
}
resultValue = !!callBack.call(ctx, this[i], i, this);
}
return resultValue;
}
// every
Array.prototype.every = nativeEvery || function (callBack, ctx) {
if (typeof callBack != 'function') return;
var resultValue = true;
for (var i = 0, len = this.length; i < len; i++) {
if (!resultValue) {
break;
}
resultValue = !!callBack.call(ctx, this[i], i, this);
}
return resultValue;
}
indexOf
數組的indexOf方法和字符串的indexOf用法非常類似,
array.indexOf(searchElement[, fromIndex])
,針對給定的要查找的值,和開始查找的位置(可選),返回整數索引值。
舉個例子
var arr = [0, 1, 2, 3, 4, 5];
arr.indexOf(1) // 1
arr.indexOf(3, 'qianlong') // 3 因為給定的開始索引值不能轉化成數字,所以還是從0位置開始搜索
arr.indexOf(3, 4) // -1
arr.indexOf(3, '4') // -1
arr.indexOf('3') // -1 // 判斷條件是強 3 !== '3' => -1
實現代碼
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeIndexOf = ObjPro.indexOf;
Array.prototype.indexOf = nativeIndexOf || function (searchElement, fromIndex) {
var returnIndex = -1,
fromIndex = fromIndex * 1 || 0;
for (var i = fromIndex, len = this.length; i < len; i++) {
if (searchElement === this[i]) {
returnIndex = i;
break;
}
}
return returnIndex;
}
lastIndexOf
數組的lastIndexOf方法和字符串的lastIndexOf用法非常類似,
array. lastIndexOf(searchElement[, fromIndex])
,針對給定的要查找的值,和開始查找的位置(可選),返回整數索引值。與indexOf不同的地方在于,它是從后往前查找。默認開始查找的位置是array.length - 1
舉個栗子
var arr = [0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0];
arr.lastIndexOf(1) // 9
arr.lastIndexOf(3, 'qianlong') // -1 這里和indexOf不一樣,傳入的值不能轉化為數字將得到-1
arr.lastIndexOf(3, 4) // 3
arr.lastIndexOf(3, '4') // 3
arr.lastIndexOf('3') // -1 // 判斷條件是強 3 !== '3' => -1
源碼實現
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeLastIndexOf = ObjPro.lastIndexOf;
Array.prototype.lastIndexOf = nativeLastIndexOf || function (searchElement, fromIndex) {
var len = this.length,
returnIndex = -1,
fromIndex = fromIndex * 1 || len - 1;
for (var i = fromIndex; i > -1; i -= 1) {
if (this[i] === searchElement){
returnIndex = i;
break;
}
}
return returnIndex;
}
reduce
reduce 相對es5中數添加的其他方法都復雜一些,我們可以通過栗子來看一下這個api怎么使用。首先基本參數如下
array.reduce(callback[, initialValue])
,接收一個回調函數,一個初始化的值initialValue
。其中callback參數分別是初始化的值initialValue
,如果沒有傳入initialValue
,則默認是數組的第一項。第二個及其后面的參數分別是當前值
,索引
,數組本身
var arr = [0, 1, 2, 3, 4, 5],
sum = arr.reduce(function (init, cur, i, array) {
return init + cur;
});
//output
sum // 15
我們來看一下上面的執行過程是怎樣的。
第一回合
// 因為initialValue沒有傳入所以回調函數的第一個參數為數組的第一項
init = 0;
cur = 1;
=> init + cur = 1;
第二回合
init = 1;
cur = 2;
=> init + cur = 3;
第三回合
init = 3;
cur = 3;
=> init + cur = 6;
第四回合
init = 6;
cur = 4;
=> init + cur = 10;
第五回合
init = 10;
cur = 5;
=> init + cur = 15;
最后得到結果15
那么我們如何自己實現一個reduce呢?
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeReduce = ObjPro.reduce;
Array.prototype.reduce = nativeReduce || function (callBack, initialVal) {
if (typeof callBack != 'function') return;
var init = initialVal,
i = 0;
if (init === void (0)) {
init = this[0];
i = 1;
}
for (i, len = this.length; i < len; i++) {
if (hasOwn.call(this, i)) {
init = callBack(init, this[i], i, this);
}
}
return init;
}
reduceRight
reduceRight基本用法與reduce類似,好比indexOf與lastIndexOf,不同之處在于它是從最右邊的值開始計算的。我們直接去看源碼怎么實現吧
var ObjPro = Object.prototype,
hasOwn = ObjPro.hasOwnProperty,
nativeReduceRight = ObjPro.reduceRight;
Array.prototype.reduceRight = nativeReduceRight || function (callBack, initialVal) {
if (typeof callBack != 'function') return;
var init = initialVal,
len = this.length,
i = len - 1;
if (init === void(0)) {
init = this[len - 1];
i -= 1;
}
for (i; i > -1; i -=1) {
if (hasOwn.call(this, i)) {
init = callBack(init, this[i], i, this);
}
}
return init;
}
結尾
終于寫完了,斷斷續續快寫了兩天,歡迎大家看了以后提一些意見,函數實現的不一定都對,肯定有一些問題的地方,歡迎大家指正。
最后把代碼放到github上面了
各位大大,請讓我打個小廣告。??????