Home > Web Front-end > JS Tutorial > body text

Detailed explanation of why traversal method is preferred over loop code in JavaScript

伊谢尔伦
Release: 2017-07-26 17:04:47
Original
1109 people have browsed it

Prefer traversal methods rather than loops

When using loops, it is easy to violate the DRY (Don't Repeat Yourself) principle. This is because we usually choose the copy-paste method to avoid hand-writing paragraphs of circular statements. But doing so will result in a lot of duplicate code in the code, and developers will "reinvent the wheel" meaninglessly. More importantly, it is easy to overlook the details in the loop when copying and pasting, such as the starting index value, termination condition, etc.

For example, the following for loop has this problem. Assume n is the length of the collection object:


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
Copy after login

It can be seen that it is easy to deal with some details of the loop. Something went wrong. Using the closure provided by JavaScript (see Item 11), the details of the loop can be encapsulated for reuse. In fact, ES5 provides some methods to deal with this problem. Among them, Array.prototype.forEach is the simplest one. Using it, we can write the loop like this:


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

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

In addition to traversing the collection object, another common pattern is to traverse each element in the original collection To perform some operation and then get a new collection, we can also use the forEach method to implement the following:


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

But due to this method, one collection is converted into another collection The pattern is very common. ES5 also provides the Array.prototype.map method to make the code simpler and more elegant:


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

In addition, there is another common pattern that is to set Filter according to certain conditions, and then obtain a subset of the original collection. Array.prototype.filter is provided in ES5 to implement this mode. This method accepts a Predicate as a parameter, which is a function that returns true or false: returning true means that the element will be retained in the new collection; returning false means that the element will not appear in the new collection. For example, we use the following code to filter product prices and only retain products with prices in the [min, max] range:


##

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

Of course, the above method is Available in environments that support ES5. In other environments, we have two options: 1. Use third-party libraries, such as underscore or lodash, which provide quite a few common methods to operate objects and collections. 2. Define it as needed.

For example, define the following method to obtain the previous elements in the set based on a certain 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]
Copy after login

In order to better reuse this method, we It can be defined on the Array.prototype object. For specific effects, please refer to Item 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]
Copy after login

There is only one situation where using a loop is better than using a traversal function: when you need to use break and continue. For example, there will be problems when using forEach to implement the takeWhile method above. How should it be implemented when the predicate is not satisfied?


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

We can use an internal exception to make the judgment, but it is also a bit clumsy and inefficient:


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;
}
Copy after login

But after using forEach, the code is even more verbose than before using it. This is obviously problematic. For this problem, ES5 provides some and every methods to handle loops that terminate prematurely. Their usage is as follows:


[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
Copy after login

These two methods are short-circuit Method (Short-circuiting): As long as any element returns true in the predicate of the some method, then some will return; as long as any element returns false in the predicate of the every method, then the every method will also return false.

Therefore, takeWhile can be implemented as follows:


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;
}
Copy after login
In fact, this is the idea of ​​functional programming. In functional programming, you rarely see explicit for loops or while loops. The details of the loops are nicely encapsulated.

The above is the detailed content of Detailed explanation of why traversal method is preferred over loop code in JavaScript. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!