Maison  >  Article  >  interface Web  >  Explication détaillée de la raison pour laquelle la méthode de traversée est préférée au code de boucle en JavaScript

Explication détaillée de la raison pour laquelle la méthode de traversée est préférée au code de boucle en JavaScript

伊谢尔伦
伊谢尔伦original
2017-07-26 17:04:471069parcourir

Priorisez l'utilisation de méthodes de parcours plutôt que de boucles

Lors de l'utilisation de boucles, il est facile de violer le principe DRY (Don't Repeat Yourself). En effet, nous choisissons généralement la méthode copier-coller pour éviter d’écrire à la main des paragraphes de déclarations circulaires. Mais cela entraînera de nombreuses duplications de code dans le code, et les développeurs « réinventeront la roue » sans aucun sens. Plus important encore, il est facile de négliger les détails de la boucle lors du copier-coller, tels que la valeur de l'index de départ, la condition de fin, etc.

Par exemple, la boucle for suivante a ce problème, en supposant que n est la longueur de l'objet de collection :


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

On peut le voir dans quelques détails de la boucle Il est facile de se tromper. Grâce à la fermeture fournie par JavaScript (voir point 11), les détails de la boucle peuvent être encapsulés pour être réutilisés. En fait, ES5 propose quelques méthodes pour résoudre ce problème. Parmi eux, Array.prototype.forEach est le plus simple. En l'utilisant, nous pouvons écrire la boucle comme ceci :


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

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

En plus de parcourir l'objet de collection, un autre modèle courant consiste à parcourir chaque élément de la collection d'origine. Effectuez certaines opérations sur les éléments, puis obtenez un nouvel ensemble. Nous pouvons également utiliser la méthode forEach pour implémenter ce qui suit :


// 使用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());
});

Cependant, en raison de cette méthode de. conversion d'un ensemble Le modèle de mappage d'une autre collection est très courant, et ES5 fournit également la méthode Array.prototype.map pour rendre le code plus simple et plus élégant :


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

De plus, il existe un modèle courant consistant à filtrer la collection selon certaines conditions, puis à obtenir un sous-ensemble de la collection originale. Array.prototype.filter est fourni dans ES5 pour implémenter ce modèle. Cette méthode accepte un Predicate comme paramètre, qui est une fonction qui renvoie true ou false : renvoyer true signifie que l'élément sera conservé dans la nouvelle collection ; renvoyer false signifie que l'élément n'apparaîtra pas dans la nouvelle collection. Par exemple, nous utilisons le code suivant pour filtrer les prix des produits et conserver uniquement les produits dont les prix se situent dans la plage [min, max] :


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

Bien sûr, ce qui précède méthode Est disponible dans les environnements prenant en charge ES5. Dans d'autres environnements, nous avons deux options : 1. Utiliser des bibliothèques tierces, telles que le trait de soulignement ou lodash, qui fournissent un certain nombre de méthodes courantes pour gérer les objets et les collections. 2. Définissez-le selon vos besoins.

Par exemple, définissez la méthode suivante pour obtenir les éléments précédents de l'ensemble en fonction d'une certaine condition :


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]

Afin de mieux réutiliser cette méthode, nous pouvons la définir sur l'objet Array.prototype Pour des effets spécifiques, veuillez vous référer au point 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]

Il n'y a qu'une seule situation dans laquelle utiliser une boucle est préférable à l'utilisation d'une fonction de traversée : lorsque vous devez utiliser break et continue. Par exemple, il y aura des problèmes lors de l'utilisation de forEach pour implémenter la méthode takeWhile ci-dessus. Comment doit-elle être implémentée lorsque le prédicat n'est pas satisfait ?


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

On peut utiliser une exception interne pour porter le jugement, mais c'est aussi un peu maladroit et inefficace :


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;
}

Mais après avoir utilisé forEach, le code est encore plus verbeux qu'avant de l'utiliser. C'est évidemment problématique. Pour ce problème, ES5 fournit toutes les méthodes pour gérer les boucles qui se terminent prématurément. Leur utilisation est la suivante :


[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

Les deux méthodes sont un court-circuit. méthode : tant qu'un élément renvoie true dans le prédicat de la méthode some, alors some retournera ; tant que n'importe quel élément renvoie false dans le prédicat de la méthode each, alors la méthode each renverra également false .

Par conséquent, takeWhile peut être implémenté comme suit :


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;
}

En fait, c'est l'idée de la programmation fonctionnelle. En programmation fonctionnelle, vous voyez rarement des boucles for ou while explicites. Les détails des boucles sont joliment encapsulés.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn