Learn the javascript loop _ javascript skills

Source: Internet
Author: User
Tags allkeys
This article not only explains the javascript cycle, but also provides some tips for prototype. You are welcome to read this article. 1. prioritize the use of arrays rather than Object types to represent Ordered Sets

The ECMAScript standard does not specify the storage order of attributes of the JavaScript Object type.

However, when we use the for .. in loop to traverse the attributes of an Object, we do need to rely on some order. Because ECMAScript does not clearly regulate this order, every JavaScript execution engine can implement it according to its own characteristics, so it cannot be guaranteed in different execution environments .. the behavior of the in loop is consistent.

For example, the result of the following code calling the report method is uncertain:

function report(highScores) {   var result = "";   var i = 1;   for (var name in highScores) { // unpredictable order     result += i + ". " + name + ": " +     highScores[name] + "\n";     i++;   }   return result; } report([{ name: "Hank", points: 1110100 }, { name: "Steve", points: 1064500 }, { name: "Billy", points: 1050200 }]); // ? 

If you do need to ensure that the running result is based on the data sequence, the array type is used to represent the data first, rather than the Object type directly. At the same time, we also try to avoid using the for... in loop, while using an explicit for Loop:

function report(highScores) {   var result = "";   for (var i = 0, n = highScores.length; i < n; i++) {     var score = highScores[i];     result += (i + 1) + ". " +     score.name + ": " + score.points + "\n";   }   return result; } report([{ name: "Hank", points: 1110100 }, { name: "Steve", points: 1064500 }, { name: "Billy", points: 1050200 }]); // "1. Hank: 1110100 2. Steve: 1064500 3. Billy: 1050200\n" 

Another behavior that is particularly dependent on sequence is the calculation of floating point numbers:

var ratings = {   "Good Will Hunting": 0.8,   "Mystic River": 0.7,   "21": 0.6,   "Doubt": 0.9 }; 

In Item 2, the addition operation of floating point numbers cannot even meet the exchange law:
(0.1 + 0.2) + 0.3 and 0.1 + (0.2 + 0.3) are
0.600000000000001 and 0.6

Therefore, the arithmetic operations on floating point numbers cannot use any sequence:

var total = 0, count = 0; for (var key in ratings) { // unpredictable order   total += ratings[key];   count++; } total /= count; total; // ? 

When the traversal order of for... in is different, the final total result will be different. The following are the two calculation orders and their corresponding results:

(0.8 + 0.7 + 0.6 +0.9) / 4 // 0.75(0.6 + 0.8 + 0.7 +0.9) / 4 // 0.7499999999999999

Of course, there is a solution to the floating point calculation problem that is expressed by the integer number. For example, we first enlarge the floating point number by 10 times to the integer data, then, the computation is reduced by 10 times after the computation is completed:

(8+ 7 + 6 + 9) / 4 / 10  // 0.75(6+ 8 + 7 + 9) / 4 / 10  // 0.75

2. Never add the Enumerable attribute to Object. prototype.

If your code depends on the for .. in loop to traverse the attributes of the Object type, do not add any enumerated attributes to the Object. prototype.

However, when enhancing the JavaScript execution environment, you often need to add new attributes or methods to the Object. prototype Object. For example, you can add a method to get all attribute names of an object:

Object.prototype.allKeys = function() {   var result = [];   for (var key in this) {     result.push(key);   }   return result; }; 

However, the result is as follows:

({ a: 1, b: 2, c: 3}).allKeys(); // ["allKeys", "a", "b","c"]

A feasible solution is to use functions instead of defining the new method on Object. prototype:

function allKeys(obj) {   var result = [];   for (var key in obj) {     result.push(key);   }   return result; } 

However, if you do need. add a new property on prototype, and do not want this property to be in .. the in loop is traversed, so you can use the Object provided by the ES5 environment. defineProject method:

Object.defineProperty(Object.prototype, "allKeys", {   value: function() {     var result = [];     for (var key in this) {       result.push(key);     }     return result;   },   writable: true,   enumerable: false,   configurable: true }); 

The key part of the above Code is to set the enumerable attribute to false. In this way, this attribute cannot be traversed in the for .. in loop.

3. for array traversal, The for loop is preferred, instead of the for... in loop.

Although this problem has been mentioned in the previous Item, can you see the final mean for the following code?

var scores = [98, 74, 85, 77, 93, 100, 89]; var total = 0; for (var score in scores) {   total += score; } var mean = total / scores.length; mean; // ? 

After calculation, the final result is 88.

But do not forget that in the for... in loop, keys are always traversed, not values, as in arrays. Therefore, the score in the for .. in loop is not a series of indexes such as 98 and 74, but 0 and 1.

So you may think the final result is:
(0 + 1 +... + 6)/7 = 21

But the answer is also wrong. Another key point is that the key type in the for... in loop is always a string type, so the + operator here actually performs the String concatenation operation:

The final total is actually the string 00123456. The value of the string converted to the numeric type is 123456, and then divided by the number of elements 7. The final result is as follows: 17636.571428571428.

Therefore, it is best to use a standard for loop for Array traversal.

4. The Traversal method rather than the loop is preferred.

When using loops, it is easy to violate the DRY (Don't Repeat Yourself) principle. This is because we usually choose the copy and paste method to avoid repeating statements in the handwritten segment. However, in this way, a large number of repeated codes appear in the code, and developers are also making repeated wheels meaningless ". More importantly, when copying and pasting, it is easy to ignore the details in the loop, such as the initial index value and the condition for terminating the judgment.

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

For (var I = 0; I <= n; I ++ ){...} // The condition for termination is incorrect. It should be I <nfor (var I = 1; I <n; I ++ ){...} // The start variable is incorrect. It should be I = 0for (var I = n; I> = 0; I --){...} // The start variable is incorrect. It should be I = n-1for (var I = n-1; I> 0; I --){...} // The condition for termination is incorrect. It should be I> = 0

It can be seen that some details of the loop are prone to errors. The closure provided by JavaScript (see Item 11) can encapsulate the loop details for reuse. In fact, ES5 provides some methods to solve this problem. The Array. prototype. forEach is the simplest one. Using it, we can write the loop like this:

// Use the for loop for (var I = 0, n = players. length; I <n; I ++) {players [I]. score ++;} // use forEachplayers. forEach (function (p) {p. score ++ ;});

In addition to traversing the set object, another common mode is to perform some operation on each element in the original set and then get a new set, we can also implement the following using the forEach method:

// Use the for loop var trimmed = []; for (var I = 0, n = input. length; I <n; I ++) {trimmed. push (input [I]. trim ();} // use forEachvar trimmed = []; input. forEach (function (s) {trimmed. push (s. trim ());});

However, ES5 also provides the Array. prototype. map Method to make the code simpler and more elegant because it is very common to convert a set into another one:

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

In addition, a common mode is to filter a set based on certain conditions and then obtain a subset of the original set. ES5 provides Array. prototype. filter to implement this mode. This method accepts a Predicate as the parameter. It is a function that returns true or false. If true is returned, the element is retained in the new set; if false is returned, the element will not appear in the new set. For example, we use the following code to filter the prices of products and only retain the products with prices in the [min, max] range:

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

Of course, the above method is available in the environment that supports ES5. In other environments, we have two options: 1. using third-party libraries, such as underscore or lodash, they all provide a considerable number of common methods to operate on objects and collections. 2. Define as needed.

For example, the following method is defined to obtain several elements in the Set Based on a 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]

To better reuse this method, we can define it on the Array. prototype object. For the specific impact, see 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]

In only one scenario, loop is better than traversal: When break and continue are required. For example, when forEach is used to implement the preceding takeWhile method, there will be a problem. What should we do if predicate is not satisfied?

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

We can use an internal exception for judgment, but it is also 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;}

However, after using forEach, the Code is even longer than before using it. This is obviously a problem. For this problem, ES5 provides some and every methods to deal with loops with early termination. 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

Both methods are Short-circuit methods (Short-circuiting): if any element returns true in the predicate of some methods, some will return; the every method returns false only if any element is returned in the predicate of the every method.

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

In fact, this is the idea of functional programming. In function programming, you rarely see an explicit for loop or while loop. Loop details are well encapsulated.

5. Summary

  • When using the for .. in loop, do not rely on the traversal order.
  • When you use the Object type to save data, you must ensure that the data is unordered.
  • To represent a set with sequence, the array type is used instead of the Object type.
  • Avoid adding any attribute to Object. prototype.
  • If it is really necessary to add method attributes to Object. prototype, consider using an independent function instead.
  • You can use Object. defineProperty to add attributes that can not be traversed by the for... in loop.
  • When traversing an array, use a standard for loop instead of a for... in loop.
  • When necessary, consider pre-saving the length of the array to improve performance.
  • Use the traversal methods Array. prototype. forEach and Array. prototype. map to replace the loop, so that the code is clearer and readable.
  • For repeated loops, you can consider abstracting them. Implemented by a third party or by yourself.
  • Explicit loops are useful in some cases, and some or every methods can be used accordingly.

The above is all the content of this article. I hope that you can better understand the principles of javascript loops and make common progress.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.