PJCHENder 未整理筆記

[JS] 談談 JavaScript 中 for ... in 這個 function

2017-10-24

[JS] 談談 JavaScript 中 for … in 這個 function

@([Udemy] JavaScript: Understanding the Weird Parts)[javaScript, udemy]

img

在 JavaScript 中有一個非常常用到的函式 for ... in 迴圈,for...in 的用法和 Array.prototype.forEach 很像,但它可以針對**物件(Object)陣列(Array)**來使用。

for in 針對物件的基本使用

讓我們先來看一下下面這個例子:

1
2
3
4
5
6
7
8
var john = {
firstName: 'John',
lastName: 'Doe'
};

for (var prop in john) {
console.log(prop + ':' + john[prop]);
}

我們建立一個物件名稱為 john,而 prop 是自訂的變數,會把該物件的屬性存在這個變數中,接著讀取下一個屬性,重覆直到沒有屬性為止。透過 for...in,可以把該物件中的所有屬性名稱和屬性值都呼叫出來。

延伸閱讀:for…in @ MDN

可能問題一:包含繼承屬性的物件 - hasOwnProperty

然而,如果我們是透過函式建構式(function constructor)來建立物件時,這個物件可能會繼承該函式建構式的一些屬性或方法,這時候當我們直接使用 for...in 時,這些繼承而來的屬性和方法也會被一併輸出,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//  function constructor
var Person = function (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}

// function constructor 的 prototype
Person.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName
}

// 根據 function constructor 所建立的物件 Customer1
var Customer1 = new Person('John', 'Doe')

// 透過for...in輸出
for (var prop in Customer1) {
console.log(prop + ': ' + Customer1[prop])
}

透過這種方法,我們會發現連同prototype中繼承的方法也被輸出了:

img

如果想要解決這個問題,我們會需要使用到 Object.prototype.hasOwnProperty 這個內建的函式,透過 obj.hasOwnProperty(prop),它會回傳 Boolean,以此區分這個屬性是直接的或是繼承而來的,也就是說,透過這個方法,它不會往該物件的原型鏈(prototype chain)去檢查。寫法如下:

1
2
3
4
5
6
for (var prop in Customer1) {
// 不是繼承而來的屬性,才輸出...
if (Customer1.hasOwnProperty(prop)) {
console.log(prop + ': ' + Customer1[prop]);
}
}

Object.prototype.hasOwnProperty() @ MDN

如此,我們輸出的結果就只會有 firstName 和 lastName 這兩個直接的屬性。

可能問題二:包含繼承屬性的陣列,儘可能不要使用 for…in

在 JavaScript 中,陣列(Array)也是一種物件,因此,我們也可以對陣列使用 for...in 的方法來輸出陣列的內容,如下:

1
2
3
4
var arr = ['John', 'Jane', 'Jim']
for (var prop in arr) {
console.log(prop + ': ' + arr[prop])
}

如此,我們會得到如下的結果:

img

然而,當我們使用方括號 [ ] 來建立陣列的時候,其實就和使用 new Array ( ) 是一樣的意思。因此,如果原本的 Array.prototype 有被添加過一些屬性或方法時,使用 for...in 的結果一樣會把這些繼承的屬性和方法給輸出:

1
2
3
4
5
6
7
Array.prototype.website = 'pjchender'

var arr = ['John', 'Jane', 'Jim']

for (var prop in arr) {
console.log(prop + ': ' + arr[prop])
}

輸出的結果會把website這個繼承而來的屬性給一併輸出:

img

為了避免這樣的問題,如果是針對陣列在處理的話,會建議可以使用一般的 for 迴圈來輸出陣列就可以了:

1
2
3
for (var i = 0; i < arr.length; i++) {
console.log(i + ': ' + arr[i])
}

因此,當我們在處理陣列資料時,為了避免呼叫出不必要的屬性,應該儘可能不要使用 for...in 的用法來處理迴圈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var Person = function (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}

Person.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName
}

var Customer1 = new Person('John', 'Doe')

for (var prop in Customer1) {
if (Customer1.hasOwnProperty(prop)) {
console.log(prop + ': ' + Customer1[prop])
}
}

/* ------------------------------------- */
Array.prototype.website = 'pjchender'

var arr = ['John', 'Jane', 'Jim']

for (var prop in arr) {
console.log(prop + ': ' + arr[prop])
};

for (var i = 0; i < arr.length; i++) {
console.log(i + ': ' + arr[i])
}

→回到此系列文章目錄

資料來源

掃描二維條碼,分享此文章