1. 程式人生 > >淺談前端常見的陣列和物件的遍歷問題

淺談前端常見的陣列和物件的遍歷問題

前端遍歷有多種情況,除最常用的for迴圈外,還有for in、for of 等等,有針對陣列的map遍歷、forEach遍歷,有針對物件的Object.keys、object.values等等。

1、for迴圈,for in,for of的區別在哪裡?

先說說陣列,舉個栗子。

  1. 普通的for迴圈,就是遍歷我們的陣列。
  2. for in迴圈,會把原型上的屬性和屬性值都遍歷下來,遍歷的是鍵名。
  3. for of迴圈,只會遍歷陣列中的每一項,不是遍歷的鍵名。
var arr = [1,2,3];
arr.a = 4;
Array.prototype.name = 'mapbar_front'
; for(var i = 0; i < arr.length; i++){ console.log(arr[i]); } //普通的for迴圈打印出來是 123 for(var i in arr){ console.log(arr[i]); } //for in 迴圈列印的是 1234,mapbar_front for(var i of arr){ console.log(i);//這裡是i,而不是arr[i] } //打印出來的是 123

再說說物件的遍歷

  1. 物件無法直接被for迴圈遍歷。
  2. 物件的遍歷一般使用的是for in 迴圈,不過for in 迴圈會把原型上的屬性和方法給遍歷出來。
  3. for of只能遍歷iterable型別的資料,不能遍歷物件。
  4. 如果只想遍歷物件自身的屬性和方法,可以使用Object.hasOwnProperty()方法進行判斷。
  5. Object.keys方法,也是一種獲取物件的屬性的方式,這種方式不會把原型上面的屬性和方法的鍵名獲取。
var obj = {
    name: 'mapbar_front',
    age: 28,
}
Object.prototype.work = 'do work!';
for(var i in obj) {
    console.log(obj[i]);
}
var keylist = Object.keys(obj);
console
.log(keylist);// ['name', 'age'] //hasOwnProperty的使用 console.log(obj.hasOwnProperty('name'));//true console.log(obj.hasOwnProperty('work'));//false

2、陣列遍歷的方法。forEach、map、filter、find、findIndex、includes的使用

任何的陣列操作,都離不開遍歷,但是因為需求不一樣,所有會有不同的方法。

如果你想對陣列中的每一項,執行某一個函式,可能你需要用到forEach方法:

var arr = [1,2,3];
arr.forEach((item, index) => {
    console.log(item, index);
    //也可以有其他的操作。
})

如果你想對陣列,做某一種處理,返回一個處理後的陣列,map方法可能就是你的想要的:

var arr = [1, 2, 3];
//給每一項乘以2,然後返回一個結果
var newarr = arr.map((item, index) => {
    return item * 2;
})

如果你想對陣列,篩選出符合條件的項,最終得到一個新的篩選後的陣列,可能你要用到filter方法:

var arr = [1, 2, 3, 4];
var newarr = arr.filter((item, idx) => {
    return item % 2 === 0;
})

如果你想要知道,我們的陣列是不是有那麼一項符合某一個條件,那麼可能你要用到some方法,這個方法的返回值是一個布林值。

var arr = [1, 2, 3, 4];
var isTrue = arr.some((item, idx) => {
    return item % 2 === 0;
})

some方法和filter方法,看起來都是是否符合某個條件,但是不一樣的是,filter方法會把符合條件的每一項push到一個新的陣列,並且返回。而some方法只要符合條件,我就返回一個true就行了。

如果你想確保你的陣列是否每一項都符合某一個條件,可能你要用到any方法:

var arr = [1, 2, 3, 4];
var isTrue = arr.any((item, idx) => {
    return item % 2 === 0;
})

如果你想知道一個數組中是不是包含某一項,你可以使用includes方法:

var arr = [1,2,3];
arr.includes(1);//true

如果你想知道一個數組是不是可以找到符合條件的某一項,可以使用find方法:

var arr = [1, 2, 3];
var my = arr.find((item, index) => {
    return item === 2;
})

3、物件相關的遍歷API總結。

  • Object.entries,返回一個鍵值對的陣列,這個鍵值對也是以陣列的方式來進行排布。(需要注意的是,Object.entries不像for in 那樣,連原型上面的屬性和方法也行進遍歷)
var obj = {
    name: 'mapbar_front',
    age: 28,
    occupation: 'web fronted'
}
Object.entries(obj);//[["name","mapbar_front"], ["age", 28], ["occupation": "web fronted"]]

一般而言,通過Object.entries 物件可以轉換為Map資料結構。

var obj = { name: 'mapbar_front', age: 27 };
var map = new Map(Object.entries(obj));
  • Object.keys 以及 Object.values 方法的使用。
    對於物件而言,獲取它的鍵名的列表很重要,可以使用Object.keys獲取一個物件的所有鍵名,返回值是一個鍵名的列表。他們同樣不會獲取原型上面的屬性或者方法。

如果你僅僅是想要一個物件的value的集合,一般使用Object.values來解決。

var obj = {
    name: 'mapbar_front',
    age: 28,
    occupation: 'web fronted'
}
console.log(Object.keys(obj));//["name", "age", "occupation"];
console.log(Object.values(obj));//["mapbar_front", 28, "web fronted"];
  • Object.getOwnPropertyNames,獲取物件的屬性名,並且包括不可列舉的屬性的屬性名。返回值也是一個數組。
Object.getOwnPropertyNames(obj);
//["name", "age", "occupation", "sex"];

但是Object.getOwnPropertyNames方法,只能獲取鍵名型別為string型別的屬性,一般而言,屬性的型別也可以是Symbol型別,這個時候就需要另外的一種獲取Symbol型別的鍵名的方法。

  • Object.getOwnPropertySymbols
    該方法用於獲取一個物件的屬性鍵名型別為Symbol型別的鍵名。返回的是一個Symbol型別的陣列。並且也能獲取到不可列舉的屬性的鍵名。
var a = Symbol();
var obj = {
    name: 'mapbar_front',
    age: 28,
    occupation: 'web fronted',
    [a]: 1234,
}
Object.defineProperty(obj, 'sex', {
    value: 1,
    configurable: true,
    enumerable: true,
    writable: true
})
var symbol = Object.getOwnPropertySymbols(obj);
console.log(symbol);// [Symbol()]這樣的一個數組
var names = Object.getOwnPropertyNames(obj);
console.log(names);

在遍歷的時候,對於物件的屬性,我們可能要判斷,這個屬性是自己的例項屬性,還是原型上的屬性,Object的API中,有這樣的一些方法:

  • Object.prototype.hasOwnProperty,用於判斷一個物件的屬性是不是屬於自身。並且也能判斷出,一個不可列舉的屬性,是不是屬於自身物件。
  • Object.prototype.isPrototypeOf,用於判斷一個物件,是不是在另一個物件的原型鏈上面。
function Foo(){}
function Bar(){}
Bar.prototype = Object.create(Foo.prototype);
var bar = new Bar();
Bar.prototype.isPrototypeOf(bar);//true。    說明Bar.prototype在bar這個物件的原型鏈上。
Foo.prototype.isPrototypeOf(bar);//true。    說明Foo.prototype在bar這個物件的原型鏈上。
Object.prototype.isPrototypeOf(bar);//true。 說明Object.prototype在bar這個物件的原型鏈上。

有時候,我們可能還會在想,為什麼一個物件的屬性沒有被for in遍歷出來,很有可能這個物件的屬性是不可列舉的,使用下面的方法進行處理:

  • Object.prototype.propertyIsEnumberable,用於判斷一個物件的屬性是不是可列舉的,也就是能不能被for in 迴圈遍歷到。
  • Object.getOwnPropertyDescriptor(O,property),用於獲取物件O的描述物件——Descriptor

關於原型,我們可能要對一個物件設定一個新的原型,或者想要判斷一個物件是不是在另一個物件的原型鏈上面。

  • Object.setPrototypeOf(obj, proto),用於設定一個物件的原型。
  • Object.prototype.isPrototypeOf,用於判斷一個物件,是不是在另一個物件的原型鏈上面。
  • Object.create(proto),用於建立一個物件,並且這個物件的原型指向proto。

關於物件的操作,有時候你可能在想,我能不能對一個物件做一些限制,比如我不想給一個物件增加新的屬性,或者我這個物件不能刪除一個屬性,又或者我這個物件我都不能改變它的屬性值這類的操作,可能你需要知道這三個關於物件的方法:

  • Object.preventExtensions(O) / Object.isExtensible,保證了這個物件無法被擴充套件,isExtensible函式是返回一個true或者false,用來表示這個物件能不能被擴充套件。
  • Object.seal(O) / Object.isSealed,表示一個物件不僅不能被擴充套件,也不能隨意刪除一個物件的屬性。
  • Object.freeze(O) / Object.isFrozen,表示一個物件不僅不能被擴充套件,也不能隨意刪除一個屬性,而且連屬性值都不能被改變。
var obj = {
    name: 'mapbar_front',
    age: 23
}
Object.preventExtensions(obj);//讓這個物件不可以擴充套件
obj.name = 'mapbar';
obj.age1 = 1234;//這裡這一句不起作用
console.log(obj);//{ name: 'mapbar', age: 23 }

var obj1 = {
    name: 'mapbar_front',
    age: 123
}
Object.seal(obj1);
delete obj1.name;
console.log(obj1);//這個obj1沒有被刪除
console.log(Object.isSealed(obj1));//true

var obj2 = {
    name: 'mapbar_front',
    age: 123
}
Object.freeze(obj2);
obj2.name = '1111';
console.log(obj2);//這個obj2沒有變
console.log(Object.isFrozen(obj2));//true