首页>前端教程>JavaScript教程

用原型重写数组基础方法(上)

通过用原型的方式重写数组的方法,加深对数组和原型的掌握。

先从最简单的开始:

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;

        }

写了好久终于把常规的数组方法写完了,还有后面迭代的方法没有……

点赞


1
保存到:

相关文章

发表评论:

◎请发表你卖萌撒娇或一针见血的评论,严禁小广告。

Top