JSオブジェクトの遍歴に深く入る

6860 ワード

概要
Javascriptプログラミングでは、常にオブジェクトのキー、値を遍歴する必要があります.ES 5はforを提供します.inはオブジェクトを遍歴するために使用されますが、オブジェクトの属性に関連する「エニュメレーション属性」、プロトタイプチェーンの属性などは、どうしても頭に触れられません.本稿ではObjectオブジェクトの本質から様々なエルゴードオブジェクトのアプローチを探索し、一般的な方法の特徴を区別する.
ここで言及するオブジェクトは、Objectの例を指すが、Set、Map、Arayなどのデータセットオブジェクトは含まれない.
Objectの「偽装」を剥がす
Javascriptのオブジェクトは、それぞれの属性に「属性記述子」があります.主に2つの形式があります.データ記述子とアクセス記述子です.Object.getOwnPropertyDescriptorObject.getOwnPropertyDescriptorsの2つの方法でオブジェクトの属性記述子を取得することができる.以下は例を通して説明する.
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
 * }
*/
見えます
  • name、_ageは、'configurable''enumerable''value''writable'の4つの属性記述子を有し、総称してデータ記述子
  • を有する.
  • ageは、'configurable''enumerable''get''set'の4つの属性記述子を有し、総称してディスクリプタ
  • にアクセスする.
    configrable
    enumerable
    value
    writable
    get
    セット
    データ記述子
    はい
    はい
    はい
    はい
    No.
    No.
    アクセスディスクリプタ
    はい
    はい
    No.
    No.
    はい
    はい
    オブジェクトの属性記述子は、Object.definePropertyおよびObject.definePropertiesによって修正されてもよい(configurabletrueである場合)詳細は、MDNマニュアルObject.definePropertyを参照することができる.
    これを理解した後、今日のテーマに関連した、つまり'enumerable'という属性記述子です.trueという値があるとき、「列挙可能」と呼びます.属性は列挙可能かどうかは、元の方法を用いてオブジェクトを遍歴したときの結果に影響を及ぼします.本明細書の後で詳しく説明します.
    属性記述子を把握して、自分の後のコードの作成やソースフレームのソースコードの学習は、非常に基本的で重要です.ここでは紹介だけを行い、本文のテーマに関連する属性に焦点を当てています.
    頻繁に巡回する方法
    for.in.遍歴
    自身およびプロトタイプチェーン上のすべての列挙可能な属性を遍歴します.
    サンプルコード:
    var Person = function({name='none', age=18, height=170}={}){
      this.name = name;
      this.age = age;
      this.height = height;
    }
    
    Person.prototype = {
      type: 'Animal'
    }
    
    var qiu = new Person()
    
    //  height          
    Object.defineProperty(qiu, 'height', {
      enumerable: false
    })
    
    for(let n in qiu){
      console.log(n);
    }
    
    // output: name age type
    
    上記のコードに示すように、for.in.を使用して、オブジェクト自体とそのプロトタイプチェーン上のすべてのエニュメレーション属性を巡回します.往々にして私たちはプロトタイプチェーン上の属性も遍歴する必要がないので、よく次のような処理が必要です.
    for(let n in qiu){
      //              
      if(qiu.hasOwnProperty(n)){
        console.log(n)
      }
    }
    
    for.in.が実行されている間、プロトタイプチェーンの検索も行われていますので、オブジェクト自体を遍歴するだけで、性能に影響があります.
    Object.keysエルゴード
    オブジェクト自体を含む、列挙可能な属性のすべての行列を返します.
    サンプルコード:
    var Person = function({name='none', age=18, height=170}={}){
      this.name = name;
      this.age = age;
      this.height = height;
    }
    
    Person.prototype = {
      type: 'Animal'
    }
    
    var qiu = new Person()
    
    //  height          
    Object.defineProperty(qiu, 'height', {
      enumerable: false
    })
    
    var keys = Object.keys(qiu);
    console.log(keys)
    // output: ['name', 'age']
    
    上記のコードを通して、Object.keysはオブジェクト自体を巡回して、すべてのエニュメレーション可能な属性を1つの配列に結合して返します.多くの場合、実は私達が必要なのは、このような機能です.例えば以下のようにキータイプのクエリーparamをurlのqueryに変換すると、コード量が少なく論理がはっきりしているだけでなく、チェーン式の書き方で全体がより優雅になります.
    const searchObj = {
      title: 'javascript',
      author: 'Nicolas',
      publishing: "O'RELLY",
      language: 'cn'
    }
    let searchStr = Object.keys(searchObj)
                    .map(item => `${item}=${searchObj[item]}`)
                    .join('&');
    let url = `localhost:8080/api/test?${searchStr}`
    
    キーの値が合っているデータを遍歴する時、Object.keysを使うのは本当に不二です.
    Object.getOwn PropertyNamesエルゴード
    オブジェクト自体(相続を含まない)属性名を含む配列を返します.
    サンプルコード:
    var Person = function({name='none', age=18, height=170}={}){
      this.name = name;
      this.age = age;
      this.height = height;
    }
    
    Person.prototype = {
      type: 'Animal'
    }
    
    var qiu = new Person()
    
    //  height          
    Object.defineProperty(qiu, 'height', {
      enumerable: false
    })
    
    var keys = Object.getOwnPropertyNames(qiu);
    console.log(keys)
    // output: ['name', 'age', 'height']
    
    Object.keysとの違いは、Object.getOwn PropertyNamesが、列挙できない属性を返します.このほか、Object.keysの表現と一致しています.
    約束したfor.of.,どうして無効ですか?
    ES 6には、配列遍歴、Set、Mapの遍歴において、ディケンサとfor.of.の循環文法が追加されています.とても便利です.しかし、オブジェクト(Objectの例を指す)に適用すると、ブラウザは異常を投げました.
    const searchObj = {
      title: 'javascript',
      author: 'Nicolas',
      publishing: "O'RELLY",
      language: 'cn'
    }
    
    for(let n of searchObj){
      console.log(n)
    }
    // Uncaught TypeError: searchObj is not iterable
    
    そうです.これは間違ったプレゼンテーションです.ES 6では、オブジェクトはデフォルトでは反復可能なオブジェクトではなく、「Symbol.iterator」属性がないと表現されています.以下のコードで比較できます.
    const searchObj = {
      title: 'javascript',
      author: 'Nicolas'
    };
    const bookList = ['javascript', 'java', 'c++'];
    const nameSet = new Set(['Peter', 'Anna', 'Sue']);
    
    console.log(searchObj[Symbol.iterator]); // undefined
    console.log(bookList[Symbol.iterator]); // function values(){[native code]}
    console.log(nameSet[Symbol.iterator]); // function values(){[native code]}
    
    //  ,Set、Map、Array [Symbol.iterator]           ,      ,      
    
    for.of.サイクルは、実際には、シーケンス代数器(または任意の反復可能なオブジェクト、ジェネレータ関数など)の値を指定変数に与えて循環する構文であり、オブジェクトがデフォルトのディテール代数器を持っていない場合は、ループはもちろんできないが、オブジェクトにデフォルトのディテール代数器、すなわち「Symbol.iterator」属性を追加することにより、次のコードが実現される.
    Object.prototype[Symbol.iterator] = function *keys(){
      for(let n of Object.keys(this)){ //     Object.keys          
        yield n
      }
    }
    
    const searchObj = {
      title: 'javascript',
      author: 'Nicolas',
      publishing: "O'RELLY",
      language: 'cn',
    };
    
    for(let key of searchObj){
      console.log(key)
    }
    // output: title author publishing language
    
    以上のコードは確かに対象のすべてのキー名を獲得しました.ジェネレータ関数の中で、Object.keysを使ってすべての列挙可能な属性値を獲得しました.しかし、これはすべての人が望んでいるわけではありません.たぶん、小明が数え切れない属性値も遍歴されているかもしれません.どのように遍歴するかには以下のいくつかの要素があります.まとめてみると、対象のpropertyは少なくとも三つの要素があります.
  • 属性が列挙可能かどうか、すなわち、そのenumerable属性記述子の値である.
  • 属性のタイプは、文字列タイプですか?それともSymbolタイプですか?
  • 属性が所属していますが、プロトタイプが含まれていますか?それともインスタンス自体が含まれていますか?
  • 各方面の意見が違っていて、既存の遍歴方式が満足できるため、標準グループは[Symbol.iterator]を加入していません.ES 6サブジェネレータ、ジェネレータの詳細については、ES 6のサブジェネレータとジェネレータ(Generator)を参照してください.