flat / flatMap
-
Array.prototype.flat
function flat(/* depthArg = 1 */) {
// 函数名花里胡哨的,其实很好理解
// 拿到参数
var depthArg = arguments.length ? arguments[0] : undefined;
// if(this==undefined) 判断,否返回Objec(this)
var O = toObject(this);
// lengthOfArrayLike => toLength(obj.length)
// 其实就是拿到数组的length 规范length的边界为有效整数(2 ** 53 - 1)
var sourceLen = lengthOfArrayLike(O);
// 相当于new Array(0)
// 拿到数组的构造函数 并new 不赘述,感兴趣可以去看源码实现
var A = arraySpeciesCreate(O, 0);
// 核心
A.length = flattenIntoArray(A, O, O, sourceLen, 0, depthArg === undefined ? 1 : toIntegerOrInfinity(depthArg));
return A;
}
-
Array.prototype.flatMap
function flatMap(callbackfn /* , thisArg */) {
var O = toObject(this);
var sourceLen = lengthOfArrayLike(O);
var A;
// typeof callbackfn == 'function'
aCallable(callbackfn);
A = arraySpeciesCreate(O, 0);
// 与flat不同的是多了个callbackfn和thisArg depthArg写死为1
A.length = flattenIntoArray(A, O, O, sourceLen, 0, 1, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
return A;
}
-
flattenIntoArray:
/**
*
* @param {*} target 目标数组
* @param {*} original 原数组,用于mapper
* @param {*} source 当前遍历数组
* @param {*} sourceLen 当前遍历数组长度
* @param {*} start target中下一位索引值
* @param {*} depth 拍平深度
* @param {*} mapper 遍历函数
* @param {*} thisArg 遍历函数上下文
* @returns
*/
var flattenIntoArray = function (target, original, source, sourceLen, start, depth, mapper, thisArg) {
var targetIndex = start;
var sourceIndex = 0;
// 绑定函数上下文
var mapFn = mapper ? bind(mapper, thisArg) : false;
var element, elementLen;
// 开始遍历
while (sourceIndex < sourceLen) {
// 判断索引有效
if (sourceIndex in source) {
// 处理遍历函数返回值,没有遍历函数则返回当前项
// 每次遇到数组时都会执行
element = mapFn ? mapFn(source[sourceIndex], sourceIndex, original) : source[sourceIndex];
// 遇到数组并且存在有效depth
if (depth > 0 && isArray(element)) {
elementLen = lengthOfArrayLike(element);
// 递归
targetIndex = flattenIntoArray(target, original, element, elementLen, targetIndex, depth - 1) - 1;
} else {
// 如果拍平结束或者不是数组
// 判断数组长度的有效值
if (targetIndex >= 0x1FFFFFFFFFFFFF) throw TypeError('Exceed the acceptable array length');
// 设置对应值
target[targetIndex] = element;
}
// 索引++
targetIndex++;
}
sourceIndex++;
}
return targetIndex;
};
fill
-
Array.prototype.fill:
function toIntegerOrInfinity(argument) {
// 转换为number
// +undefined = NaN
// +null = 0
var number = +argument;
// eslint-disable-next-line no-self-compare -- safe
// 取小值 例如 1.1取1 -1.1取-2
// number !== number 判断NaN情况
// number === 0 判断0和null情况
return number !== number || number === 0 ? 0 : (number > 0 ? floor : ceil)(number);
}
function toAbsoluteIndex(index, length) {
// 先对第一个参数取整
var integer = toIntegerOrInfinity(index);
// 小于0取0 大于0取自身
return integer < 0 ? max(integer + length, 0) : min(integer, length);
}
function fill(value /* , start = 0, end = @length */) {
// if(this==undefined) 判断,否返回Objec(this)
var O = toObject(this);
// 与数组length与2**53-1 取小值(Math.min)
var length = lengthOfArrayLike(O);
var argumentsLength = arguments.length;
// start参数初始化
var index = toAbsoluteIndex(argumentsLength > 1 ? arguments[1] : undefined, length);
var end = argumentsLength > 2 ? arguments[2] : undefined;
// end参数初始化 默认为length
var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
// 遍历填充值
while (endPos > index) O[index++] = value;
return O;
}
- 这里有一个使用陷阱,
O[index++] = value; 直接赋值操作,如果value为引用类型,一旦`value某些属性更改,那么所有填充的值都会被更改
forEach, map, filter, some, every, find, findIndex, filterReject
Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterReject }以上函数的核心实现函数
var createMethod = function (TYPE) {
var IS_MAP = TYPE == 1;
var IS_FILTER = TYPE == 2;
var IS_SOME = TYPE == 3;
var IS_EVERY = TYPE == 4;
var IS_FIND_INDEX = TYPE == 6;
var IS_FILTER_REJECT = TYPE == 7;
var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
return function ($this, callbackfn, that, specificCreate) {
// if(this==undefined) 判断,否返回Objec(this)
var O = toObject($this);
var self = IndexedObject(O);
// callbackfn绑定上下文,一般是第三个参数
var boundFunction = bind(callbackfn, that);
// 取数组length与2**53-1 取小值(Math.min)
var length = lengthOfArrayLike(self);
var index = 0;
// 创建数组
// 具体实现有兴趣可以查看相关源码
var create = specificCreate || arraySpeciesCreate;
// 创建最终返回结果
// map => new Array(length)
// filter/filterReject => new Array(0)
// 其余为 undefined
var target = IS_MAP ? create($this, length) : IS_FILTER || IS_FILTER_REJECT ? create($this, 0) : undefined;
var value, result;
for (;length > index; index++) if (NO_HOLES || index in self) {
// find/findIndex 或者 有效索引
value = self[index];
// 执行callbackfn拿到结果
result = boundFunction(value, index, O);
if (TYPE) {
// 排除了forEach
if (IS_MAP) target[index] = result; // map修改索引对应值
else if (result) switch (TYPE) {
// 只要出现一次true
case 3: return true; // some 只要有一项返回true就返回true
case 5: return value; // find 直接返回值
case 6: return index; // findIndex 直接返回值的索引
case 2: push(target, value); // filter => target.push(value)
} else switch (TYPE) {
// 只要出现一次false
case 4: return false; // every
case 7: push(target, value); // filterReject
}
}
}
// findIndex没找到返回-1
// 如果是every此时result应该全为true,所以返回false
// 如果是some此时result应该全为false,所以判断是否是every函数即可
// 除此之外的函数都返回target
return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : target;
};
};
module.exports = {
// `Array.prototype.forEach` method
// https://tc39.es/ecma262/#sec-array.prototype.foreach
forEach: createMethod(0),
// `Array.prototype.map` method
// https://tc39.es/ecma262/#sec-array.prototype.map
map: createMethod(1),
// `Array.prototype.filter` method
// https://tc39.es/ecma262/#sec-array.prototype.filter
filter: createMethod(2),
// `Array.prototype.some` method
// https://tc39.es/ecma262/#sec-array.prototype.some
some: createMethod(3),
// `Array.prototype.every` method
// https://tc39.es/ecma262/#sec-array.prototype.every
every: createMethod(4),
// `Array.prototype.find` method
// https://tc39.es/ecma262/#sec-array.prototype.find
find: createMethod(5),
// `Array.prototype.findIndex` method
// https://tc39.es/ecma262/#sec-array.prototype.findIndex
findIndex: createMethod(6),
// `Array.prototype.filterReject` method
// https://github.com/tc39/proposal-array-filtering
filterReject: createMethod(7)
};
-
IndexedObject
fails(function () {
// 针对非数组(如ES3)和不可枚举的旧V8字符串的回退
// throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
// eslint-disable-next-line no-prototype-builtins -- safe
// 判断字符串是否可枚举
return !Object('z').propertyIsEnumerable(0);
}) ? function (it) {
// 如果是字符串,切分为数组
return classof(it) == 'String' ? split(it, '') : Object(it);
} : Object;
-
result = boundFunction(value, index, O);遍历函数第三个参数一般为原始数组,很少用到
-
流程:遍历数组,先排除没有返回值的,some,find,findIndex,every 函数都为短路判断,只要出现一次false/true就返回。
reduce
-
es.array.reduce.js
var $reduce = require('../internals/array-reduce').left;
$({ target: 'Array', proto: true, forced: !STRICT_METHOD || CHROME_BUG }, {
// 关注下面这部分
reduce: function reduce(callbackfn /* , initialValue */) {
var length = arguments.length;
return $reduce(this, callbackfn, length, length > 1 ? arguments[1] : undefined);
}
});
-
array-reduce.js
var createMethod = function (IS_RIGHT) {
/**
* that 上下文
* callbackfn 回调函数
* argumentsLength 参数长度
* memo 上一轮循环返回值
*/
return function (that, callbackfn, argumentsLength, memo) {
// typeof callbackfn === ‘function’
aCallable(callbackfn);
// var O = Object(that)
var O = toObject(that);
// 判断是String还是Array,是String则split为数组
var self = IndexedObject(O);
// 返回数组长度
var length = lengthOfArrayLike(O);
// 初始化索引,reduceRight初始索引为最后一项
var index = IS_RIGHT ? length - 1 : 0;
// 判断每次循环+1\-1
var i = IS_RIGHT ? -1 : 1;
// 如果reduce/reduceRight入参只有一项,没有初始值时
if (argumentsLength < 2) while (true) {
if (index in self) {
// 则memo为数组第一项/最后一项
memo = self[index];
index += i;
break;
}
index += i;
if (IS_RIGHT ? index < 0 : length <= index) {
throw TypeError('Reduce of empty array with no initial value');
}
}
for (;IS_RIGHT ? index >= 0 : length > index; index += i) if (index in self) {
// 执行回调
memo = callbackfn(memo, self[index], index, O);
}
return memo;
};
};
module.exports = {
// `Array.prototype.reduce` method
// https://tc39.es/ecma262/#sec-array.prototype.reduce
left: createMethod(false),
// `Array.prototype.reduceRight` method
// https://tc39.es/ecma262/#sec-array.prototype.reduceright
right: createMethod(true)
};
Array.from
-
array-from.js
module.exports = function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
// if(this==undefined) 判断,否返回Objec(this)
var O = toObject(arrayLike);
// 判断是否是构造函数
// 判断this不是function或者调用reflect.construct(function(){},[],this),返回false,否则返回true
var IS_CONSTRUCTOR = isConstructor(this);
var argumentsLength = arguments.length;
var mapfn = argumentsLength > 1 ? arguments[1] : undefined;
var mapping = mapfn !== undefined;
// 相当于 mapfn = mapfn.bind(thisArg,argumentsLength > 2 ? arguments[2] : undefined)
if (mapping) mapfn = bind(mapfn, argumentsLength > 2 ? arguments[2] : undefined);
// 获取迭代器或者默认迭代器
// 相当于 O[Symbol['iterator']]
var iteratorMethod = getIteratorMethod(O);
var index = 0;
var length, result, step, iterator, next, value;
// 如果目标是不可迭代的或者它是一个带有默认迭代器的数组
if (iteratorMethod && !(this == Array && isArrayIteratorMethod(iteratorMethod))) {
// 相当于 iteratorMethod.call(O),iterator为迭代器执行之后的对象
iterator = getIterator(O, iteratorMethod);
next = iterator.next;
// 如果有构造函数,则new,否则为[]
result = IS_CONSTRUCTOR ? new this() : [];
// (step = iterator.next()).done
for (;!(step = call(next, iterator)).done; index++) {
// 相当于 value = mapfn(step.value,index)
value = mapping ? callWithSafeIterationClosing(iterator, mapfn, [step.value, index], true) : step.value;
// result插入对应值
createProperty(result, index, value);
}
} else {
length = lengthOfArrayLike(O);
// 创建对应长度的数组
result = IS_CONSTRUCTOR ? new this(length) : Array(length);
for (;length > index; index++) {
// 遍历插入对应值
value = mapping ? mapfn(O[index], index) : O[index];
createProperty(result, index, value);
}
}
result.length = index;
return result;
};
sort
根据数组长度不同,用到了两个算法,分别是插入和归并
-
es.array.sort.js
var getSortCompare = function (comparefn) {
return function (x, y) {
// 使用陷阱,注意undefined的情况
if (y === undefined) return -1;
if (x === undefined) return 1;
if (comparefn !== undefined) return +comparefn(x, y) || 0;
// 没有comparefn默认从大到小排序
return toString(x) > toString(y) ? 1 : -1;
};
};
sort: function sort(comparefn) {
if (comparefn !== undefined) aCallable(comparefn);
var array = toObject(this);
// 通过判断版本判断是否使用原生sort,以及对Chakra和v8的错误情况判断
if (STABLE_SORT) return comparefn === undefined ? un$Sort(array) : un$Sort(array, comparefn);
var items = [];
var arrayLength = lengthOfArrayLike(array);
var itemsLength, index;
for (index = 0; index < arrayLength; index++) {
// 相当于items.push(array[index])
if (index in array) push(items, array[index]);
}
// 重点
// internalSort就是下文的mergeSort函数 负责计算排序
// getSortCompare负责初始化比较函数
internalSort(items, getSortCompare(comparefn));
itemsLength = items.length;
index = 0;
while (index < itemsLength) array[index] = items[index++];
while (index < arrayLength) delete array[index++];
return array;
}
-
array-sort.js
插入和归并就不多介绍了
var mergeSort = function (array, comparefn) {
var length = array.length;
var middle = floor(length / 2);
// 长度小于8,使用插入排序,大于则使用归并排序
return length < 8 ? insertionSort(array, comparefn) : merge(
array,
mergeSort(arraySlice(array, 0, middle), comparefn),
mergeSort(arraySlice(array, middle), comparefn),
comparefn
);
};
var insertionSort = function (array, comparefn) {
// 插入排序
var length = array.length;
var i = 1;
var element, j;
while (i < length) {
j = i;
element = array[i];
while (j && comparefn(array[j - 1], element) > 0) {
array[j] = array[--j];
}
if (j !== i++) array[j] = element;
} return array;
};
var merge = function (array, left, right, comparefn) {
// 归并排序
var llength = left.length;
var rlength = right.length;
var lindex = 0;
var rindex = 0;
while (lindex < llength || rindex < rlength) {
array[lindex + rindex] = (lindex < llength && rindex < rlength)
? comparefn(left[lindex], right[rindex]) <= 0 ? left[lindex++] : right[rindex++]
: lindex < llength ? left[lindex++] : right[rindex++];
} return array;
};
flat / flatMap
Array.prototype.flatArray.prototype.flatMapflattenIntoArray:targetIndex是关键,始终穿插在各层数组中记录索引,例如:fill
Array.prototype.fill:O[index++] = value;直接赋值操作,如果value为引用类型,一旦`value某些属性更改,那么所有填充的值都会被更改forEach, map, filter, some, every, find, findIndex, filterReject
Array.prototype.{ forEach, map, filter, some, every, find, findIndex, filterReject }以上函数的核心实现函数IndexedObjectresult = boundFunction(value, index, O);遍历函数第三个参数一般为原始数组,很少用到流程:遍历数组,先排除没有返回值的,
some,find,findIndex,every函数都为短路判断,只要出现一次false/true就返回。reduce
es.array.reduce.js
array-reduce.js
Array.from
array-from.js
sort
es.array.sort.js
array-sort.js