詳解js中的遍歷

實例屬性和原型屬性

  • JavaScript中對象的屬性分爲兩種: 數據屬性訪問器屬性
  • 根據具體的上下文環境的不一樣,又能夠將屬性分爲: 原型屬性實例屬性
  • 原型屬性 是定義在對象的原型prototype 中的屬性,
  • 實例屬性 一方面來自構造的函數中,而後就是構造函數實例化後添加的新屬性。

js的枚舉

JavaScript中遍歷一個對象的屬性並不太簡單,主要有兩個緣由:es6

  • JavaScript中的對象一般都處在某個原型鏈中,它會從一個或多個的上層原型上繼承一些屬性
  • JavaScript中的屬性不光有值,它還有一些除了值之外的其餘特性,其中一個影響屬性遍歷的特性就是Enumerable(一個屬性描述符) ,若是該值爲 true ,則這個屬性是可枚舉的,不然反之
屬性描述符
  • 屬性描述符 主要有兩種形式:數據描述符存取描述符
  • 使用Object.getOwnPropertyDescriptorObject.getOwnPropertyDescriptors兩個方法獲取對象的屬性描述符
var obj = {
  name: '10',
  _age: 25,
  get age(){
    return this._age;
  },
  set age(age){
    if(age<1){
      throw new Error('Age must be more than 0');
    }else{
      this._age = age;
    }
  }
};

var des = Object.getOwnPropertyDescriptors(obj);
console.log(des);
/**
 * des: {
 *  name: {
 *    configurable: true,
 *    enumerable: true,
 *    value: "10",
 *    writable: true,
 *    __proto__: Object
 *  },
 *  _age: {
 *    configurable: true,
 *    enumerable: true,
 *    value: 25,
 *    writable: true,
 *    __proto__: Object
 *  },
 *  age: {
 *    configurable: true,
 *    enumerable: true,
 *    get: f age(),
 *    set: f age(age),
 *    __proto__: Object
 *  },
 *  __proto__: Object
 * }
*/
value

該屬性的值(僅針對數據屬性描述符有效)數組

writable

writable屬性設置爲false時,該屬性被稱爲「不可寫」。它不能被從新分配。瀏覽器

get

獲取該屬性的訪問器函數(getter)。若是沒有訪問器, 該值爲undefined。(僅針對包含訪問器或設置器的屬性描述有效)數據結構

set

獲取該屬性的設置器函數(setter)。 若是沒有設置器, 該值爲undefined。(僅針對包含訪問器或設置器的屬性描述有效)函數

configurable

configurable特性表示對象的屬性是否能夠被刪除,以及除writable特性外的其餘特性是否能夠被修改。this

enumerable

enumerable定義了對象的屬性是否能夠在 for...in 循環和 Object.keys() 中被枚舉。prototype

  • name、_age擁有 'configurable''enumerable''value''writable'四個屬性描述符,統稱數據描述符
  • age擁有'configurable''enumerable''get''set'四個屬性描述符,統稱存取描述符
分類 'configurable' 'enumerable' 'value' 'writable' 'get' 'set'
數據描述符 yes yes yes yes no no
存取描述符 yes yes no no yes yes

對象的屬性描述符,能夠經過Object.definePropertyObject.defineProperties來修改(configurabletrue的條件下)code

經常使用的遍歷方法

for...in...遍歷

  • 遍歷自身及原型鏈上全部可枚舉的屬性
  • 使用 for...in 循環遍歷對象屬性時返回的屬性會由於各個 瀏覽器不一樣 致使對象屬性遍歷的順序有可能不是當初構建時的順序。
Chrome Opera 的 JavaScript 解析引擎遵循的是新版 ECMA-262 第五版規範。所以,使用 for-in 語句遍歷對象屬性時遍歷書序並不是屬性構建順序。而 IE6 IE7 IE8 Firefox Safari 的 JavaScript 解析引擎遵循的是較老的 ECMA-262 第三版規範,屬性遍歷順序由屬性構建的順序決定。
for-in 語句沒法保證遍歷順序,應儘可能避免編寫依賴對象屬性順序的代碼。若是想順序遍歷一組數據,請使用數組並使用 for 語句遍歷。
var Animal = function({name='none', age=3, weight=80}={}){
  this.name = name;
  this.age = age;
  this.weight = weight;
}

Animal.prototype = {
  color: 'red'
}

var dog = new Animal()

// 將weight屬性設置爲 不可枚舉
Object.defineProperty(dog, 'weight', {
  enumerable: false
})

for(let i in dog){
  console.log(n);
}

//原型鏈上的color一樣被遍歷出來了,而且因爲weight屬性被設置成了enumerable:false,因此不可被遍歷
//name 
//age 
//color

for...of遍歷

一個數據結構只要部署了Symbol.iterator屬性,就被視爲具備 iterator 接口,就能夠用for...of循環遍歷它的成員。也就是說,for...of循環內部調用的是數據結構的Symbol.iterator方法。對象

for...of循環可使用的範圍包括數組、SetMap 結構、某些相似數組的對象(好比arguments對象、DOM NodeList 對象)、Generator 對象,以及字符串。繼承

若是不太清楚iterator,請去看看阮一峯大神的這篇文章,裏面關於for...of以及iterator都講的很是詳細!

其實for...offor...in都是迭代一些東西,它們之間的主要區別在於它們的迭代方式。

  • for...in語句以原始插入順序迭代對象的可枚舉屬性。
  • for...of 語句遍歷可迭代對象定義要迭代的數據。

請仔細看如下實例,理解其中的區別

Object.prototype.objCustom = function() {}; 
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i); //  0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); //  0, 1, 2, "foo"
  }
}

for (let i of iterable) {
  console.log(i); //  3, 5, 7
}
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {}; 

let iterable = [3, 5, 7]; 
iterable.foo = 'hello';

在這段代碼裏面,因爲繼承和原型鏈,對象iterable繼承屬性objCustomarrCustom

for (let i in iterable) {
  console.log(i); // 0, 1, 2, "foo", "arrCustom", "objCustom" 
}

在這段代碼裏面,此循環僅以原始插入順序記錄iterable 對象的可枚舉屬性。它不記錄數組元素3, 5, 7hello,由於這些不是枚舉屬性。可是它記錄了數組索引以及arrCustomobjCustom(爲什麼記錄arrCustomobjCustom在本文for...in裏面有講過)。

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); //  0, 1, 2, "foo"
  }
}

hasOwnProperty()用來檢查找到的枚舉屬性是否是對象本身的(便是不是繼承的)

for (let i of iterable) {
  console.log(i); //  3, 5, 7 
}

該循環迭代並記錄iterable做爲可迭代對象定義的迭代值,這些是數組元素 3, 5, 7,而不是任何對象的屬性。

Object.keys遍歷

Object.keys() 方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和使用 for...in 循環遍歷該對象時返回的順序一致 (二者的主要區別是 一個 for-in 循環還會枚舉其原型鏈上的屬性)。

Object.getOwnPropertyNames()遍歷

Object.getOwnPropertyNames()方法返回一個由指定對象的全部自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值做爲名稱的屬性)組成的數組,此方法不會獲取原型鏈上的屬性。

var Animal = function({name='', age=1, weight=70}={}){
  this.name = name;
  this.age = age;
  this.weight = weight;
}

Animal.prototype = {
  type: 'Animal'
}

var dog = new Animal()

// 將height屬性設置爲 不可枚舉
Object.defineProperty(dog, 'weight', {
  enumerable: false
})

var keys = Object.getOwnPropertyNames(dog);
console.log(keys)
// ['name', 'age', 'weight']

結語

這篇文章但願能讓你們更加理解js中的遍歷,寫的很差多多見諒並指出!

相關文章
相關標籤/搜索