通过用原型的方式重写数组的方法,加深对数组和原型的掌握。
先从最简单的开始:
1、push
// push(val1,val2,...) // 1、数组的方法,只能数组对象调用 // 2、功能是在数组的尾部添加数据 // 3、可以添加多个数据,逗号隔开 // 4、返回的是数组的length值,是数字类型 // 5、改变了原始数组 Array.prototype.myPush = function () { const arr = this; // 保存长度,减少对数组长度的反复查找,节约性能 let len = arr.length; for (let i = 0; i < arguments.length; i++) { arr[len++] = arguments[i]; } return len; }
2、pop
// pop() // 1、功能是:删除数组尾部的一个值 // 2、返回被删除的数据 // 3、改变的是原数组 Array.prototype.myPop = function () { const arr = this; let len = arr.length; // 必须要判断数组的长度,如果为0,直接返回undefined if (len == 0) return; let lastItem = arr[len - 1]; // 数组的长度少一个1,就会直接删除最后一个值。 arr.length--; return lastItem; }
3、unshift
// unshift(val,val,...) // shift() // 在数组的头部添加数据和删除数据,是先后先出的模式。 Array.prototype.myUnshift = function () { const arr = this; let len = arr.length; let argL = arguments.length; // 加入参数长度为0,返回数组的长度,不用操作后面 if (argL == 0) return len; // 先把原始数组的值往右移动参数的个数 for (let i = 0; i < len; i++) { arr[argL + i] = arr[i]; } // 再把参数的值一一对应到数组中 for (let i = 0; i < argL; i++) { arr[i] = arguments[i]; len++; } return len; }
4、shift
// shift Array.prototype.myShift = function () { const arr = this; let len = arr.length; //如果数组长度为0,则不执行,返回undefined。 if (len == 0) return; // 取出第一个值 let firstItem = arr[0]; // 遍历后面所有值,往前移动一个位置。 for (let i = 1; i < len; i++) { arr[i - 1] = arr[i]; } // 数组长度减1 arr.length--; // 返回第一个值 return firstItem; }
5、join
// join(分隔符) // 功能是:把数组的值转成字符串,用分隔符拼接。 // 参数是可以缺省,默认是逗号隔开。 // 如果是空字符串,直接把数组拼接成字符串 // 返回的是字符串 Array.prototype.myJoin = function (sep) { const arr = this; if (sep || sep === '') { sep = sep; } else { sep = ','; } let len = arr.length; let str = ''; if (len == 0) return str; for (let i = 0; i < len; i++) { if (i < len - 1) { str += arr[i] + sep; } else { str += arr[i]; } } return str; }
6、concat
// concat(val,val,...) // 功能是:把多个值合并在新数组中返回 // 参数:任意数据类型的值 // 返回值:一个新数组 // 不改变原数组,返回新数组,但是是浅拷贝 // 能够拆开第一层数组。 Array.prototype.myConcat = function () { const arr = this; let len = arr.length; let argsL = arguments.length; let newArr = []; // 先把原数组遍历出来,把值赋值给新数组 for (let i = 0; i < len; i++) { newArr[i] = arr[i]; } // 如果参数长度为0,返回新数组。 if (argsL == 0) return newArr; // 遍历参数 for (let i = 0; i < argsL; i++) { // 如果参数中有数组,则解开第一层数组的值 if (arguments[i] instanceof Array) { for (let j = 0; j < arguments[i].length; j++) { newArr[len++] = arguments[i][j]; } } else { newArr[len++] = arguments[i]; } } return newArr; }
7、slice
// slice(start,end) // 功能:截取数组的一段值 // 参数:start表示开始的索引值,end表示结束的索引值,不包括结束的索引值。[start,end),可以为负值。 // 返回值:返回一个截取出来的新数组 // 参数缺省,表示把所有的值都截取出来,返回到一个新数组,可以用来对数组进行浅拷贝。 // 没有修改原数组 // slice Array.prototype.mySlice = function (start, end) { const arr = this; let len = arr.length; let argsL = arguments.length; let newArr = []; // 如果数值长度为0,返回新的空数组 if (len == 0) return newArr; // 如果第一个参数缺省,默认值为0 start = arguments[0] || 0; // 如果第二个参数缺省,默认值为数组的长度 end = arguments[1] || len; // 如果第一个参数小于0 if (arguments[0] < 0) { start = len + arguments[0]; } // 如果第二个参数小于0 if (arguments[1] < 0) { end = len + arguments[1]; } // 如果第一个参数大于等于第二个参数,返回新的空数组 if (start >= end) return newArr; // 遍历start到end之间的值 for (let i = start; i < end; i++) { newArr.push(arr[i]); } return newArr; }
8、splice
// splice(index,howmany,val,val,....) // 功能:对数组任意位置进行增加,删除,修改值 // 修改原数组,返回被删除的值构成的新数组,如果没有删除值,返回空数组。 // 可以添加或者替换数组的一部分 Array.prototype.mySplice = function () { const arr = this; let len = arr.length; let argsL = arguments.length; let newArr = []; // 如果没有参数,返回空数组 if (argsL == 0) return newArr; // 第一个参数作为起始位置 let start = arguments[0]; // 如果第一个参数小于0 if (start < 0) { start = start + len; } // 如果第一个参数不是数字,或者是NaN,默认0 if (typeof start !== 'number' || start !== start || start < 0) { start = 0; } // 如果第一个参数大于数组的最大下标值 if (start > len - 1) { start = len; } // 第二个参数作为删除的个数。 // let howmany = (arguments[1] !== undefined) ? arguments[1] : len - start; let howmany = arguments[1]; // 如果没有传参第二个参数,默认为删除起始位置后面的所有值。如果为0,不删除。 // 这里不要用短路||运算符,否则参数为0的时候,也会返回后面len - start的值。 if (arguments[1] === undefined) { howmany = len - start; } // 如果第二个参数不是数字或者是NaN,默认为0 if (typeof howmany !== 'number' || howmany !== howmany) { howmany = 0; } // 如果被删除的个数大于了可以删除的个数,则只能截取到末尾。 if (howmany > len - start) { howmany = len - start; } // 把要删除的值添加到新数组中 for (let i = start; i < start + howmany; i++) { newArr.push(arr[i]); } // 获取第三个以后的参数 let addArgs = Array.prototype.slice.call(arguments, 2); // 要添加的参数的长度 let addArgsL = addArgs.length; // 如果有参数,则根据howmany来看,是添加还是替换。 // 如果是插入或替换,只需用替换元素长度减去删除的个数即可得出向后移动的位置数,把插入替换的元素在移动出空缺的位置上对号入座; if (addArgsL > 0) { // 添加的个数和删除的个数的差值 let num = addArgsL - howmany; // 如果添加的个数大于删除的个数,则是从后面扩展数组。 if (num > 0) { // 必须是从后面往前面遍历,否则前面的数会把后面的数覆盖 for (let i = len - 1; i >= start; i--) { arr[i + num] = arr[i]; } } else { // 如果添加的个数少于要删除的个数,则从起始位置加上删除个数的位置开始往前移动 for (let i = start + howmany; i < len; i++) { arr[i + num] = arr[i]; } } // 改变数组的长度,如果添加的参数少于删除的个数,数组要把后面多余的数删除。 // 数组原始的长度加上添加和删除的差值 arr.length = len + num; console.log(addArgsL, howmany, arr.length, start); // 再把要添加的数对应到相应的位置上。 for (let j = 0; j < addArgsL; j++) { arr[start++] = addArgs[j] } } else { // 没有要添加的参数,值直接删除。 for (let i = start; i < len; i++) { arr[i] = arr[i + howmany]; } arr.length = arr.length - howmany; } return newArr; }
9、reverse
// reverse() // 功能:翻转数组的值 // 没有参数 // 返回原数组 Array.prototype.myReverse = function () { const arr = this; let len = arr.length; if (len == 0) return arr; let centerpos = Math.floor(len / 2); let temp = ''; for (let i = 0; i < centerpos; i++) { temp = arr[i]; arr[i] = arr[len - 1 - i]; arr[len - 1 - i] = temp; } return arr; }
10、sort
// sort // v8的sort源代码使用的是数组小于10,插入排序,大于10,快速排序,大于1000,插入和快排混合。 // https://zhuanlan.zhihu.com/p/33626637 // https://zhuanlan.zhihu.com/p/24050357 // 这里使用的是冒泡排序 Array.prototype.mySort = function () { const arr = this; // 如果没有传参数 // 默认字符串排序 if (!arguments[0]) { for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - 1 - i; j++) { // 如果是默认排序,则转成字符串,升序排列 if (String(arr[j]) > String(arr[j + 1])) { let temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } return arr; } else { let fn = arguments[0]; for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - 1 - i; j++) { // 调用比较函数,计算相邻两个数的差值 let result = fn(arr[j], arr[j + 1]); // 如果差值大于0,则交换两个数的位置 if (result > 0) { let temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } return arr; } }
11、indexOf
// indexOf(item,start) // 功能:查找数组里面是否有item这个项.可以指定从哪个索引位置开始查找,start缺省,表示0 // 返回值:如果找到了,返回这个item项的索引值,没找到,返回-1 // 这个方法不能判断数组里面是否有NaN,因为NaN === NaN,返回false Array.prototype.myIndexOf = function (ele, start) { const arr = this; if (arr.length == 0) return -1; start = start || 0; let result = -1; for (let i = start; i < arr.length; i++) { // 如果元素相等,返回第一个相等的元素的下标值 if (ele === arr[i]) { result = i; break; } } return result; }
12、lastIndexOf
// lastIndexOf(item,start) // 功能和indexOf()类似,只不过是从数组的右边往左边查找 // 数组的元素的索引值是不变的,只是从右边往左边查找,返回的依然是值固定的索引值. Array.prototype.myLastIndexOf = function (ele, start) { const arr = this; let len = arr.length; if (len == 0) return -1; start = start || len - 1; let result = -1; for (let i = start; i >= 0; i--) { // 如果元素相等,返回第一个相等的元素的下标值 if (ele === arr[i]) { result = i; break; } } return result; }
写了好久终于把常规的数组方法写完了,还有后面迭代的方法没有……
发表评论:
◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。