ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript でループ コードよりもトラバーサル メソッドが推奨される理由の詳細な説明

JavaScript でループ コードよりもトラバーサル メソッドが推奨される理由の詳細な説明

伊谢尔伦
伊谢尔伦オリジナル
2017-07-26 17:04:471110ブラウズ

ループの代わりにトラバーサルメソッドを使用することをお勧めします

ループを使用する場合、DRY (Don't Reply Yourself) 原則に違反する可能性があります。これは、循環文の段落を手書きすることを避けるために、通常、コピー&ペーストの方法を選択するためです。しかし、そうするとコード内に重複コードが大量に含まれることになり、開発者は無意味に「車輪の再発明」をすることになります。さらに重要なのは、開始インデックス値や終了条件など、コピー アンド ペーストするときにループ内の詳細を見落としやすいことです。

たとえば、n がコレクション オブジェクトの長さであると仮定すると、次の for ループにはこの問題があります:


for (var i = 0; i <= n; i++) { ... }
// 终止条件错误,应该是i < n
for (var i = 1; i < n; i++) { ... }
// 起始变量错误,应该是i = 0
for (var i = n; i >= 0; i--) { ... }
// 起始变量错误,应该是i = n - 1
for (var i = n - 1; i > 0; i--) { ... }
// 终止条件错误,应该是i >= 0

ループの一部の詳細で間違いを犯しやすいことがわかります。 JavaScript によって提供されるクロージャ (項目 11 を参照) を使用すると、ループの詳細を再利用のためにカプセル化できます。実際、ES5 ではこの問題に対処する方法がいくつか提供されています。その中で、Array.prototype.forEach が最も単純なものです。これを使用すると、次のようなループを作成できます:


// 使用for循环
for (var i = 0, n = players.length; i < n; i++) {
  players[i].score++;
}

// 使用forEach
players.forEach(function(p) {
  p.score++;
});

コレクション オブジェクトの走査に加えて、別の一般的なパターンは、元のコレクションの各要素に対して何らかの操作を実行し、新しいコレクションを取得することです。また、forEach メソッドを使用して以下を実装します:


// 使用for循环
var trimmed = [];
for (var i = 0, n = input.length; i < n; i++) {
  trimmed.push(input[i].trim());
}

// 使用forEach
var trimmed = [];
input.forEach(function(s) {
  trimmed.push(s.trim());
});

しかし、あるコレクションを別のコレクションに変換するこのパターンは非常に一般的であるため、ES5 ではコードをよりシンプルかつエレガントにする Array.prototype.map メソッドも提供しています:


var trimmed = input.map(function(s) {
  return s.trim();
});

さらに、特定の条件に従ってコレクションをフィルターし、元のコレクションのサブセットを取得する別の共通モードがあります。 ES5 では、このモードを実装するために Array.prototype.filter が提供されています。このメソッドは、true または false を返す関数である Predicate をパラメータとして受け取ります。true を返すと、要素が新しいコレクションに保持されることを意味し、false を返すと、要素が新しいコレクションに表示されないことを意味します。たとえば、次のコードを使用して製品の価格をフィルタリングし、[最小、最大] 範囲内の価格を持つ製品のみを保持します:


listings.filter(function(listing) {
  return listing.price >= min && listing.price <= max;
});

もちろん、上記の方法は ES5 をサポートする環境で使用できます。他の環境では、次の 2 つのオプションがあります。 1. オブジェクトやコレクションを操作するための一般的なメソッドを多数提供する、アンダースコアや lodash などのサードパーティ ライブラリを使用します。 2. 必要に応じて定義します。

たとえば、特定の条件に基づいてコレクション内の最初のいくつかの要素を取得するには、次のメソッドを定義します。


function takeWhile(a, pred) {
  var result = [];
  for (var i = 0, n = a.length; i < n; i++) {
    if (!pred(a[i], i)) {
      break;
    }
    result[i] = a[i];
  }
  return result;
}

var prefix = takeWhile([1, 2, 4, 8, 16, 32], function(n) {
  return n < 10;
}); // [1, 2, 4, 8]

このメソッドをより適切に再利用するために、Array.prototype オブジェクトで定義できます。具体的な影響については項目 42 を参照してください。


Array.prototype.takeWhile = function(pred) {
  var result = [];
  for (var i = 0, n = this.length; i < n; i++) {
    if (!pred(this[i], i)) {
      break;
    }
    result[i] = this[i];
  }
  return result; 
};

var prefix = [1, 2, 4, 8, 16, 32].takeWhile(function(n) {
  return n < 10;
}); // [1, 2, 4, 8]

ループを使用する方がトラバーサル関数を使用するよりも優れている場合は 1 つだけです。それは、break と continue を使用する必要がある場合です。 たとえば、forEach を使用して上記の takewhile メソッドを実装する場合、述語が満たされない場合に問題が発生します。


function takeWhile(a, pred) {
  var result = [];
  a.forEach(function(x, i) {
    if (!pred(x)) {
      // ?
    }
    result[i] = x;
  });
  return result;
}

内部例外を使用して判断できますが、少し不器用で非効率でもあります:


function takeWhile(a, pred) {
  var result = [];
  var earlyExit = {}; // unique value signaling loop break
  try {
    a.forEach(function(x, i) {
      if (!pred(x)) {
        throw earlyExit;
      }
      result[i] = x;
    });
  } catch (e) {
    if (e !== earlyExit) { // only catch earlyExit
      throw e;
    }
  }
  return result;
}

しかし、forEach を使用した後のコードは、使用前よりもさらに冗長になります。これは明らかに問題があります。 この問題に対して、ES5 はループの早期終了に対処するためのいくつかのメソッドを提供します。その使用法は次のとおりです:


[1, 10, 100].some(function(x) { return x > 5; }); // true
[1, 10, 100].some(function(x) { return x < 0; }); // false

[1, 2, 3, 4, 5].every(function(x) { return x > 0; }); // true
[1, 2, 3, 4, 5].every(function(x) { return x < 3; }); // false

これら 2 つのメソッドは、ショートサーキットメソッド (ショートサーキット) です。 some メソッドの述語で要素が true を返す場合、every メソッドの述語でいずれかの要素が false を返す限り、some は false を返します。

したがって、takewhile は次のように実装できます:


function takeWhile(a, pred) {
  var result = [];
  a.every(function(x, i) {
    if (!pred(x)) {
      return false; // break
    }
    result[i] = x;
    return true; // continue
  });
  return result;
}

実際、これは関数型プログラミングの考え方です。関数型プログラミングでは、明示的な for ループや while ループはほとんど見られません。ループの詳細がうまくカプセル化されています。

以上がJavaScript でループ コードよりもトラバーサル メソッドが推奨される理由の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。