2016年6月29日

[筆記] 談談 JavaScript 中 for ... in 這個 function

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

for in 針對物件的基本使用

讓我們先來看一下下面這個例子:
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 時,這些繼承而來的屬性和方法也會被一併輸出,如下:
//  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)去檢查。寫法如下:
for (var prop in Customer1) {
  // 不是繼承而來的屬性,才輸出...
  if (Customer1.hasOwnProperty(prop)) {
    console.log(prop + ': ' + Customer1[prop]);
  }
}
如此,我們輸出的結果就只會有 firstName 和 lastName 這兩個直接的屬性。

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

在 JavaScript 中,陣列(Array)也是一種物件,因此,我們也可以對陣列使用 for...in 的方法來輸出陣列的內容,如下:
var arr = ['John', 'Jane', 'Jim']
for (var prop in arr) {
  console.log(prop + ': ' + arr[prop])
}
如此,我們會得到如下的結果:
img
然而,當我們使用方括號 [ ] 來建立陣列的時候,其實就和使用 new Array ( ) 是一樣的意思。因此,如果原本的 Array.prototype 有被添加過一些屬性或方法時,使用 for...in 的結果一樣會把這些繼承的屬性和方法給輸出:
Array.prototype.website = 'pjchender'

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

for (var prop in arr) {
  console.log(prop + ': ' + arr[prop])
}
輸出的結果會把website這個繼承而來的屬性給一併輸出:
img
為了避免這樣的問題,如果是針對陣列在處理的話,會建議可以使用一般的 for 迴圈來輸出陣列就可以了:
for (var i = 0; i < arr.length; i++) {
  console.log(i + ': ' + arr[i])
}
因此,當我們在處理陣列資料時,為了避免呼叫出不必要的屬性,應該儘可能不要使用 for...in 的用法來處理迴圈
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])
}

資料來源

0 意見:

張貼留言