The JavaScript language is designed to be so flexible that you have to be careful to fall into the trap of using it.

  • 译 文: Top 10 bugs and their bugs fixing
  • Translator: Fundebug

To ensure readability, free translation rather than literal translation is used in this paper. In addition, the copyright of this article belongs to the original author, and translation is for study only.

JavaScript is almost 100% used on websites today. JavaScript looks like a very simple language, but it’s not. It has a lot of details that are easy to get wrong and cause bugs if you don’t pay attention to them.

1. Incorrectly referencing this

In closures or callbacks, the scope of the this keyword can easily be mistaken. Here’s an example:

Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(function() { this.clearBoard(); // What does this mean? }, 0); };Copy the code

If we execute the above code, we will see an error:


                                                    
Uncaught TypeError: undefined is not a function
 Copy the code

Here’s why it went wrong: When you call setTimeout, you’re actually calling window.settimeout (). The anonymous function passed in setTimeout is in the window object environment, so this refers to the window, but the window does not have clearBoard methods.

How to solve it? Define a new variable reference to this of the Game object, and then use it.


                                                    
Game.prototype.restart = function () {
  this.clearLocalStorage();
  var self = this;   // 将this指向的对象绑定到self
  this.timer = setTimeout(function(){
    self.clearBoard();
  }, 0);
};
 Copy the code

Or use bind() :

Game.prototype.restart = function () { this.clearLocalStorage(); this.timer = setTimeout(this.reset.bind(this), 0); // bind to 'this' }; Game.prototype.reset = function(){ this.clearBoard(); // this is referenced correctly};Copy the code

2. Block scope-related bugs

In most programming languages, each function block has a separate new scope, but not in JavaScript. Such as:

for (var i = 0; i < 10; i++) { /* ... */ } console.log(i); // What is output?Copy the code

In this case, a call to console.log() usually yields undefined or an error. But this is going to print 10. In JavaScript, the variable I persists even after the for loop has ended and the last value is recorded. Some developers forget this, which leads to a lot of bugs. We can eliminate this problem by using let instead of for.

3. Memory leaks

You need to monitor memory usage because leaks are hard to avoid. Memory leaks can result from references to nonexistent objects or circular references.

  • How to avoid it: Reachability.
  • Accessible objects:
    • Objects accessible from anywhere in the existing Call Stack
    • Global object

When an object is accessible by reference, it is stored in memory. The browser garbage collector only collects unreachable objects.

4. Confused equality judgment

JavaScript automatically converts all variable types in Boolean environments to Boolean types, but this can lead to bugs. For example:

// All are true console.log(false == '0'); console.log(null == undefined); console.log(" \t\r\n" == 0); console.log('' == 0); If ({}) //... If ([]) / /...Copy the code

{} and [] are both objects that are converted to true. To prevent bugs, it is recommended to use === and! ==, because there is no implicit type conversion.

Inefficient DOM operations

In JavaScript, you can easily manipulate the DOM(add, modify, and delete), but developers tend to do it inefficiently. This can lead to bugs because these operations are computationally expensive. To solve this problem, Document fragments are recommended if you need to manipulate multiple DOM elements.

AD: Is your online code bug-free? Welcome to Fundebug for free! We can help you find bugs the first time!

6. Wrong function definition in for loop

For example:

var elements = document.getElementsByTagName('input'); var n = elements.length; For (var I = 0; i < n; Onclick = function() {console.log(" #" + I); }; }Copy the code

If we have 10 elements, clicking on any of them will show “Element number #10”! Because by the time onclick is called, the for loop has already ended, all I’s are 10.

Solution:

var elements = document.getElementsByTagName('input'); var n = elements.length; Var makeHandler = function(num) {outer function return function() {// inner function Console. log(" element number ##" + num); }; }; for (var i = 0; i < n; i++) { elements[i].onclick = makeHandler(i+1); }Copy the code

The makeHandler is called immediately when the for loop executes, gets the current value I +1, and stores it in the variable num. MakeHandler returns a function using the num variable that is bound to the element’s click event.

7. Inheritance by stereotype error

Developers who don’t understand inheritance correctly can write buggy code:

BaseObject = function(name) { if(typeof name ! == "undefined") { this.name = name; } else { this.name = 'default' } }; var firstObj = new BaseObject(); var secondObj = new BaseObject('unique'); console.log(firstObj.name); // -> output 'default' console.log(secondobj.name); // -> output 'unique'Copy the code

However, if we do the following:


                                                    
delete secondObj.name;
 Copy the code

So:

console.log(secondObj.name); // -> output 'undefined'Copy the code

What we really want is to print the default name.


                                                    
BaseObject = function (name) {
    if(typeof name !== "undefined") {
        this.name = name;
    }
};
BaseObject.prototype.name = 'default';
 Copy the code

Every BaseObject inherits the Name attribute, and the default value is default. If the name attribute of secondObj is removed, the prototype chain lookup will return the correct default value.

var thirdObj = new BaseObject('unique'); console.log(thirdObj.name); // -> output 'unique' delete thirdobj.name; console.log(thirdObj.name); // -> print 'default'Copy the code

Invalid references in instance methods

Let’s implement a simple constructor to create an object:


                                                    
var MyObject = function() {}
MyObject.prototype.whoAmI = function() {
    console.log(this === window ? "window" : "MyObj");
};
var obj = new MyObject();
 Copy the code

For ease of use, we define the variable whoAmI to reference obj. WhoAmI:


                                                    
var whoAmI = obj.whoAmI;
 Copy the code

Print it out and see:


                                                    
console.log(whoAmI);
 Copy the code

The console will print:


                                                    
function () {
    console.log(this === window ? "window" : "MyObj");
}
 Copy the code

Now let’s compare the two calls:

obj.whoAmI(); // print "MyObj" (as expected) whoAmI(); // print "window"Copy the code

When we assign obj.whoAmI to whoAmI, the new variable whoAmI is defined globally, so this refers to the global window, not MyObj. If we really want to get a reference to MyObj’s function, we need to be in its scope.

var MyObject = function() {} MyObject.prototype.whoAmI = function() { console.log(this === window ? "window" : "MyObj"); }; var obj = new MyObject(); obj.w = obj.whoAmI; // still in the scope of obj obj.whoami (); // output "MyObj" obj.w(); / / output MyObj ""Copy the code

9. The first argument of setTimeout/setInterval is an incorrect string

If you put a string as a setTimeout/setTimeInterval, it will be passed to the constructor function and construct a new function. This process is slow and inefficient, leading to bugs.

var hello = function(){ console.log("hello, fundebug !" ); } setTimeout("hello", 1000);Copy the code

A good alternative is to pass functions as arguments:

setInterval(logTime, 1000); // Pass the logTime function setTimeout(function() {// pass an anonymous function logMessage(msgValue); }, 1000);Copy the code

10. Failed to use strict mode

Using Strict Model adds a lot of restrictions to enforce security and prevent certain errors, but if you don’t use Strict Mode, you’ll have one less helper to help you avoid errors:

  • Easier debugging
  • Avoid accidentally defining global variables that shouldn’t be defined
  • Avoid the this implicit conversion
  • Avoid reuse of attribute names or parameter values
  • Eval () is more secure
  • Invalid use of delete automatically throws an error
















Are your users experiencing bugs?

Experience the Demo
Free to use