陣列常用方法的簡單封裝

♛琉璃發表於2020-12-21

首先回顧有關原型的三句話:

把每個函式當做物件時,有一個屬性prototype,其值是一個物件;
每個prototype都有一個constructor屬性,指向prototype屬性所在建構函式;
每個物件都有一個__proto__屬性,指向所屬類的原型。

陣列基本操作方法

以下方法均改變原陣列,需要運算元組的 length 屬性

Push

  • 返回值:新陣列的長度
  • 思路:陣列的長度等於 arguments[i]
Array.prototype.myPush = function () {
    for (let i = 0; i < arguments.length; i++) {
        this[this.length] = arguments[i];
    }
    return this.length;
};

Pop

  • 返回值:刪除的項(如果空陣列,返回 undefined)
  • 思路:讓陣列長度 -1
Array.prototype.myPop = function () {
    return this.length==0?undefined:(this[this.length - 1],this.length--);
};

Shift

  • 返回值:刪除的項
  • 思路:讓陣列前一個值 this[i] 等於後一個值 this[i + 1] ,之後把陣列長度 -1
Array.prototype.myShift = function () {
    if (this.length == 0) {
        return;
    }
    let del = this[0];
    for (let i = 0; i < this.length; i++) {
        this[i] = this[i + 1];
    }
    this.length--;
    return del;
};

Unshift

  • 返回值:新陣列的長度
  • 思路:讓陣列後一個值 this[i] 等於前 n(n=arguments.length)個值 this[i - arguments.length] ,之後把前 n 個值填為 arguments[i]
Array.prototype.myUnshift = function () {
    this.length += arguments.length;
    for (let i = this.length - 1; i >= 0; i--) {
        if (i >= arguments.length) {
            this[i] = this[i - arguments.length];
        } else {
            this[i] = arguments[i];
        }
    }
    return this.length;
};
  • ES6 方法實現能簡單一些。其實就是拼接陣列,之後把拼接的陣列一項一項賦值給原陣列
Array.prototype.myUnshift = function (...arg) {
    var newArr = [...arg, ...this];
    for (let i = 0; i < newArr.length; i++) {
        this[i] = newArr[i];
    }
    return this.length;
};

陣列操作方法

以下方法除splice(改變原陣列)均不改變原陣列

splice(特殊)

因為 push 實現比較簡單,這裡用到了 push 方法,能簡寫一兩行

  • 返回值:刪除的項(陣列)

  • 思路:按引數數量分別進行判定

    • 引數小於等於1個,從 start 開始新增到新陣列,並把新增那項刪除(陣列長度也減少)
    • 引數大於1個,先進行刪除(跟上一步相同)再把陣列分成三份,左邊+中間被替換的項+右邊,合併陣列

    注意:

    1. 第一個引數為負數:如果轉換為正數,大於陣列長度,直接轉換為0;小於等於陣列長度,需加上陣列長度
    2. 第二個引數:如果小於0,直接轉換為0
    3. 第三個引數起:要新增的項
Array.prototype.mySplice = function (start, del) {
    let arr = [];
    if (start < 0) {
        start = -start > this.length ? 0 : this.length + start;
    }
    if (arguments.length <= 1) {
        for (let i = start; i < this.length; i++) {
            arr.push(this[i]);
        }
        this.length = start;
    } else {
        del = del < 0 ? 0 : del;
        // 刪除陣列這一步
        for (let i = 0; i < del; i++) {
            arr.push(this[start + i]);
            this[start + i] = this[start + i + del];
        }
        this.length -= del;
        let lArr = [];
        for (let i = 0; i < start; i++) {
            lArr.push(this[i]);
        }
        for (let i = 0; i < arguments.length - 2; i++) {
            lArr.push(arguments[i + 2]);
        }
        for (let i = start; i < this.length; i++) {
            lArr.push(this[i]);
        }
        for (let i = 0; i < lArr.length; i++) {
            this[i] = lArr[i];
        }
    }
    return arr;
};
  • 刪除陣列那一步,我第一時間想到的是冒泡(把刪除項一次一次冒到最後一位),最後出來的程式碼是下面這樣(非常麻煩)
let that = del;
for (let i = that; i > 0; i++) {
    if (that <= 0) {
        break;
    }
    for (let j = start; j < this.length - 1; j++) {
        [this[j], this[j + 1]] = [this[j + 1], this[j]];
    }
    arr.push(this[this.length - 1]);
    this.length--;
    that--;
}
  • 大可不必這樣,把刪除項的下一位(不刪除)往前挪一個一個覆蓋要刪除的項即可
for (let i = 0; i < del; i++) {
    arr.push(this[start + i]);
    this[start + i] = this[start + i + del];
}
this.length -= del;

concat

  • 返回值:拼接後的新陣列
  • 思路:如果引數是陣列需遍歷後一個一個新增到新陣列
Array.prototype.myConcat = function () {
    let arr = [];
    for (let i = 0; i < this.length; i++) {
        arr[i] = this[i];
    }
    for (let i = 0; i < arguments.length; i++) {
        const el = arguments[i];
        if (Array.isArray(el)) {
            for (let i = 0; i < el.length; i++) {
                arr[arr.length] = el[i];
            }
        } else {
            arr[arr.length] = el;
        }
    }
    return arr;
};

slice

  • 返回值:複製後的新陣列
  • 思路:如果傳參是負數索引,需對其長度進行判定。如果大於陣列長度,將其改為0;小於陣列長度,將其改為arr.length+(負數索引)
Array.prototype.mySlice = function (start = 0, end = this.length) {
    var arr = [];
    if (start < 0) {
        start = -start > this.length ? 0 : this.length + start;
    }
    if (end < 0) {
        end = -end > this.length ? 0 : this.length + end;
    }
    for (let i = start; i < end; i++) {
        arr.push(this[i]);
    }
    return arr;
};

flat

  • 返回值:扁平後的新陣列
  • 思路:遞迴(下面實現沒有加上可以指定遞迴深度的引數,類似arr.flat(Infinity)
Array.prototype.myFlat = function () {
    let arr = [];
    fn(this);
    function fn(ary) {
        for (let i = 0; i < ary.length; i++) {
            const item = ary[i];
            if (Array.isArray(item)) {
                fn(item);
            } else {
                arr.push(item);
            }
        }
    }
    return arr;
};

Array.prototype.myFlat = function () {
    return this.toString().split(",").map((item) => Number(item));
};

陣列重排方法

reverse

  • 返回值:倒序後的陣列
  • 思路:第n個數和倒數第n個數兩兩對換
Array.prototype.myReverse = function () {
    for (let i = 0, j = this.length - 1; j > i; i++, j--) {
        var temp = this[i];
        this[i] = this[j];
        this[j] = temp;
    }
    return this;
};

sort

  • 返回值:排序後的陣列
  • 思路:不傳參的時候,兩兩比較 String(xxx) 的值;傳參的時候判斷 callBack(a-b) 是否大於 0 即可
Array.prototype.mySort = function (callBack) {
    if (this.length <= 1) {
        return this;
    }
    if (typeof callBack === "function") {
        for (let i = 0; i < this.length - 1; i++) {
            for (let j = 0; j < this.length - 1 - i; j++) {
                if (callBack(this[j], this[j + 1]) > 0) {
                    [this[j], this[j + 1]] = [this[j + 1], this[j]];
                }
            }
        }
    } else if (typeof callBack === "undefined") {
        for (let i = 0; i < this.length - 1; i++) {
            for (let j = 0; j < this.length - 1 - i; j++) {
                if (String(this[j]) > String(this[j + 1])) {
                    [this[j], this[j + 1]] = [this[j + 1], this[j]];
                }
            }
        }
    } else {
        return "引數異常";
    }
    return this;
};

陣列位置方法

以下方法均不改變原陣列

indexOf

  • 返回值:索引/-1
  • 思路:遍歷陣列,第二個引數是從哪個索引開始。如果不傳參,從頭查到尾部
Array.prototype.myIndexOf = function (val, index = 0) {
    if (index < 0) {
        index = -index > this.length ? 0 : index + this.length;
    }
    for (let i = index; i < this.length; i++) {
        if (this[i] === val) {
            return i;
        }
    }
    return -1;
};

lastIndexOf

  • 返回值:索引/-1
  • 思路:遍歷陣列,第二個引數是到哪個索引結束(從0開始到這個索引結束)。如果不傳參,從頭查到尾部
Array.prototype.myLastIndexOf = function (val, index = this.length) {
    if (index < 0) {
        index = -index >= this.length ? 0 : index + this.length;
    }
    for (let i = index; i >= 0; i--) {
        if (this[i] === val) {
            return i;
        }
    }
    return -1;
};

includes

  • 返回值:true/false
  • 思路:遍歷陣列
Array.prototype.myIncludes = function (val) {
    for (let i = 0; i < this.length; i++) {
        if (this[i] === val) {
            return true;
        }
    }
    return false;
};

陣列迭代方法

以下方法均不改變原陣列

forEach

回撥函式內部 this 一般指向 window

  • 返回值:undefined
  • 思路:遍歷陣列
Array.prototype.myForEach = function (callBack) {
    for (let i = 0; i < this.length; i++) {
        let index = i, item = this[i];
        callBack(item, index);
    }
};

map

  • 返回值:對映後的新陣列
  • 思路:遍歷陣列,把陣列每以項經過運算後賦值給新陣列
Array.prototype.myMap = function (callBack) {
    let arr = [];
    for (let i = 0; i < this.length; i++) {
        let index = i, item = this[i];
        arr[i] = callBack(item, index);
    }
    return arr;
};

reduce

  • 返回值:函式累計處理的結果
  • 思路:initial 返回值在陣列的每次迭代中被記住,最後成為最終的結果值
Array.prototype.myReduce = function (callBack, initial) {
    if (typeof callBack !== "function") throw new TypeError("callBack must be function");
    let i = 0;
    if (typeof initial === "undefined") {
        initial = this[0];
        i = 1;
    }
    for (; i < this.length; i++) {
        initial = callBack(initial, this[i], i)
    }
    return initial;
};

相關文章