Learn the looping _javascript skills of JavaScript with me

Source: Internet
Author: User
Tags allkeys integer numbers

1. Use an array instead of an object type to represent a sequential set

The ECMAScript standard does not specify the order in which attributes are stored in the object type of JavaScript.

But in the use for.. In loops, it is true that the properties in object are traversed in a certain order. Because ECMAScript does not explicitly regulate this order, each JavaScript execution engine can be implemented according to its own characteristics, so there is no guarantee for it in different execution environments. The behavior of the in loop is consistent.

For example, the result of the following code when calling the method is indeterminate:

function (highscores) { 
  var result = ""; 
  var i = 1; 
  for (var name in highscores) {//Unpredictable order result 
    + = i + "." + Name + ":" + 
    Highscores[name] + "\ n"; c7/>i++; 
  } 
  return result; 
} 
The [{name: "Hank", points:1110100}, 
{name: "Steve", points:1064500}, 
{name: "Billy", points:1050200 }]); 
// ? 

If you do need to make sure that the results of the run are based on the order of the data, use the array type first to represent the data rather than using the object type directly. Also, try to avoid using for. In loop, and use an explicit for loop:

function (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; 
} 
The [{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 order is the calculation of floating-point numbers:

var ratings = { 
  "Good'll Hunting": 0.8, 
  "Mystic River": 0.7, "0.6": Doubt 
}; 

In Item 2, the addition of floating-point numbers does not even satisfy the Exchange law:
The results of (0.1 + 0.2) + 0.3 and 0.1 + (0.2 + 0.3) were
0.600000000000001 and 0.6

So for the arithmetic operation of floating-point numbers, it is more impossible to use any order:

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

When for.. In the traversal order is not the same, the final result of the total will be different, the following are the two calculation sequence and its 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, one solution to the problem of calculating floating-point numbers is to use integer numbers, for example, we enlarge the above floating-point number 10 times times into integer data and then reduce it by 10 times times after the calculation is over:

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

2, never add an enumerable (enumerable) attribute to the Object.prototype

If your code relies on a for. In loop to walk through the properties in the object type, do not add any enumerable attributes to the Object.prototype.

However, when the JavaScript execution environment is enhanced, it is often necessary to add new properties or methods to the Object.prototype object. For example, you can add a method to get all the property names in an object:

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

But the result is the following:

({a:1, b:2, C:3}). AllKeys (); ["AllKeys", "a", "B", "C"]

A viable solution is to use functions instead of defining new methods on Object.prototype:

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

But if you do need to add new attributes to the Object.prototype, you don't want the attribute to be in the for. is traversed in the loop, you can take advantage of the Object.defineproject method provided by the ES5 environment:

Object.defineproperty (Object.prototype, "AllKeys", { 
  value:function () { 
    var result = []; 
    For (var key, this) { 
      result.push (key); 
    } 
    return result; 
  }, 
  writable:true, 
  enumerable:false, 
  configurable:true 
}); 

The key part of the above code is to set the enumerable property to false. In this case, in the for.. The property cannot be traversed in the in loop.

3. For array traversal, use a for loop instead of for. In loop

Although the last item has said this question, but for the following code, can you see the final average number?

var scores = [the ",", ","); 
var total = 0; 
for (Var score in scores) {total 
  = score; 
} 
var mean = Total/scores.length; 
Mean // ? 

By calculation, the final result should be 88.

But don't forget the for. In the loop, the traversal is always the key, not the value, and the same is true for the array. So the above for.. The score in the in loop is not a series of values such as the expected 98, 74, but a series of indexes such as 0, 1.

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

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

The resulting total is actually a string of 00123456. This string is converted to a numeric type with a value of 123456, and then divided by the number of elements by 7, we get the final result: 17636.571428571428

So, for array traversal, it's best to use a standard for loop

4, priority to use traversal method rather than loop

In the use of loops, it is easy to violate the dry (Don ' t Repeat yourself) principle. This is because we usually choose to copy and paste the method to avoid writing a paragraph of circular statements. But doing so would give the code a lot of repetitive code, and developers would be "reinventing the wheel" without meaning. More importantly, when copying and pasting, it is easy to ignore those details in the loop, such as starting index values, terminating the judgment condition, and so on.

For example, this problem exists for the following for loops, assuming that n is the length of the collection object:

for (var i = 0; I <= N; i++) {...}
Abort condition error, should be I < n for
(var i = 1; i < n; i++) {...}
Starting variable error, should be i = 0 for
(var i = n; I >= 0; i--) {...}
Starting variable error, should be i = N-1 for
(var i = n-1 i > 0; i--) {...}
Abort condition error, should be I >= 0

It is easy to make mistakes in some of the details of a loop. With the closure provided by JavaScript (see item 11), the details of the loop can be encapsulated for reuse. In fact, ES5 provides a number of ways to deal with this problem. The Array.prototype.forEach is one of the simplest. With it, we can write loops like this:

Use for loop
for (var i = 0, n = players.length i < n; i++) {
  players[i].score++;
}

Use a ForEach
players.foreach (function (p) {
  p.score++;
});

In addition to iterating over the collection objects, another common pattern is to do something about each element in the original collection and then get a new one, and we can also use the Foreach method to do the following:

Use for loop
var trimmed = [];
for (var i = 0, n = input.length i < n; i++) {
  Trimmed.push (Input[i].trim ());
}

Use the foreach
var trimmed = [];
Input.foreach (function (s) {
  Trimmed.push (S.trim ());
});

But since this pattern of converting one set to another is very common, ES5 also provides a Array.prototype.map method to make code simpler and more elegant:

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

In addition, a common pattern is to filter the set according to a certain condition and then get a subset of the original set. Array.prototype.filter is provided in ES5 to implement this pattern. The method takes a predicate as an argument, and it is a function that returns TRUE or false: returning true means that the element is kept in the new collection, and false means that the element does not appear in the new collection. For example, we use the following code to filter the price of a commodity, leaving only the price in the [min, Max] Range:

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

Of course, the above approach is available in an environment that supports ES5. In other environments, we have two choices: 1. Using Third-party libraries, such as underscore or lodash, they all provide a considerable number of common methods for manipulating objects and collections. 2. Define yourself as needed.

For example, the following method is defined to get some of the preceding elements in the collection 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,,], function (n) {return
  n <
});//[1, 2, 4, 8]

In order to better reuse this method, we can define it on the Array.prototype object, and the specific effect can be referred 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, 32].takewhile (function (n) {return
  n <
});//[1, 2, 4, 8]

It's better to use loops on only one occasion than with traversal functions: When you need to use break and continue. For example, when using a foreach to implement the above TakeWhile method, there is a problem, when not satisfied with the predicate should be how to achieve it?

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 to judge, but it's 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;
}

But after using foreach, the code is even more verbose than it was before it was used. There is clearly a problem with this. For this issue, ES5 provides a some and every method for handling cycles that have an early termination, and their usage is as follows:

[1, 100].some (function (x) {return x > 5;});  True
[1, 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): As long as any element returns true in the predicate of the Some method, some returns; only one of the elements returns false in the every of the predicate method , the Every method also returns 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;
}

In fact, this is the idea of functional programming. In functional programming, you can rarely see an explicit for loop or a while loop. The details of the loop are nicely encapsulated.

5, summary

    • Using for.. In loops, do not rely on the order in which they are traversed.
    • When you use the object type to save data, you need to ensure that the data in it is unordered.
    • Use an array type instead of an object type when you want to represent a sequential collection.
    • Avoid adding any attributes to the Object.prototype.
    • If it is really necessary to add a method property to the Object.prototype, consider using a standalone function instead.
    • Use Object.defineproperty to add a can not be used for. The property to iterate through in the loop.
    • When traversing an array, use a standard for loop instead of a for. In loop.
    • Consider the length of the previously saved array to improve performance if necessary.
    • Use traversal methods Array.prototype.forEach and Array.prototype.map instead of loops to make your code more readable.
    • For recurring loops, consider abstracting them. Through Third-party-provided methods or by themselves.
    • Explicit loops are useful in some situations, and the some or every methods can be used accordingly.

The above is the entire content of this article, I hope that through this article we better understand the principle of JavaScript cycle, we common progress.

Related Article

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.