JavaScript is full of bugs, and you can easily write bugs.

  • What the F *ck JavaScript?
  • 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 a great language, with a very concise syntax, a large ecosystem, and most importantly, a great community supporting it. At the same time, we know that JavaScript is a tricky language. There are some pits that drive us crazy, and there are some wacky tricks that make us laugh. The idea of this article is derived from Brian Leroux’s talk “WTFJS” at dotJS2012.

My main purpose in collecting these examples is to sort them out and clearly understand how they work. It’s fun to learn a lot of knowledge that I didn’t know before. If you’re a beginner, you can learn more about JavaScript by studying these notes; If you are a professional developer, you can use these notes as a good source of reference. Anyway, just read on and you’ll learn something new.

A function is not a function, right?

⚠️ This is a bug with a lower version, V8(<=5.5), or Node.js(<=7).

// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]

new Foo instanceof null
// > TypeError: function is not a function
/ / > the at... ... ...
Copy the code

Note: This bug does not appear in higher versions tested (Node.js, V8.1.1). If you haven’t upgraded to a higher version, why not give it a try?

An array of addition

What happens if we add two arrays?

[1.2.3] + [4.5.6]  / / - > '1,2,34,5,6'
Copy the code

This is actually a splicing operation, so let’s explain it step by step:

[1.2.3] + [4.5.6]
/ / call the toString ()
[1.2.3].toString() + [4.5.6].toString()
// String concatenation
'1, 2, 3' + '4 and 6'
// ->
'1,2,34,5,6'
Copy the code

Removal of semicolons from arrays

We create an array of four empty elements. As a result, the array actually has only three elements, because the last semicolon has been removed.

let a = [,,,]
a.length     / / - > 3
a.toString() / / - > ', '
Copy the code

Trailing commas (also known as final commas) are useful when adding new elements, parameters, or attributes. If you want to add a new attribute that has a semicolon at the end of the previous line, you can add it to the new line without modifying the previous line. This makes version control diff operations clearer and less problematic. – Trailing commas at MDN

Array equality matching is scary

Please look at:

[] = =' '   // -> true[] = =0    // -> true
[' '] = =' ' // -> true
[0] = =0   // -> true
[0] = =' '  // -> false
[' '] = =0  // -> true

[null] = =' '      // true
[null] = =0       // true
[undefined] = =' ' // true
[undefined] = =0  // true[[]] = =0  // true[[]] = =' ' // true[[[[[[]]]]]] = =' ' // true[[[[[[]]]]]] = =0  // true

[[[[[[ null= =]]]]]]0  // true
[[[[[[ null= =]]]]]]' ' // true

[[[[[[ undefined= =]]]]]]0  // true
[[[[[[ undefined= =]]]]]]' ' // true
Copy the code

Please refer to 7.2.13 Abstract Equality Comparison for details

Undefined and Number

If no arguments are passed to the Number constructor, 0 is returned. If undefined is passed as an argument, NaN is returned.

Number(a)/ / - > 0
Number(undefined) // -> NaN
Copy the code

According to the specification:

  1. If no arguments are passed in, then n=0;
  2. Otherwise, n= ToNumber(value);
  3. If value is undefined, then ToNumnber(undefined) is NaN.

Reference:

  • 20.1.1 The Number Constructor
  • 7.1.3 ToNumber (argument)

JavaScript has a lot of bugs, so use Fundebug!

ParseInt is not a good thing either

ParseInt is famous for its strange behavior:

parseInt('f*ck');     // -> NaN
parseInt('f*ck'.16); / / - > 15
Copy the code

This is because parseInt parses character by character until it encounters an unusable character. The hexadecimal number for f is 15.

Infinity can be converted to the corresponding number:

//
parseInt('Infinity'.10) // -> NaN
// ...
parseInt('Infinity'.18) // -> NaN...
parseInt('Infinity'.19) / / - > 18
// ...
parseInt('Infinity'.23) / / - > 18...
parseInt('Infinity'.24) / / - > 151176378
// ...
parseInt('Infinity'.29) / / - > 385849803
parseInt('Infinity'.30) / / - > 13693557269
// ...
parseInt('Infinity'.34) / / - > 28872273981
parseInt('Infinity'.35) / / - > 1201203301724
parseInt('Infinity'.36) / / - > 1461559270678...
parseInt('Infinity'.37) // -> NaN
Copy the code

Beware of null arguments:

parseInt(null.24) / / - > 23
Copy the code

First, null is translated as the string “null”. N “for 23 in base 24. ParseInt (null, 24) === 23… Wait, what?” The at StackOverflow.

parseInt('n'.24) / / - > 23
Copy the code

Don’t forget base 8:

parseInt('06'); / / 6
parseInt('08'); // 8 if support ECMAScript 5
parseInt('08'); // 0 if not support ECMAScript 5
Copy the code

If the input string starts with 0, it is base 8 or base 10. Which one it is depends on the implementation. For ECMAScript5, the value is in base 10. But not all browsers support it. Therefore, the safest way is to specify the base when calling parseInt.

ParseInt always converts input to a string.

parseInt({ toString: (a)= > 2.valueOf: (a)= > 1 }) / / - > 2
Number({ toString: (a)= > 2.valueOf: (a)= > 1 })   / / - > 1
Copy the code

The math of true and false

true + true / / - > 2
(true + true) * (true + true) - true / / - > 3
Copy the code

Let’s convert true to Number:

Number(true) / / - > 1
Copy the code

Unary plus attempts to convert a parameter to a number. It converts string integers to float, and non-string true, false, and NULL are also converted. For values that cannot be converted, NaN is returned. Therefore, we have a simpler conversion method:

+true / / - > 1
Copy the code

The ToNumber function is called when you use addition or multiplication. By definition:

If true, return 1. If false, return +0. That’s why we can add Boolean values (true, false) to numbers.

Reference:

  • 12.5.6 the Unary + Operator
  • 12.8.3 The Addition Operator (+)
  • 7.1.3 ToNumber (argument)

HTML comments can be used in JavaScript

In JavaScript, use

// valid comment<! -- valid comment tooCopy the code

The purpose of htML-enabled comments is to allow browsers that do not support

Node.js is also based on V8 implementation, so it can be used in Node.js.

Reference: B.1.3 html-like Comments

NaN is Number

NaN is of type ‘number’ :

typeof NaN // -> 'number'
Copy the code

To see how Typeof and Instanceof work, see:

  • 12.5.5 The typeof Operator
  • 12.10.4 Runtime Semantics: InstanceofOperator (O, C)

[] and NULL are objects

typeof []   // -> 'object'
typeof null // -> 'object'

/ / but
null instanceof Object // false
Copy the code

For null, as defined by Typeof, “object” is returned as an object that does not implement [[Call]].

You can use the toString function to check the type of an object (Array, Date, Null) :

Object.prototype.toString.call([])
// -> '[object Array]'

Object.prototype.toString.call(new Date)
// -> '[object Date]'

Object.prototype.toString.call(null)
// -> '[object Null]'
Copy the code