What should you focus on when writing code?

  1. correctness
  2. style
  3. The efficiency of

correctness

When writing code, we should first pay attention to its correctness. If the correctness cannot be guaranteed, the business logic will fail and customers will complain after it is online. It sounds funny, but how can a front-end developer submit the wrong code to the wire? But in real development, we can write the wrong code and not even know it. For example: the pitfalls of the shuffle algorithm.

Shuffle the deck

To implement the shuffle function, we naturally want to use math. random to randomly scatter the cards. The implementation code is as follows:

const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; Function shuffle(cards) {return [...cards].sort(() => math.random () > 0.5? 1:1); }Copy the code

The above code seems to work, but in fact there is a big problem. This way of shuffling cards is not random enough and it is unfair. We could write a program that performs the shuffle function 10,000 times, and then sum up each digit.

const result = Array(10).fill(0); 
for(let i = 0; i < 10000; i++) {
    const testItems = [...cards];
    shuffle(testItems); 
    testItems.forEach((item, idx) => result[idx] += item); 
}
console.log(result);
Copy the code

It turns out that the larger the number, the more likely it is to be in the lower position. The reason for this is that there is a sort algorithm inside the sort method of arrays. We don’t know the implementation of this algorithm, but generally a sort algorithm uses some sort of rule to take two elements in sequence and compare them, and then swap places based on the comparison result. We can change the shuffling algorithm to realize that each card has the same probability of appearing in each position. Firstly, one card is randomly selected and exchanged with the last one, and then one card is extracted from the remaining cards and exchanged with the penultimate position until all the cards are drawn. This algorithm can deduce that the probability of each card appearing in a certain position is the same through mathematical induction, and the derivation process is as follows:

  • If there are 1 cards, each card has a 100% probability of being in the last position;
  • If there are 2 cards, each card has a 1/2 chance of being in the last position;
  • .
  • If there are n cards, each card has a probability of 1/n of being in the last position;

The specific code is as follows:

const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; function shuffle(cards){ const c = [...cards]; for(let i = c.length; i>0; i--){ const pIdx = Math.floor(Math.random() * i); [c[pIdx], c[i-1]] = [c[i-1], c[pIdx]]; } return c; } const result = Array(10).fill(0); for(let i = 0; i < 10000; i++) { const c = shuffle(cards); for(let j = 0; j < 10; j++) { result[j] += c[j]; } } console.log(shuffle(cards)); console.log(result);Copy the code

style

When multiple people collaborate, code programming styles become more important. Many developers fight over whether to write semicolons, indent, curly braces at the end of a line, etc. There is no such thing as a good or bad style, as long as the developer conventions on the project are consistent. Eslint is a great tool for unifying project specifications. Let’s take a look at the NPM package left-pad, which causes the author to log out of the NPM package because of a slot in the code style, and then raises a series of events.

Left – pad

Left-pad, as an NPM package, implements the function of left character completion. At that time, it mainly has the following slots:

  • Particle size splitting is too fine
  • Code style amateur
  • Code quality/efficiency is not high

If we take a closer look at the code, we can understand the author’s intention. This dependency package was released in 2016, before es Module was mature and Tree shaking was powerful enough to remove unused code, so the NPM granularity is understandable. For the code style, it’s actually fine. There are no comments, but the code is semantic. Code is comments. There is indeed room for improvement in code quality and execution efficiency. When a large number of strings need to be completed, the time complexity of filling empty strings before STR characters through loop traversal is O(n). In fact, it can be improved to repeat method of ES6, whose time complexity is O(logn).

Let’s take a look at the source code implementation of repeat:

if (! String.prototype.repeat) { String.prototype.repeat = function(count) { 'use strict'; if (this == null) { throw new TypeError('can\'t convert ' + this + ' to object'); } var str = '' + this; count = +count; if (count ! = count) { count = 0; } if (count < 0) { throw new RangeError('repeat count must be non-negative'); } if (count == Infinity) { throw new RangeError('repeat count must be less than infinity'); } count = Math.floor(count); if (str.length == 0 || count == 0) { return ''; } // Make sure count is a 31-bit integer. This allows us to use the following optimized algorithm. // Currently (August 2014), most browsers do not support strings of 1 << 28 length, so:  if (str.length * count >= 1 << 28) { throw new RangeError('repeat count must not overflow maximum string size'); } var rpt = ''; for (;;) { if ((count & 1) == 1) { rpt += str; } count >>>= 1; if (count == 0) { break; } str += str; } return rpt; }}Copy the code

The above repeat source code implementation, mainly uses the fast power algorithm, the essence is to carry out binary operations, the execution efficiency is relatively high.

MDN: string.prototype.repeat ()

The efficiency of

When we write code, after ensuring correctness, we can consider efficient implementation scheme as far as possible, but we need to consider whether efficient coding is needed in combination with application scenarios. As in the left-pad case above, you don’t need to concatenate too many empty strings in the first half of the string. On the contrary, using repeat is less readable and not easy to understand. Let’s take a look at the power of four case again, to understand that efficient solutions need to be combined with scenarios.

Check if it’s a power of four

To determine if a number is a power of 4, in positive thinking, we divide it by 4 and then mod it, and if there’s a remainder, it’s not a power of 4. The specific implementation code is as follows:

version1

function isPowerOfFour(num){
    num = parseInt(num);
    while(num > 1){
        if(num % 4) return false;
        num /= 4;
    }
    return true;
}
Copy the code

If the number judged is very large, the efficiency of using the above method is not too high. We can improve the code and use the bit operation to shift right 2 bits to replace the division by 4. The specific code is as follows:

version2

function isPowerOfFour(num){
  num = parseInt(num);
  while(num > 1) {
    if(num & 0b11) return false;
    num >>>= 2;
  }
  return true;
}
Copy the code

The time complexity of the above code is O(logn). In fact, it can be further optimized to make the time complexity constant O(1). The specific code is as follows:

version3

function isPowerOfFour(num){
  num = parseInt(num);
  return num > 0 &&
        (num & (num - 1)) === 0 &&
        (num & 0xAAAAAAAA) === 0;
}
Copy the code

Num & (num – 1) === 0; num & (num – 1) === 0; Num & 0xAAAAAAAA is used to determine whether the following 0 is even.

Version3 of the code execution efficiency is high, the code style is very simple, suitable for large numbers to determine whether it is a power of 4. But if it’s just a small number judgment, you don’t need to worry too much about efficient algorithms.

If the number is small, we can use the features just converted into binary numbers and JS regular matching to achieve, the specific code is as follows:

version4

function isPowerOfFour(num){ num = parseInt(num).toString(2); return /^1(? :00)*$/.test(num); }Copy the code

conclusion

We want to really write good JS code, first of all, we need to pay attention to the correctness of the code, to ensure the normal operation of the program online without a bug. Large development teams should also agree on a good code style, and team development is easy to maintain. Then there is the calculation of large numbers of data, etc., and the efficiency of the code should also be concerned. (Considering efficiency, we must combine the specific application scenarios, and try to use efficient algorithms for large data volume; If the amount of data is small and using efficient algorithms will result in more code or less readability, we need to weigh whether it is necessary to use efficient algorithms.)