你真的瞭解JS陣列的那些方法嗎?

依米花開發表於2018-04-14

寄語:
JS陣列的方法非常的豐富和瑣碎,學習和使用過程中總是需要反覆的記憶、檢視文件,但作為JS基礎這樣真的不應該,我們應該把它記得滾瓜爛熟,深入理解才對。
但是要怎樣才能做到呢?這正是我也本文的初衷,根據個人的經驗總結了一下,希望能夠幫助和我一樣困惑的人,搞定這個頭疼的問題。

陣列的建立


陣列的建立有三種方式:建構函式方式、字面量方式、ES6新增的Array.of()方法建立。

  • 建構函式方式:

    let arr = new Array(); // 建立一個空陣列
    let arr = new Array(10); // 建立長度為10的陣列
    let arr = new Array('a'); // 建立包含一個字串元素a的陣列
    let arr = new Array(10, 'a'); // 建立包含10和字串a兩個元素的陣列 
    複製程式碼

    小結:
    1.new 關鍵字是可省略的
    2.當只傳一個number型別引數時,建立的是引數指定長度的陣列。即建構函式形式建立陣列,不能建立只包含一個number型別元素的陣列

  • 字面量方式:

    let arr = [10, 'a'];  // 字面量方式,即直接給陣列賦值
    複製程式碼
  • Array.of()方法(ES6新增):
    方法的作用是將一組值(即傳進來的引數)轉換為陣列。
    這個方法彌補了建構函式建立陣列的不足。可以實現建立只有一個number型別元素的陣列。

    let arr = Array.of(3); 
    console.log(arr); // [3]
    // 引數可以是各種型別
    let arr1 = Array.of(1, 'a', true, null, undefined, {name: "zhangsan"}, [45]);
    console.log(arr1); // [ 1, 'a', true, null, undefined, { name: 'zhangsan' }, [ 45 ] ]
    複製程式碼

陣列的檢測


兩種較為準確的檢測方法:

  • 利用物件的toString方法:
    Object.prototype.toString.call([]) === "[object Array]"; // true
  • Array.isArray():
    Array.isArray([1, 2, 3]); // true

陣列的屬性


  • length屬性:
    作用:
    • 設定或返回陣列的長度
    • 可用來增加和刪除陣列項
    let arr = [1, 2];  
    arr.length = 1;  
    console.log(arr); // [1]  
    arr.length = 3;    
    console.log(arr); // [1, , ] 
    複製程式碼

陣列的方法


JavaScript的陣列方法包括陣列原型的方法、建構函式的方法(ES6新增部分)
針對每一個方法我們主要了解四個方面:作用、引數、返回值、原陣列是否改變
(一). 原型上的方法

  1. push():
    作用:向陣列的末尾新增一項或多項
    引數:ele1[, ele2[, ...[, elen]]]
    返回值:新增元素後陣列的長度
    原陣列是否改變:是

    let arr = [1, 2, 3];
    let temp = arr.push('a', 'b');
    console.log(arr, temp); // [1, 2, 3, 'a', 'b'] 5
    複製程式碼
  2. pop():
    作用:刪除陣列最後一項
    引數:無
    返回值:刪除的那一項
    原陣列是否改變:是

    let arr = [1, 2, 3];
    let temp = arr.pop();
    console.log(arr, temp); // [1, 2] 3
    複製程式碼
  3. unshift():
    作用:向陣列開頭新增一項或多項
    引數:ele1[, ele2[, ...[, elen]]]
    返回值:新增元素後陣列的長度
    原陣列是否改變:是

    let arr = [1, 2, 3];
    let temp = arr.unshift('a', 'b');
    console.log(arr, temp); // ['a', 'b', 1, 2, 3] 5
    複製程式碼
  4. shift():
    作用:刪除陣列第一項
    引數:無
    返回值:刪除的那一項
    原陣列是否改變:是

    let arr = [1, 2, 3];  
    let temp = arr.shift();
    console.log(arr, temp); // [2, 3] 1
    複製程式碼
  5. splice():
    作用:刪除、插入、替換陣列項
    引數:startIndex[, deleteCount[, item1[, ...[, itemN]]]]
    返回值:刪除項組成的陣列
    原陣列是否改變:是

    let arr = [1, 2, 3];
    
    // 插入元素
    let temp = arr.splice(1, 0, 'a', 'b'); // 在索引1的位置插入元素'a''b' 
    console.log(arr, temp); // [1, 'a', 'b', 2, 3] []
    
    // 刪除元素
    let temp1 = arr.splice(1, 2); // 刪除從索引1的位置開始的2項 
    console.log(arr, temp1); // [1, 2, 3] ['a', 'b']
    
    // 替換一個元素
    let temp2 = arr.splice(1, 1, 'a'); // 將索引1的位置的元素替換為'a'
    console.log(arr, temp2); // [1, 'a', 3 ] [2]
    
    // 替換多個元素
    let temp3 = arr.splice(0, 2, 'b', 'c'); // 將索引0的位置開始的兩項,替換成’b‘和’c‘
    console.log(arr, temp3); // [’b‘, 'c', 3] [1, 'a']
    
    // 只傳第一個引數,則刪除從第一個引數指定的位置到陣列結尾的所有項
    let temp4 = arr.splice(0); //從索引0的位置開始,刪除後面的所有項
    console.log(arr, temp4); // [] [’b‘, 'c', 3]
    複製程式碼
  6. copyWithin()
    作用:將陣列指定位置(start到end)的元素複製到當前陣列的其他位置(target開始),這種複製會替換原位置的元素(ES6新增)
    引數說明:target[,start[,end]]
    引數說明:

    • target: 複製的目標位置(包括),即要被替換的元素開始的位置
    • start: 要copy的元素的開始位置,預設0
    • end: 要copy的元素的結束位置,預設為陣列最後一個元素

    返回值:複製替換之後的陣列
    原陣列是否改變:是

    let arr = [1, 2, 3, 4, 5];
    // 用索引0~4範圍內的元素,替換索引3~4範圍內的元素,因為要替換的位置只有兩個,所以只將4,5替換為了1,2
    let temp = arr.copyWithin(3);
    console.log(arr, temp); //  [1, 2, 3, 1, 2] [1, 2, 3, 1, 2]
    
    let arr1 = [1, 2, 3, 4, 5];
    // 用索引2~4範圍內的元素,替換索引3~4範圍內的元素,因為要替換的位置只有兩個,所以只將4,5替換為了3,4
    let temp1 = arr1.copyWithin(3, 2);
    console.log(arr1, temp1); // [1, 2, 3, 3, 4] [1, 2, 3, 3, 4]
    複製程式碼

    小結:
    總結上述的描述,copyWithin的作用就是在陣列長度的範圍內,複製start(包括)到end(不包括)範圍內的元素,然後用上述的元替換掉從target(包括)開始到陣列結尾的元素,能替換多少就替換多少

  7. reverse():
    作用:翻轉原陣列
    引數:無
    返回值:翻轉後的陣列
    原陣列是否改變:是

    let arr = [1, 2, 3];
    let temp = arr.reverse();
    console.log(arr, temp); // [ 3, 2, 1 ] [ 3, 2, 1 ]
    複製程式碼
  8. sort():
    作用:陣列排序
    引數:compareFunction
    引數說明:

    • compareFunction返回值大於0時調換當前比對項的順序,否則順序不 變;
    • 引數可以不傳,不傳預設按照Unicode編碼的順序排列

    返回值:排序後的陣列
    原陣列是否改變:是

     // 陣列從小到大排序
    let arr = [1, 4, 6, 7, 8, 3, 2];
    let temp = arr.sort((a, b) => {
        return a - b;
    })
    console.log(arr, temp); // [ 1, 2, 3, 4, 6, 7, 8 ] [ 1, 2, 3, 4, 6, 7, 8 ]
    
    // 一個實用的陣列排序的例子,根據物件元素的排序,排序物件在陣列中的位置
    let objArr = [{id: 3, name: "lilei"},{id: 1, name: "hanmeimei"},{id: 2, name: "yimi"}];
    let tempArr = objArr.sort((a, b) => {
        // 按照id從小到大的順序,對陣列中的物件進行排序
        // 這個示例說明回撥函式的形參a,b實際就是陣列中當前進行比對的兩個元素
        return a.id - b.id;
    }); 
    console.log(objArr); //  [{id: 1, name: 'hanmeimei'}, {id: 2, name: 'yimi'}, { id: 3, name: 'lilei' }]
    console.log(tempArr); // [{id: 1, name: 'hanmeimei'}, {id: 2, name: 'yimi'}, { id: 3, name: 'lilei'}]
    複製程式碼
  9. concat():
    作用:基於當前的陣列拼接陣列
    引數:value1[, value2[, ...[, valueN]]
    引數說明:

    • 引數的型別可以是任意型別。
    • 不是陣列型別直接按順序拼接到陣列末尾,陣列型別的則將陣列元素逐一取出拼接到陣列末尾
    • 不傳則相當於複製陣列

    返回值:拼接後的陣列
    原陣列是否改變:否

    let arr = [1,2];
    let temp = arr.concat('a', {id:1}, ['lilei', 'hanmeimei']);
    console.log(arr, temp); // [ 1, 2 ] [ 1, 2, 'a', { id: 1 }, 'lilei', 'hanmeimei']
    
    // 用於複製陣列
    let arr = [1, 2];
    let temp = arr.concat();
    console.log(arr, temp);  // [ 1, 2 ] [ 1, 2 ]
    複製程式碼
  10. slice():
    作用:基於當前陣列的一項或多項建立一個新的陣列
    引數:startIndex[,endIndex]
    引數說明:返回的元素包含startIndex位置的元素,但不包括endIndex位置的元素
    返回值:返回擷取的元素組成的陣列
    原陣列是否改變:否

    let arr = [0, 1, 2, 3, 4];
    let temp = arr.slice(1,3); // 返回從索引1(包括)位置到索引3(不包括)位置之前的元素
    console.log(arr, temp); // [0, 1, 2, 3, 4] [1, 2]
    
    // 用於複製陣列
    let arr = [0, 1, 2, 3, 4];
    let temp = arr.slice(0); // 返回從索引0(包括)位置到陣列結尾的所有元素
    console.log(arr, temp); // [0, 1, 2, 3, 4] [0, 1, 2, 3, 4]
    複製程式碼
  11. indexOf():
    作用:從陣列開頭查詢元素在陣列中的索引位置(ES5的方法)
    引數:searchElement[, fromIndex]
    返回值:searchElement在陣列中的索引,沒找到searchElement則返回-1
    原陣列是否改變:否

    let arr = [1, 2, 3, 4, 5, 6, 2];
    // 從陣列開頭開始查詢
    let temp = arr.indexOf(2);
    console.log(arr, temp); // [ 1, 2, 3, 4, 5, 6, 2 ] 1
    // 從指定的位置開始查詢
    let temp1 = arr.indexOf(2,3); // 從索引3(包括)的位置向後查詢元素2
    console.log(arr, temp1); // [ 1, 2, 3, 4, 5, 6, 2 ] 6
    複製程式碼
  12. lastIndexOf():
    作用:從陣列結尾查詢元素在陣列中的索引位置(ES5的方法)
    引數:searchElement[, fromIndex]
    返回值:searchElement在陣列中的索引,沒找到searchElement則返回-1 原陣列是否改變:否

    let arr = [1, 2, 3, 4, 5, 6, 2];
    // 從陣列末尾開始查詢
    let temp = arr.lastIndexOf(2);
    console.log(arr, temp); // [ 1, 2, 3, 4, 5, 6, 2 ] 6
    // 從指定的位置開始查詢
    let temp1 = arr.lastIndexOf(2,3); // 從索引3(包括)的位置向前查詢元素2
    console.log(arr, temp1); // [ 1, 2, 3, 4, 5, 6, 2 ] 1
    複製程式碼
  13. every():
    作用:對陣列中的每一項執行給定函式,如果該函式對每一項都返回true,則返回true(ES5方法)
    引數:callback[, thisArg]
    引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
    返回值:true 或 false
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4];
    let temp = arr.every((item, index, array) => {
        return item > 2;
    });
    console.log(arr, temp); // [ 1, 2, 3, 4 ] false
    
    // 方法的第二個引數可選,作用是設定第一個引數中的this指向,如果使用第二個引數,注意callback不能是箭頭函式
    // 後面的迭代方法涉及此引數的,用法相同,不在贅述
    let arr = [1, 2, 3, 4];
    let temp = arr.every(function(item, index, array) {
        return item > this.id;
    }, {id: 2});
    console.log(arr, temp); // [ 1, 2, 3, 4 ] false
    複製程式碼
  14. some():
    作用:對陣列中的每一項執行給定函式,如果該函式對任意一項返回true,則返回true(ES5方法)
    引數:callback[, thisArg]
    引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
    返回值:true 或 false
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4];
    let temp = arr.some((item, index, array) => {
        return item > 2;
    });
    console.log(arr, temp); // [ 1, 2, 3, 4 ] true
    複製程式碼
  15. filter():
    作用:對陣列中的每一項執行給定函式,返回該函式返回true的項組成的陣列(ES5方法)
    引數:callback[, thisArg]
    引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
    返回值:函式返回true的項組成的陣列
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4];
    let temp = arr.filter((item, index, array) => {
        return item > 2;
    });
    console.log(arr, temp); // [ 1, 2, 3, 4 ] [3, 4]
    複製程式碼
  16. map():
    作用:對陣列中的每一項執行給定函式,返回每次函式呼叫的結果組成的陣列(ES5方法)
    引數:callback[, thisArg]
    引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
    返回值:函式每次呼叫結果組成的陣列
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4];
    let temp = arr.map((item, index, array) => {
        return item * item;
    });
    console.log(arr, temp); // [ 1, 2, 3, 4 ] [ 1, 4, 9, 16]
    複製程式碼
  17. forEach():
    作用:對陣列中的每一項執行給定函式。無返回值(ES5方法)
    引數:callback[, thisArg]
    引數說明:callback有三個引數item(當前項),index(當前項索引),array(陣列物件本身)
    返回值:無
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4];
    let temp = arr.forEach((item, index, array) => {
        // 不會有返回值,但可在這裡執行某些操作
        return item * item;
    });
    console.log(arr, temp); // [ 1, 2, 3, 4 ] undefined
    複製程式碼

    注意:
    forEach在所有項都遍歷完成之前,無法像for迴圈一樣提前終止迴圈

  18. reduce():
    作用:從陣列的第一項開始,逐步遍歷到最後,迭代陣列的所有項(ES5方法)
    引數:callback[, initialValue]
    引數說明:

    • callback迭代函式,有四個引數(prev, cur, index, array)
      • prev 前一個值,(initialValue || 陣列第一項 || 上一次迭代的結果)
      • cur 當前迭代項
      • index 當前迭代項索引
      • array 迭代的原陣列
    • initialValue 迭代的基礎值,不傳基礎值是陣列第一項

    返回值:陣列迭代後,整體的迭代結果
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    // 陣列求和
    let arr = [1, 2, 3];
    let sum = arr.reduce((prev, cur, index, array) => {
        return prev + cur;
    });
    console.log(arr, sum); // [ 1, 2, 3 ] 6
    
    // 傳initialValue 基礎值的示例
    let sum1 = arr.reduce((prev, cur, index, array) => {
        return prev + cur;
    }, 10);
    // 返回的值是:10+1+2+3
    console.log(arr, sum1); // [ 1, 2, 3 ] 16
    複製程式碼

    reduce原始碼的實現

    Array.prototype.myReduce = function(callback, initialVal){
       let prev = initialVal || this[0]; 
        for(var i = pre ? 0 : 1; i < this.length; i++){
            prev = callback(prev, this[i], i, this);
       }
       return prev
    }
    複製程式碼
  19. reduceRight():
    作用:從陣列的最後一項開始,逐步遍歷到第一項,迭代陣列的所有項(ES5方法)
    引數:callback[, initialValue]
    引數說明:

    • callback迭代函式,有四個引數(prev, cur, index, array)
      • prev 前一個值,(initialValue || 陣列第一項 || 上一次迭代的結果)
      • cur 當前迭代項
      • index 當前迭代項索引
      • array 迭代的原陣列
    • initialValue 迭代的基礎值,不傳基礎值是陣列第一項

    返回值:陣列迭代後,整體的迭代結果
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    // 拼接字串,從後向前迭代陣列進行拼接
    let arr = ['h', 'e', 'l', 'l', 'o'];
    let str = arr.reduceRight((prev, cur, index, array) => {
        return prev + cur;
    });
    console.log(arr, str); // [ 'h', 'e', 'l', 'l', 'o' ] 'olleh'
    複製程式碼
  20. find():
    作用:查詢陣列中第一個符合條件的元素,返回該元素 (ES6新增)
    引數:callback[, thisArg]
    引數說明:引數的使用同上述的forEach、every、map、some、filter方法一致
    返回值:查詢到則返回該元素,沒找到返回undefined
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4, 5];
    let temp = arr.find((item, index, array) => {
        return item > 2;
    })
    console.log(arr, temp); // [1, 2, 3, 4, 5] 3
    複製程式碼
  21. findIndex():
    作用:查詢陣列中第一個符合條件的元素所在位置的索引,並返回該索引值
    引數:callback[, thisArg]
    引數說明:引數的使用同上述的forEach、every、map、some、filter方法一致
    返回值:查詢到則返回該索引值,沒找到返回-1
    原陣列是否改變:涉及callback,因此不確定,具體詳情見下文中的原型方法的小結部分。

    let arr = [1, 2, 3, 4, 5];
    let temp = arr.findIndex((item, index, array) => {
        return item > 2;
    })
    console.log(arr, temp); // [1, 2, 3, 4, 5] 2
    複製程式碼
  22. fill():
    作用:用指定元素,填充陣列從start(包括)到end(不包括)之間的元素,如果該區間內已經有元素,直接替換掉(ES6新增)
    引數:value[, start[, end]]
    返回值:填充後的陣列
    原陣列是否改變:是

    let arr = [1, 2, 3, 4, 5];
    let temp = arr.fill('a', 2, 4);
    console.log(arr, temp); // [1, 2, 'a', 'a', 5] [1, 2, 'a', 'a', 5]
    複製程式碼
  23. includes():
    作用:判斷陣列中是否包含指定的元素(ES7新增)
    引數:searchElement[, fromIndex]
    返回值:true或false
    原陣列是否改變:否

    let arr = [1, 2, 3, 4, 5];
    let temp = arr.includes(5);
    console.log(arr, temp); // [1, 2, 3, 4, 5] true
    
    // 這個方法彌補了indexOf檢視元素時的一個不足,即查詢NaN的誤差
    let arr1 = [NaN, 'a'];
    let temp1 = arr1.includes(NaN);
    let temp2 = arr1.indexOf(NaN);
    console.log(temp1, temp2); // true -1
    複製程式碼
  24. toString()、toLocalString():
    作用:呼叫陣列每一項的toString()方法,返回的是以逗號分隔的字串
    引數:無
    返回值:轉化後的字串
    原字陣列是否改變:否

    let arr = [1, [1, 2, [4]], {name: "zhangsan"}, 3];
    let temp = arr.toString();
    console.log(arr); [ 1, [ 1, 2, [ 4 ] ], { name: 'zhangsan' }, 3 ] 
    console.log(temp); // '1,1,2,4,[object Object],3'
    複製程式碼
  25. join():
    作用:將陣列元素轉化為字串(呼叫每個元素的toString方法),並使用指定的分隔符(預設為逗號)進行拼接,返回拼接後的字串
    引數:分隔符,預設為逗號(,)
    返回值:拼接後的字串
    原陣列是否改變:否

    let arr = [1, [1, 2, [4]], {name: "zhangsan"}, 3];
    let temp = arr.join();
    console.log(arr); [ 1, [ 1, 2, [ 4 ] ], { name: 'zhangsan' }, 3 ] 
    console.log(temp); // '1,1,2,4,[object Object],3'
    
    // 陣列求和
    let arr1 = [1, 2, 3];
    console.log(eval(arr1.join('+'))); // 6
    複製程式碼

原型方法的總結:
1. 陣列的方法無外乎是對陣列的增刪改查、轉換、迭代。增、刪、改都會改變原有的陣列,查、轉換的方法不涉及callback引數的不會改變原陣列,涉及到的則視情況而定,迭代方法因為均涉及到callback引數,因此也不確定。
那麼為什麼涉及到callback就不確定了呢???
首先如果直接在callback中操作原陣列,那肯定原陣列會改變。例如:

let arr = [1,2,3,4];    
let temp = arr.forEach((item,index,array) => {  
   // 直接通過索引操作原陣列  
   array[index] *= item;
});  
console.log(arr,temp); // [1, 4, 9, 16] undefined  
複製程式碼

如果不是直接操作原陣列,而是操作callback的item引數的時,如果item是基本資料型別則原陣列中對應的該項元素不會改變,如果是引用型別(陣列,物件、函式等)則改變,因為操作引用型別的值,實質是操作該值所在存貯地址的內容,而item對應的原陣列中的元素和item是同一引用地址,因此會導致原陣列中對應元素改變。(小夥伴們對這裡如果還是不理解,可以看下陣列方法polyfill的實現,這裡不再贅述)
2. 所有涉及索引的方法,開始位置都是在操作範疇的,結束位置都是不包括在操作範圍內的

(二).建構函式的方法

  • Array.from():
    作用:將類陣列轉化為陣列
    引數:arrayLike[, mapFn[, thisArg]]
    引數說明:

    • arrayLike:類陣列物件,可以是我們常見的nodeList、arguments、字串、iterable物件等
    • mapFn: 對轉化後的陣列進行操作的回撥函式
    • thisArg: 指定mapFun中的this

    返回值:轉化後的陣列,如果有mapFn,則返回結果是經過mapFn處理的陣列
    原類陣列是否改變:不使用mapFn,則類陣列不改變。使用mapFn則結果同上述迭代方法中使用callback的情況一致。

    let str = 'hello';
    let temp = Array.from(str);
    console.log(str, temp); // hello [ 'h', 'e', 'l', 'l', 'o' ]
    let temp1 = Array.from(str, (item, index) => {
        return item.toUpperCase();
    });
    console.log(str, temp1); // hello [ 'H', 'E', 'L', 'L', 'O' ]
    複製程式碼

    小結:
    Array.from() 等價於 Array.prototype.slice.call(arguments,0)

陣列擴充套件運算子(ES6新增)


陣列的擴充套件運算子可以將陣列轉化為以逗號分割的引數序列。
幾個簡單使用的應用場景:

  1. 將陣列通過擴充套件運算子轉化為引數序列直接傳參,無需使用apply轉化了
    let arr = [1, 2, 3];
    
    // apply寫法
    Math.min.apply(null, arr)
    
    // 擴充套件運算子寫法
    Math.min(...arr)
    複製程式碼
  2. 可以用於複製和拼接陣列
    let arr1 = [2, 3, 4];
    let arr2 = ['a', 'b', 'c'];
    
    // 拼接陣列arr1和arr2
    console.log([...arr1, ...arr2]); // [2, 3, 4, 'a', 'b', 'c']
    複製程式碼
  3. 可用於將字串分解為真正的陣列,
    […'hello']  // [ 'h', 'e', 'l', 'l', 'o' ]
    複製程式碼

寫在最後


長篇大論了這麼久,不是為了重複書中或者官方的使用說明。而是再分享一些學習的思路,讓初學者能夠更快更準的掌握JavaScript陣列,讓停留在會用層次的同學瞭解它更多。
希望我上述的方法說明能夠讓你對JavaScript陣列一目瞭然,在各個方法所闡述的幾個方面的對比中,將它更輕易的記住。

相關文章