JavaScriptの4つの配列遍歴方法:for VS forEach()VS for/in VS for/of

15328 ワード

JS騒动の操作.
  • 原文:For vs forEach()vs for/in vs for/of in JavaScript
  • 翻訳者:Fundbug
  • この文章は意訳を採用して、著作権は元の作者の所有になります.
    私たちはJavaScriptの配列やオブジェクトを巡る様々な方法を持っていますが、それらの違いは非常に疑問です.Airbnnコードスタイルはfor/inとfor/ofを使用禁止です.なぜ知っていますか?
    この文章は以下の4つの循環文法の違いを詳しく紹介します.
  • for (let i = 0; i < arr.length; ++i)
  • arr.forEach((v, i) => { /* ... */ })
  • for (let i in arr)
  • for (const v of arr)
  • 構文forおよびfor/inを使用して、実際の配列要素値ではなく、配列の下のスケールにアクセスできます.
    for (let i = 0; i < arr.length; ++i) {
        console.log(arr[i]);
    }
    
    for (let i in arr) {
        console.log(arr[i]);
    }
    
    for/ofを使用すると、配列の要素値に直接アクセスすることができる.
    for (const v of arr) {
        console.log(v);
    }
    
    forEach()を使用すると、配列の下付きおよび要素値に同時にアクセスすることができる.
    arr.forEach((v, i) => console.log(v));
    
    数値以外の属性
    JavaScriptの配列はObjectであり、これは配列に文字列の属性を追加できることを意味します.
    const arr = ["a", "b", "c"];
    
    typeof arr; // 'object'
    
    arr.test = "bad"; //        
    
    arr.test; // 'abc'
    arr[1] === arr["1"]; // true, JavaScript       Object
    
    4つの循環文法は、for/inだけが非デジタル属性を無視しない:
    const arr = ["a", "b", "c"];
    arr.test = "bad";
    
    for (let i in arr) {
        console.log(arr[i]); //   "a, b, c, bad"
    }
    
    だからこそ、for/inを使用して配列を巡回するのはよくない.
    他の3つの循環文法は、数字以外の属性を無視します.
    const arr = ["a", "b", "c"];
    arr.test = "abc";
    
    //    "a, b, c"
    for (let i = 0; i < arr.length; ++i) {
        console.log(arr[i]);
    }
    
    //    "a, b, c"
    arr.forEach((el, i) => console.log(i, el));
    
    //    "a, b, c"
    for (const el of arr) {
        console.log(el);
    }
    
    ポイント:for/inを使用して配列を巡回しないでください.本当にデジタル属性ではないものを遍歴したいなら.ESLintのGard-for-innルールを使用してfor/inを禁止することができます.
    配列の空要素
    JavaScript配列は空の要素があります.以下のコード文法は正しいです.配列長は3です.
    const arr = ["a", , "c"];
    
    arr.length; // 3
    
    さらに不可解なことに、ループステートメント処理['a',, 'c']['a', undefined, 'c']と同じ方式ではない.['a',, 'c']では、for/inforEachは空の要素をスキップし、forfor/ofはスキップしない.
    //   "a, undefined, c"
    for (let i = 0; i < arr.length; ++i) {
        console.log(arr[i]);
    }
    
    //   "a, c"
    arr.forEach(v => console.log(v));
    
    //   "a, c"
    for (let i in arr) {
        console.log(arr[i]);
    }
    
    //   "a, undefined, c"
    for (const v of arr) {
        console.log(v);
    }
    
    ['a', undefined, 'c']については、4つの循環文法が一致し、プリントされたものはすべて「a,undefined,c」である.
    空の要素を追加する方法もあります.
    //    `['a', 'b', 'c',, 'e']`
    const arr = ["a", "b", "c"];
    arr[5] = "e";
    
    もう一つは、JSONも空の要素をサポートしていません.
    JSON.parse('{"arr":["a","b","c"]}');
    // { arr: [ 'a', 'b', 'c' ] }
    
    JSON.parse('{"arr":["a",null,"c"]}');
    // { arr: [ 'a', null, 'c' ] }
    
    JSON.parse('{"arr":["a",,"c"]}');
    // SyntaxError: Unexpected token , in JSON at position 12
    
    ポイント:for/inforEachは空の要素をスキップします.配列中の空の要素は「holes」と呼ばれます.この問題を避けたいなら、無効化を考慮してもいいです.
    parserOptions:
        ecmaVersion: 2018
    rules:
        no-restricted-syntax:
            - error
            - selector: CallExpression[callee.property.name="forEach"]
              message: Do not use `forEach()`, use `for/of` instead
    
    関数のthisforEachforfor/inは、外部作用領域のfor/ofを保持する.thisでは、矢印関数を使用しない限り、そのコールバック関数のthisが変化します.
    Node v 11.8.0を使用して、下記のコードをテストします.結果は以下の通りです.
    "use strict";
    
    const arr = ["a"];
    
    arr.forEach(function() {
        console.log(this); //   undefined
    });
    
    arr.forEach(() => {
        console.log(this); //   {}
    });
    
    ポイント:ESLINEを使用するforEach規則は、すべてのコールバック関数に矢印関数を使用する必要があります.
    Aync/AwaitとGenerators
    もう一つのポイントは、no-arrow-callbackはAync/AwaitとGeneratorsとうまく提携できません.forEach()コールバック関数でawaitは使用できません.
    async function run() {
      const arr = ['a', 'b', 'c'];
      arr.forEach(el => {
        // SyntaxError
        await new Promise(resolve => setTimeout(resolve, 1000));
        console.log(el);
      });
    }
    
    forEachコールバック関数ではyieldを使用できません.
    function* run() {
      const arr = ['a', 'b', 'c'];
      arr.forEach(el => {
        // SyntaxError
        yield new Promise(resolve => setTimeout(resolve, 1000));
        console.log(el);
      });
    }
    
    forEachにとっては、この問題はない.
    async function asyncFn() {
        const arr = ["a", "b", "c"];
        for (const el of arr) {
            await new Promise(resolve => setTimeout(resolve, 1000));
            console.log(el);
        }
    }
    
    function* generatorFn() {
        const arr = ["a", "b", "c"];
        for (const el of arr) {
            yield new Promise(resolve => setTimeout(resolve, 1000));
            console.log(el);
        }
    }
    
    もちろん、for/ofのコールバック関数をasync関数と定義すると、エラーが発生しません.しかし、forEach()を順番に実行させたいなら、頭が痛いです.
    下のコードは大きいサイズから小さいサイズまで0-9を印刷します.
    async function print(n) {
        //   0    1 ,  1    0.9 
        await new Promise(resolve => setTimeout(() => resolve(), 1000 - n * 100));
        console.log(n);
    }
    
    async function test() {
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(print);
    }
    
    test();
    
    ポイント:できるだけforEachでaysnc/awaitやgeneratorsを使わないでください.
    結論
    簡単に言えば、forEachは巡回的に最も信頼できる方法であり、for/ofよりも循環的に簡潔であり、for及びfor/inのように多くの奇怪な特例がない.forEach()の欠点は、インデックス値を取るのが不便であり、チェーンfor/of.forEach()を呼び出すことができないということです.forEach()を使用して配列インデックスを取得し、このように書くことができる.
    for (const [i, v] of arr.entries()) {
        console.log(i, v);
    }
    
    参照
  • For-each over an array in JavaScript?
  • Why is using“for…in”with array iteration a bad idea?
  • Aray iteration and holes in JavaScript
  • Funebugについて
    FunndebugはJavaScript、微信小プログラム、微信小ゲーム、支払宝小プログラム、React Native、Node.js、JavaオンラインでリアルタイムBUG監視を適用します.2016年に双十一が正式にオンラインしてから、Fundebugは累計で10億+エラー事件を処理しました.有料顧客はGoogle、360、金山軟件、庶民網など多くのブランド企業があります.皆さん、無料で試用してください.
    著作権声明
    転載する時、作者のFunebugと本文の住所を明記してください.blog.fundebug.com/2019/03/11/…
    転載先:https://juejin.im/post/5c85bc0ef265da2de970b2cd