js 中 for 和 for in 遍歷數(shù)組區(qū)別及Object.keys()、Object.getOwnPropertyNames()

ECMAScript 將對(duì)象的屬性分為兩種:數(shù)據(jù)屬性訪問(wèn)器屬性。每一種屬性?xún)?nèi)部都有一些特性,這里我們只要關(guān)注對(duì)象屬性的[[Enumerable]]特性,可以理解為:是否可枚舉。
然后根據(jù)上下文環(huán)境的不同,我們又可以將屬性分為:原型屬性實(shí)例屬性。原型屬性是定義在對(duì)象的原型(prototype)中的屬性,而實(shí)例屬性就是構(gòu)造函數(shù)實(shí)例化后添加的新屬性。

for in 循環(huán)

使用 for in 循環(huán),只遍歷對(duì)象自身和繼承的可枚舉的屬性。
雖然 for in 主要用于遍歷對(duì)象屬性,但同樣也可以用來(lái)遍歷數(shù)組元素。

let arr = ['a', 'b', 'c', 'd'];
for (let i in arr) {
  console.log('索引: ' + i, '值: ' + arr[i]);
}
for (let i = 0; i < arr.length; i++) {
  console.log('索引: ' + i, '值: ' + arr[i]);
}
// 兩種方式都輸出:
  // 索引: 0 值: a
  // 索引: 1 值: b
  // 索引: 2 值: c
  // 索引: 3 值: d

for 和 for in 遍歷數(shù)組的區(qū)別主要有 3 點(diǎn):
1. 如果擴(kuò)展了 Array,那么擴(kuò)展的屬性會(huì)被 for in 輸出。

let colors = ['red', 'green', 'yellow'];
// 擴(kuò)展了 Array.prototype
Array.prototype.demo = function () {};
for (let i in colors) {
  console.log(i);  // 0 1 2 demo
}
// 查看原型的方法[[enumerable]]特性,以 push 方法為例
console.log(Array.prototype.propertyIsEnumerable('push'));  // false
console.log(Object.getOwnPropertyDescriptor(Array.prototype, 'push'));  // {value: ?, writable: true, enumerable: false, configurable: true}
// 查看 demo 屬性的特性
console.log(Array.prototype.propertyIsEnumerable('demo'));  // true
console.log(Object.getOwnPropertyDescriptor(Array.prototype, 'demo'));  // {value: ?, writable: true, enumerable: true, configurable: true}

可以看出我們添加的 demo 方法,默認(rèn)是可以被 for in 枚舉出來(lái)的。如果不想被其枚舉,可以使用 Es5 的 Object.defineProperty() 來(lái)重新定義這個(gè)屬性,此外,還可以使用 hasOwnProperty() 方法來(lái)過(guò)濾掉。

2. for 和 for in 遍歷數(shù)組時(shí)下標(biāo)類(lèi)型不一樣。

let colors = ['red', 'green', 'yellow'];
for (let i in colors) {
  console.log(typeof i);  // string string string
}
for (let i = 0; i < colors.length; i ++) {
  console.log(typeof i);  // number number number
}

3. 對(duì)于不存在的數(shù)組項(xiàng)的處理差異。
對(duì)于數(shù)組來(lái)講,我們知道如果將其 length 屬性設(shè)置為大于數(shù)組項(xiàng)數(shù)的值,則新增的每一項(xiàng)都會(huì)得到 undefined 的值。

let colors = ['red', 'green', 'yellow'];
// 將數(shù)組長(zhǎng)度變?yōu)?10
colors.length = 10;
// 在添加一個(gè)元素到數(shù)組末尾
colors.push('blue');
for (let i in colors) {
  console.log(i);  // 0 1 2 10
}

Object.keys()

Object.keys() 返回對(duì)象自身的所有可枚舉的屬性的鍵名。返回一個(gè)由屬性名組成的數(shù)組。

// 遍歷數(shù)組
let colors = ['red', 'green', 'yellow'];
colors.length = 10;
colors.push('blue');
Array.prototype.demo = function () {};
console.log(Object.keys(colors));  // ["0", "1", "2", "10"]

// 遍歷對(duì)象
function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.demo = function() {};
var jenemy = new Person('jenemy', 25);
console.log(Object.keys(jenemy));  // ["name", "age"]

Object.getOwnPropertyNames()

Object.getOwnPropertyNames() 方法返回對(duì)象的所有自身屬性的屬性名(包括不可枚舉的屬性)組成的數(shù)組,但不會(huì)獲取原型鏈上的屬性。

A.prototype.date = Date();
function A (a, b) {
  this.a = a;
  this.b = b;
  this.getA = function () {
    return this.a;
  }
}
let a = new A('b', 'bb');
a.getB = function () {};
Object.defineProperty(a, 'demo', {
  value: function () {  }
})
console.log(Object.getOwnPropertyNames(a));  // ["a", "b", "getA", "getB", "demo"]

總結(jié):其實(shí)這幾個(gè)方法之間的差異主要在屬性是否可可枚舉,是來(lái)自原型,還是實(shí)例。

方法 適用范圍 描述
for..in 數(shù)組,對(duì)象 獲取可枚舉的實(shí)例和原型屬性名
Object.keys() 數(shù)組,對(duì)象 返回可枚舉的實(shí)例屬性名組成的數(shù)組
Object.getPropertyNames() 數(shù)組,對(duì)象 返回除原型屬性以外的所有屬性(包括不可枚舉的屬性)名組成的數(shù)組
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容