通过用原型的方式重写数组的方法,加深对数组和原型的掌握。
先从最简单的开始:
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;
}写了好久终于把常规的数组方法写完了,还有后面迭代的方法没有……
发表评论:
◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。