We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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:
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; };
targetIndex是关键,始终穿插在各层数组中记录索引,例如:
targetIndex
[1,[2,3]] 第一轮循环,targetIndex=0 target赋值之后 targetIndex++ 第二轮循环,为数组,递归遍历[2,3],不为数组 target[targetIndex]=2 => target[1]=2 然后 targetIndex++ 然后 target[targetIndex]=3 => target[2]=3 target=[1,2,3]
Array.prototype.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
Array.prototype.{ 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);遍历函数第三个参数一般为原始数组,很少用到
result = boundFunction(value, index, O);
流程:遍历数组,先排除没有返回值的,some,find,findIndex,every 函数都为短路判断,只要出现一次false/true就返回。
some,find,findIndex,every
false/true
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.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; };
根据数组长度不同,用到了两个算法,分别是插入和归并
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; };
The text was updated successfully, but these errors were encountered:
No branches or pull requests
flat / flatMap
Array.prototype.flat
Array.prototype.flatMap
flattenIntoArray
: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 }
以上函数的核心实现函数IndexedObject
result = 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
The text was updated successfully, but these errors were encountered: