通过实现25个数组方法来理解及高效使用数组方法(长文,建议收藏)(1)

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: [ 6, 7, 8, 9, 10 ]

}

复制代码

.filter


Array.prototype.filter 过滤回调返回为false的值,每个值都保存在一个新的数组中,然后返回。

[1, 2, 3, 4, 5].filter(number => number >= 3);

// -> [3, 4, 5]

复制代码

实现

function push(array, …values) {

const { length: arrayLength } = array;

const { length: valuesLength } = values;

for (let index = 0; index < valuesLength; index += 1) {

array[arrayLength + index] = values[index];

}

return array.length;

}


function filter(array, callback) {

const result = [];

const { length } = array;

for (let index = 0; index < length; index += 1) {

const value = array[index];

if (callback(value, index, array)) {

push(result, value);

}

}

return result;

}

复制代码

获取每个值并检查所提供的回调函数是否返回truefalse,然后将该值添加到新创建的数组中,或者适当地丢弃它。

注意,这里对result 数组使用push方法,而不是将值保存在传入数组中放置的相同索引中。这样,result就不会因为丢弃的值而有空槽。

测试

logOperation(‘filter’, [1, 2, 3, 4, 5], array => filter(array, value => value >= 2));

复制代码

运行:

{

operation: ‘filter’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: [ 2, 3, 4, 5 ]

}

复制代码

.reduce


reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值reduce() 方法接受四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用 reduce() 的数组

确切地说,如何计算该值是需要在回调中指定的。来看呓使用reduce的一个简单的例子:对一组数字求和:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].reduce((sum, number) => {

return sum + number;

}, 0) // -> 55

复制代码

注意这里的回调接受两个参数:sumnumber。第一个参数总是前一个迭代返回的结果,第二个参数在遍历中的当前数组元素。

这里,当咱们对数组进行迭代时,sum包含到循环当前索引的所有数字的和因为每次迭代咱们都将数组的当前值添加到sum中。

实现

function reduce(array, callback, initValue) {

const { length } = array;

let acc = initValue;

let startAtIndex = 0;

if (initValue === undefined) {

acc = array[0];

startAtIndex = 0;

}

for (let index = startAtIndex; index < length; index += 1) {

const value = array[index];

acc = callback(acc, value, index, array)

}

return acc;

}

复制代码

咱们创建了两个变量accstartAtIndex,并用它们的默认值初始化它们,分别是参数initValue0

然后,检查initValue是否是undefined。如果是,则必须将数组的第一个值设置为初值,为了不重复计算初始元素,将startAtIndex设置为1

每次迭代,reduce方法都将回调的结果保存在累加器(acc)中,然后在下一个迭代中使用。对于第一次迭代,acc被设置为initValuearray[0]

测试

logOperation(‘reduce’, [1, 2, 3, 4, 5], array => reduce(array, (sum, number) => sum + number, 0));

复制代码

运行:

{ operation: ‘reduce’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: 15

}

复制代码

检索类

===

有什么操作比搜索特定值更常见?这里有一些方法可以帮助我们。

.findIndex


findIndex帮助咱们找到数组中给定值的索引。

[1, 2, 3, 4, 5, 6, 7].findIndex(value => value === 5); // 4

复制代码

findIndex方法对数组中的每个数组索引0..length-1(包括)执行一次callback函数,直到找到一个callback函数返回真实值(强制为true)的值。如果找到这样的元素,findIndex会立即返回该元素的索引。如果回调从不返回真值,或者数组的length0,则findIndex返回-1

实现

function findIndex(array, callback) {

const { length } = array;

for (let index = 0; index < length; index += 1) {

const value = array[index];

if (callback(value, index, array)) {

return index;

}

}

return -1;

}

复制代码

测试

logOperation(‘findIndex’, [1, 2, 3, 4, 5], array => findIndex(array, number => number === 3));

复制代码

运行:

{

operation: ‘findIndex’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: 2

}

复制代码

.find


findfindIndex的唯一区别在于,它返回的是实际值,而不是索引。实际工作中,咱们可以重用已经实现的findIndex

[1, 2, 3, 4, 5, 6, 7].find(value => value === 5); // 5

复制代码

实现

function find(array, callback) {

const index = findIndex(array, callback);

if (index === -1) {

return undefined;

}

return array[index];

}

复制代码

测试

logOperation(‘find’, [1, 2, 3, 4, 5], array => find(array, number => number === 3));

复制代码

运行

{

operation: ‘find’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: 3

}

复制代码

.indexOf


indexOf是获取给定值索引的另一种方法。然而,这一次,咱们将实际值作为参数而不是函数传递。同样,为了简化实现,可以使用前面实现的findIndex

[3, 2, 3].indexOf(3); // -> 0

复制代码

实现

function indexOf(array, searchedValue) {

return findIndex(array, value => value === searchedValue)

}

复制代码

测试

logOperation(‘indexOf’, [1, 2, 3, 4, 5], array => indexOf(array, 3));

复制代码

执行结果

{

operation: ‘indexOf’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: 2

}

复制代码

.lastIndexOf


lastIndexOf的工作方式与indexOf相同,lastIndexOf() 方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1

[3, 2, 3].lastIndexOf(3); // -> 2

复制代码

实现

function lastIndexOf(array, searchedValue) {

for (let index = array.length - 1; index > -1; index -= 1 ){

const value = array[index];

if (value === searchedValue) {

return index;

}

}

return -1;

}

复制代码

代码基本与findIndex类似,但是没有执行回调,而是比较valuesearchedValue。如果比较结果为 true,则返回索引,如果找不到值,返回-1

测试

logOperation(‘lastIndexOf’, [1, 2, 3, 4, 5, 3], array => lastIndexOf(array, 3));

复制代码

执行结果

{

operation: ‘lastIndexOf’,

arrayBefore: [ 1, 2, 3, 4, 5, 3 ],

arrayAfter: [ 1, 2, 3, 4, 5, 3 ],

mutates: false,

result: 5

}

复制代码

.every


every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试,它返回一个布尔值。

[1, 2, 3].every(value => Number.isInteger(value)); // -> true

复制代码

咱们可以将every 方法看作一个等价于逻辑与的数组。

实现

function every(array, callback){

const { length } = array;

for (let index = 0; index < length; index += 1) {

const value = array[index];

if (!callback(value, index, array)) {

return false;

}

}

return true;

}

复制代码

咱们为每个值执行回调。如果在任何时候返回false,则退出循环,整个方法返回false。如果循环终止而没有进入到if语句里面(说明条件都成立),则方法返回true

测试

logOperation(‘every’, [1, 2, 3, 4, 5], array => every(array, number => Number.isInteger(number)));

复制代码

执行结果

{

operation: ‘every’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: true

}

复制代码

.some


some 方法与 every 刚好相反,即只要其中一个为true 就会返回true。与every 方法类似,咱们可以将some 方法看作一个等价于逻辑或数组。

[1, 2, 3, 4, 5].some(number => number === 5); // -> true

复制代码

实现

function some(array, callback) {

const { length } = array;

for (let index = 0; index < length; index += 1) {

const value = array[index];

if (callback(value, index, array)) {

return true;

}

}

return false;

}

复制代码

咱们为每个值执行回调。如果在任何时候返回true,则退出循环,整个方法返回true。如果循环终止而没有进入到if语句里面(说明条件都不成立),则方法返回false

测试

logOperation(‘some’, [1, 2, 3, 4, 5], array => some(array, number => number === 5));

复制代码

执行结果

{

operation: ‘some’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: true

}

复制代码

.includes


includes方法的工作方式类似于 some 方法,但是includes不用回调,而是提供一个参数值来比较元素。

[1, 2, 3].includes(3); // -> true

复制代码

实现

function includes(array, searchedValue){

return some(array, value => value === searchedValue)

}

复制代码

测试

logOperation(‘includes’, [1, 2, 3, 4, 5], array => includes(array, 5));

复制代码

执行结果

{

operation: ‘includes’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: true

}

复制代码

拼接、附加和反转数组

==========

.concat


concat() 方法用于合并两个或多个数组,此方法不会更改现有数组,而是返回一个新数组。

[1, 2, 3].concat([4, 5], 6, [7, 8]) // -> [1, 2, 3, 4, 5, 6, 7, 8]

复制代码

实现

function concat(array, …values) {

const result = […array];

const { length } = values;

for (let index = 0; index < length; index += 1) {

const value = values[index];

if (Array.isArray(value)) {

push(result, …value);

} else {

push(result, value);

}

}

return result;

}

复制代码

concat将数组作为第一个参数,并将未指定个数的值作为第二个参数,这些值可以是数组,也可以是其他类型的值。

首先,通过复制传入的数组创建 result 数组。然后,遍历 values ,检查该值是否是数组。如果是,则使用push函数将其值附加到结果数组中。

push(result, value) 只会向数组追加为一个元素。相反,通过使用展开操作符push(result,…value) 将数组的所有值附加到result 数组中。在某种程度上,咱们把数组扁平了一层。

测试

logOperation(‘concat’, [1, 2, 3, 4, 5], array => concat(array, 1, 2, [3, 4]));

复制代码

执行结果

{

operation: ‘concat’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: [ 1, 2, 3, 4, 5, 1, 2, 3, 4 ]

}

复制代码

.join


join() 方法用于把数组中的所有元素放入一个字符串,元素是通过指定的分隔符进行分隔的。

[‘Brian’, ‘Matt’, ‘Kate’].join(', ') // -> Brian, Matt, Kate

复制代码

实现

function join(array, joinWith) {

return reduce(

array,

(result, current, index) => {

if (index === 0) {

return current;

}

return ${result}${joinWith}${current};

},

‘’

)

}

复制代码

reduce的回调是神奇之处:reduce遍历所提供的数组并将结果字符串拼接在一起,在数组的值之间放置所需的分隔符(作为joinWith传递)。

array[0]值需要一些特殊的处理,因为此时result是一个空字符串,而且咱们也不希望分隔符(joinWith)位于第一个元素前面。

测试

logOperation(‘join’, [1, 2, 3, 4, 5], array => join(array, ', '));

复制代码

执行结果

{

operation: ‘join’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: ‘1, 2, 3, 4, 5’

}

复制代码

.reverse


reverse() 方法将数组中元素的位置颠倒,并返回该数组,该方法会改变原数组。

实现

function reverse(array) {

const result = []

const lastIndex = array.length - 1;

for (let index = lastIndex; index > -1; index -= 1) {

const value = array[index];

result[lastIndex - index ] = value

}

return result;

}

复制代码

其思路很简单:首先,定义一个空数组,并将数组的最后一个索引保存为变量(lastIndex)。接着反过来遍历数组,将每个值保存在结果result 中的(lastIndex - index)位置,然后返回result数组。

测试

logOperation(‘reverse’, [1, 2, 3, 4, 5], array => reverse(array));

复制代码

执行结果

{

operation: ‘reverse’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: [ 5, 4, 3, 2, 1 ]

}

复制代码

添加、删除和追加值

=========

.shift


shift() 方法从数组中删除第一个元素,并返回该元素的值,此方法更改数组的长度。

[1, 2, 3].shift(); // -> 1

复制代码

实现

function shift(array) {

const { length } = array;

const firstValue = array[0];

for (let index = 1; index > length; index += 1) {

const value = array[index];

array[index - 1] = value;

}

array.length = length - 1;

return firstValue;

}

复制代码

首先保存数组的原始长度及其初始值,然后遍历数组并将每个值向下移动一个索引。完成遍历后,更新数组的长度并返回初始值。

测试

logOperation(‘shift’, [1, 2, 3, 4, 5], array => shift(array));

复制代码

执行结果

{

operation: ‘shift’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 2, 3, 4, 5 ],

mutates: true,

result: 1

}

复制代码

.unshift


unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。

[2, 3, 4].unshift(1); // -> [1, 2, 3, 4]

复制代码

实现

function unshift(array, …values) {

const mergedArrays = concat(values, …array);

const { length: mergedArraysLength } = mergedArrays;

for (let index = 0; index < mergedArraysLength; index += 1) {

const value = mergedArrays[index];

array[index] = value;

}

return array.length;

}

复制代码

首先将需要加入数组(作为参数传递的单个值)和数组拼接起来。这里需要注意的是values 放在第一位的,也就是放置在原始数组的前面。

然后保存这个新数组的长度并遍历它,将它的值保存在原始数组中,并覆盖开始时的值。

测试

logOperation(‘unshift’, [1, 2, 3, 4, 5], array => unshift(array, 0));

执行结果

{

operation: ‘unshift’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 0, 1, 2, 3, 4, 5 ],

mutates: true,

result: 6

}

复制代码

.slice


slice()

复制代码

方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end)原始数组不会被改变。

slice 会提取原数组中索引从 beginend 的所有元素(包含 begin,但不包含 end)。

[1, 2, 3, 4, 5, 6, 7].slice(3, 6); // -> [4, 5, 6]

复制代码

实现 (简单实现)

function slice(array, startIndex = 0, endIndex = array.length) {

const result = [];

for (let index = startIndex; index < endIndex; index += 1) {

const value = array[index];

if (index < array.length) {

push(result, value);

}

}

return result;

}

复制代码

咱们遍历数组从startIndexendIndex,并将每个值放入result。这里使用了这里的默认参数,这样当没有传递参数时,slice方法只创建数组的副本。

注意:if语句确保只在原始数组中存在给定索引下的值时才加入 result 中。

测试

logOperation(‘slice’, [1, 2, 3, 4, 5], array => slice(array, 1, 3));

复制代码

执行结果

{

operation: ‘slice’,

arrayBefore: [ 1, 2, 3, 4, 5 ],

arrayAfter: [ 1, 2, 3, 4, 5 ],

mutates: false,

result: [ 2, 3 ]

}

复制代码

.splice


splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

首先,指定起始索引,然后指定要删除多少个值,其余的参数是要插入的值。

const arr = [1, 2, 3, 4, 5];

// 从位置0开始,删除2个元素后插入 3, 4, 5

arr.splice(0, 2, 3, 4, 5);

arr // -> [3, 4, 5, 3, 4, 5]

复制代码

实现

function splice( array, insertAtIndex, removeNumberOfElements, …values) {

const firstPart = slice(array, 0, insertAtIndex);

const secondPart = slice(array, insertAtIndex + removeNumberOfElements);

const removedElements = slice(

array,

insertAtIndex,

insertAtIndex + removeNumberOfElements

);

const joinedParts = firstPart.concat(values, secondPart);

const { length: joinedPartsLength } = joinedParts;

for (let index = 0; index < joinedPartsLength; index += 1) {

array[index] = joinedParts[index];

}

array.length = joinedPartsLength;

return removedElements;

}

复制代码

其思路是在insertAtIndexinsertAtIndex + removeNumberOfElements上进行两次切割。这样,将原始数组切成三段。第一部分(firstPart)和第三部分(secondPart)加个插入的元素组成为最后数组的内容。

测试

logOperation(‘splice’, [1, 2, 3, 4, 5], array => splice(array, 1, 3));

复制代码

执行结果

{

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

html5

  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值