Array.prototype.every
以下代码均来自:MDN
- every函数测试数组的每一个元素是否通过测试,此方法并不一定会遍历全部的元素,它的执行机制是遇到未通过测试的项则立即返回false,如果全部通过测试则返回true
- every函数并不改变原数组
- 空数组对任何检测回调函数都返回true(空数组中所有元素都符合给定的条件,注:因为空数组没有元素)
这个解释还真绕- IE9+支持
- 如果某个索引的值未定义,则这个未定的索引不会被遍历,而且不会影响结果的返回
if (!Array.prototype.every)
{
Array.prototype.every = function(fun /*, thisArg */)
{
'use strict';
//用void 0是为了防止undefined被重写而出现判断不准确的情况
//过滤undefined和null
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
//检测传入的参数类型
if (typeof fun !== 'function')
throw new TypeError();
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++)
{
//用(值,索引,数组)参数来调用回调函数,如果回调函数返回false则立即return false退出
if (i in t && !fun.call(thisArg, t[i], i, t))
return false;
}
//全部通过测试则返回true
return true;
};
}
Array.prototype.some
- some函数也不一定会遍历所有的项,当遇到符合条件的第一个项时就会返回true退出遍历
- some不会改变原数组
- some 遍历的元素的范围在第一次调用 callback. 时就已经确定了。在调用 some 后被添加到数组中的值不会被 callback 访问到。如果数组中存在且还未被访问到的元素被 callback 改变了,则其传递给 callback 的值是 some 访问到它那一刻的值。未被遍历的元素被动态删除后是不会被访问的
- 如果某个索引的值未定义,则这个未定的索引不会被遍历,而且不会影响结果的返回
// Production steps of ECMA-262, Edition 5, 15.4.4.17
// Reference: http://es5.github.io/#x15.4.4.17
if (!Array.prototype.some) {
Array.prototype.some = function(fun/*, thisArg*/) {
'use strict';
//过滤undefined和null
if (this == null) {
throw new TypeError('Array.prototype.some called on null or undefined');
}
//检测传入的参数类型
if (typeof fun !== 'function') {
throw new TypeError();
}
var t = Object(this);
var len = t.length >>> 0;
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++) {
//用(值,索引,数组)参数来调用回调函数,如果回调函数返回true则立即return true退出
if (i in t && fun.call(thisArg, t[i], i, t)) {
return true;
}
}
//如果没有元素通过测试,则返回false
return false;
};
}
//改变数组中的项
[1,2,3,4,5].some(function(v, i, arr){
if(v % 2 === 0){
//被改变的值,会按改变后的值检测
++arr[i + 1];
//后来动态添加的元素项,不会参与测试
arr.push(20)
}
console.log(arr)
return v > 10;
})
/*
VM88:6 (5) [1, 2, 3, 4, 5]
VM88:6 (6) [1, 2, 4, 4, 5, 20]
VM88:6 (7) [1, 2, 4, 5, 5, 20, 20]
VM88:6 (7) [1, 2, 4, 5, 5, 20, 20]
VM88:6 (7) [1, 2, 4, 5, 5, 20, 20]
false
*/
//判断两个数组是否有交集
function isIntersection(arr1, arr2){
return arr1.some(function(v, i, arr1){
return arr2.some(function(vv, ii, arr2){
return v === vv
});
});
}
Array.prototype.find
- find函数也未必会遍历所有的元素项,当遇到符合条件的元素项时,就会立刻返回这个元素项,并不再继续遍历后面的元素,如果遍历完所有的项没有符合要求的值则返回undefined
- find不会改变原来的数组
- IE系列均不支持
- 某个索引上的值未定义也会被遍历当作undefined来处理
- 在第一次调用 callback 函数时会确定元素的索引范围,因此在 find 方法开始执行之后添加到数组的新元素将不会被 callback 函数访问到。如果数组中一个尚未被callback函数访问到的元素的值被callback函数所改变,那么当callback函数访问到它时,它的值是将是根据它在数组中的索引所访问到的当前值。被删除的元素仍旧会被访问到。
此处第三条是mdn上的原话,但是我在尝试之后发现如果先动态的添加一个元素,然后再把此元素动态的删除,这个元素被遍历了,不知道这个是不是bug
终于知道这个问题怎么回事了:这个是不是bug我不清楚,只是知道先删除再添加,它等同于修改了原本索引上对应的值,所以被修改的值后来也被遍历了
// https://tc39.github.io/ecma262/#sec-array.prototype.find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
value: function(predicate) {
// 1. Let O be ? ToObject(this value).
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
//决定以谁的名义调用回调函数
var thisArg = arguments[1];
// 5. Let k be 0.
var k = 0;
// 6. Repeat, while k < len
while (k < len) {
// a. Let Pk be ! ToString(k).
// b. Let kValue be ? Get(O, Pk).
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
// d. If testResult is true, return kValue.
var kValue = o[k];
//如果以(thisArg, kValue, kindex, arr)调用检测函数返回了true则直接返回这个kValue,否则继续循环
if (predicate.call(thisArg, kValue, k, o)) {
return kValue;
}
// e. Increase k by 1.
k++;
}
// 7. Return undefined.
return undefined;
}
});
}
[1,2,3,4,5].find(function(v, i, arr){
if(v % 2 === 0){
//被改变的值,会按改变后的值检测
++arr[i + 1];
//后来动态添加的元素项,不会参与测试
arr.pop();
arr.push(20)
}
console.log(arr)
return v > 10;
})
/*
VM344:9 (5) [1, 2, 3, 4, 5]
VM344:9 (5) [1, 2, 4, 4, 20]
VM344:9 (5) [1, 2, 4, 5, 20]
VM344:9 (5) [1, 2, 4, 5, 20]
VM344:9 (6) [1, 2, 4, 5, 20, 20]
20 这里最后居然输出了20,有点匪夷所思
*/
Array.prototype.findIndex
这个函数和find基本一模一样,只是在最后返回的时候find返回的是vaule,findIndex返回的index(找到第一个满足条件的元素则直接返回这个元素的索引值,如果均不满足则返回-1)
[1,2,3,4,5].find(function(v, i, arr){
if(v % 2 === 0){
//被改变的值,会按改变后的值检测
++arr[i + 1];
//后来动态添加的元素项,不会参与测试
arr.pop();
arr.push(20)
}
console.log(arr)
return v > 10;
})
/*
VM344:9 (5) [1, 2, 3, 4, 5]
VM344:9 (5) [1, 2, 4, 4, 20]
VM344:9 (5) [1, 2, 4, 5, 20]
VM344:9 (5) [1, 2, 4, 5, 20]
VM344:9 (6) [1, 2, 4, 5, 20, 20]
4
*/
Array.prototype.filter
- filter函数返回一个包含满足检测条件的所有元素的新数组,如果没有满足条件的元素项,则返回一个空数组
- filter不修改原数组
- IE9+支持filter函数
- filter函数会遍历所有的元素项(某个索引未定义值则会被跳过)
- filter 遍历的元素范围在第一次调用 callback 之前就已经确定了。在调用 filter 之后被添加到数组中的元素不会被 filter 遍历到。如果已经存在的元素被改变了,则他们传入 callback 的值是 filter 遍历到它们那一刻的值。被删除或从来未被赋值的元素不会被遍历到。
if (!Array.prototype.filter)
{
Array.prototype.filter = function(fun /* , thisArg*/)
{
"use strict";
if (this === void 0 || this === null)
throw new TypeError();
var t = Object(this);
var len = t.length >>> 0;
if (typeof fun !== "function")
throw new TypeError();
var res = [];
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
for (var i = 0; i < len; i++)
{
if (i in t)
{
var val = t[i];
// NOTE: Technically this should Object.defineProperty at
// the next index, as push can be affected by
// properties on Object.prototype and Array.prototype.
// But that method's new, and collisions should be
// rare, so use the more-compatible alternative.
if (fun.call(thisArg, val, i, t))
res.push(val);
}
}
return res;
};
}
Array.prototype.fill
- fill函数会改变调用的数组,而不是返回新数组,当一个对象被传递给 fill 方法的时候, 填充数组的是这个对象的引用
- IE系列不支持
- 需要注意一点是,调用fill函数,数组一定要有一个确定的length,如果指定的位置超出了数组的边界是不会被填充的
也就是说fill不会改变数组的length属性
if (!Array.prototype.fill) {
Object.defineProperty(Array.prototype, 'fill', {
value: function(value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
//如果没有第二个参数,默认为0
var relativeStart = start >> 0;
// Step 8.
//按照数组的长度来扶正start值赋值给k
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
//如果没传第三个值,则默认为数组的length
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
//按照数组的长度来扶正end值赋值给final
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
//开始索引小于结束索引,开进行循环
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
}
});
}
//当传入的填充参数是个引用类型的值,则填充的都是这个参数的引用
var arr = new Array(10).fill({name: "dao-keer", age: 18});
arr[0].name = "dao keer go";
console.log(arr)
//每个元素项都被修改成了:{name: "dao keer go", age: 18},填充引用类型的参数时需要特别小心
//超出边界的位置不会被填充
[1,2,3,4,5].fill(88, 1, 10);
//[1, 88, 88, 88, 88]
Array.prototype.forEach
- forEach会遍历整个数组,但是如果某个索引上没有定义,则该索引不会被遍历,没有返回一个新数组! & 没有返回值
- 没有办法中止或者跳出 forEach 循环,除了抛出一个异常
- forEach() 为每个数组元素执行callback函数,它总是返回 undefined值,并且不可链式调用
- forEach 遍历的范围在第一次调用 callback 前就会确定。调用forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()) ,之后的元素将被跳过
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling toObject() passing the
// |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get() internal
// method of O with the argument "length".
// 3. Let len be toUint32(lenValue).
var len = O.length >>> 0;
// 4. If isCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let
// T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty
// internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Call the Call internal method of callback with T as
// the this value and argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined
};
}
//对应的索引上如果没有定义,则不会被遍历到
[1, 2, , 4, 5, ,7].forEach(function(v,i,arr){
console.log("index: " + i + ", value: " + v)
});
/*
VM99:2 index: 0, value: 1
VM99:2 index: 1, value: 2
VM99:2 index: 3, value: 4
VM99:2 index: 4, value: 5
VM99:2 index: 6, value: 7
*/
//删除和修改元素
[1,2,3,4,5,6,7,8].forEach(function(v, i, arr){
if(i === 2){
arr.shift();
}
if(i === 3){
arr[i + 1] = 88;
}
console.log(v)
});
/*
VM114:8 1
VM114:8 2
VM114:8 3 //执行到这里的时候,删除了第一个元素,后面的元素4被跳过了
VM114:8 5
VM114:8 88
VM114:8 7
VM114:8 8
*/
Array.prototype.map
- map方法总是返回一个新的数组,不改变length属性,某个索引上未定义则会跳过不执行回调函数
- 原数组中新增加的元素将不会被 callback 访问到;若已经存在的元素被改变或删除了,则它们的传递到 callback 的值是 map 方法遍历到它们的那一时刻的值;而被删除的元素将不会被访问到
- map是根据元素组中的元素项进行的浅拷贝赋值,如果改变了新数组里的引用类型的值,会影响原数组
// 实现 ECMA-262, Edition 5, 15.4.4.19
// 参考: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback, thisArg) {
var T, A, k;
if (this == null) {
throw new TypeError(" this is null or not defined");
}
// 1. 将O赋值为调用map方法的数组.
var O = Object(this);
// 2.将len赋值为数组O的长度.
var len = O.length >>> 0;
// 3.如果callback不是函数,则抛出TypeError异常.
if (Object.prototype.toString.call(callback) != "[object Function]") {
throw new TypeError(callback + " is not a function");
}
// 4. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined.
if (thisArg) {
T = thisArg;
}
// 5. 创建新数组A,长度为原数组O长度len
A = new Array(len);
// 6. 将k赋值为0
k = 0;
// 7. 当 k < len 时,执行循环.
while(k < len) {
var kValue, mappedValue;
//遍历O,k为原数组索引
if (k in O) {
//kValue为索引k对应的值.
kValue = O[ k ];
// 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
mappedValue = callback.call(T, kValue, k, O);
// 返回值添加到新数组A中.
A[ k ] = mappedValue;
}
// k自增1
k++;
}
// 8. 返回新数组A
return A;
};
}
//修改引用类型的值,将影响原数组,修改引用类型的值需要特别注意
var arr = [1,2,3,4,{name: "dao-keer"},6], res;
res = arr.map(function(v, i, arr){
if(typeof v === "number"){
console.log(v);
}else{
v.name = "dao keer go";
}
//map的回调函数需要有返回值
return v;
});
console.log(arr);
console.log(res);
//{name: "dao keer go"} 两个数组的引用类型的值相同