This article introduces variables and types in JavaScript in detail from basic principles to practical applications.

First, JavaScript data types

The ECMAScript standard specifies seven data types, which it divides into two types: base and reference data types

The base type

  • StringA sequence of characters representing a text value
  • NumberInteger floating point number
  • BooleanContains two valuestrue.false
  • UndefinedContains only one value:undefined
  • NullContains only one value:null
  • SymbolA unique data type that cannot be changed

BigInt (a seventh primitive type BigInt has been added to ES10 and is now supported by the latest Chrome)

Reference types

  • object(In JS everything except basic data types are objects. Data are objects, functions are objects, and regular expressions are objects.)

The difference between base types and reference types

Reference stack and heap memory explain the difference between base and reference types

Null and undefined

In the base type, there are two types Null and Undefined. They both have one and only one value, Null and Undefined, and they both represent nothing and Null. I usually distinguish them as follows:

null

An object is intentionally assigned to null to indicate that it is empty and has no value.

So it is normal for an attribute of an object to have a value of NULL, which is zero when converted to a value.

undefined

Represents “missing value”, that is, there should be a value here, but it is not defined yet,

Undefined NaN when converted to a value (a special value for a non-numeric value)

4. Symbol type

The Symbol type is a new base type added to ES6.

Each Symbol value returned from Symbol() is unique. A symbol value can be used as an identifier for object properties; This is the only purpose of this data type.

Application scenarios

See Action on Scene > Handwriting Call function

Number type

5.1 Precision Loss

Inaccurate decimals, such as 0.1+0.2! = = 0.3

All the data in the computer is stored in binary, so the computer first converts the data into binary for calculation, and then converts the calculation result into decimal.

In the calculation of 0.1+0.2, the accuracy of binary calculation was lost, resulting in the re-conversion to decimal does not match the expected result.

5.2 the IEEE 754

The IEEE754 standard contains a binary representation of a set of real numbers. It has three parts:

  • The sign bit
  • Index a
  • Mantissa bits

The three precision floating point numbers are as follows:

JavaScriptThe 64-bit double precision floating-point encoding is used, so itsThe sign bitAccount for1Bit, exponent bit11Digit, mantissa digit52position

Now let’s understand what is the sign bit, the exponential bit and the mantissa bit, taking 0.1 as an example:

Its binary is: 0.0001100110011001100…

To save storage space, in computers it is expressed in scientific notation, i.e

1.100110011001100... X 2-4

If this is confusing, think of a decimal number:

The scientific notation for 1100 is 11 X 102

So:

The sign bit is to identify the positive and negative, 1 represents negative, 0 represents positive;

Index bits The index of the storage science counting method;

The mantissa digit stores the significant digit after scientific counting;

So what we usually see as binary is actually the mantissa that the computer actually stores.

5.3 Maximum number that JavaScript can represent

Limited by the IEEE 754 double precision 64-bit specification:

Maximum number represented by exponential potential energy: 1023(decimal)

The maximum mantissa digit that can be expressed is the case where all the mantissa digits are 1

So the largest number that JavaScript can represent is bits

1.111… X 21023 = 1.7976931348623157e+308 in decimal form.

5.4 Maximum security number

MAX_SAFE_INTEGER specifies the maximum safe Number, which is 9007199254740991. There is no loss of precision (except for decimals) within this Number, which is actually 1.111… X 252.

We can also use open source libraries to handle large integers:

  • node-bignum
  • node-bigint

The bigInt type was introduced in ES10 and is now available in Chrome. You can use bigInt to manipulate numbers that exceed the maximum safe number.

Other reference types

In ECMAScript’s definition of types, only the Object type is given. In fact, many of the variables we use to refer to types are not constructed by Object, but their prototype chain ends in Object, and these types are reference types.

  • ArrayAn array of
  • DateThe date of
  • RegExpregular
  • Functionfunction

Packing and unpacking

  • Boxing conversion: Converts the base type to the corresponding packaging type
  • Unboxing: Converts a reference type to a primitive type

Since primitive types cannot extend properties and methods, how do we call methods using primitive types?

Whenever we operate on an underlying type, an object wrapped around the type is automatically created in the background, allowing us to call some methods and properties, such as the following code:

var name = "msd";
var name2 = name.substring(2);
Copy the code

In fact, the following processes occur:

  • To create aStringIs an instance of a wrapper type
  • Called on an instancesubstringmethods
  • Destroy instance

That is, when we call a method with a primitive type, we automatically box and unbox it, just as when we call a method with a Number or Boolean type.

The conversion from a reference type to a basic type — that is, the unpacking — follows the toPrimitive rules of the ECMAScript specification. It usually calls the valueOf and toString methods of the reference type. You can also override the TopeimPron method directly. Conversion to different types of values generally follows different principles, such as:

  • The reference type is converted toNumberType, called firstvalueOfCall again,toString
  • The reference type is converted toStringType, called firsttoStringCall again,valueOf

If neither valueOf nor toString exists, or if no primitive type is returned, TypeError is raised.

const obj = {
  valueOf: () = > { console.log('valueOf'); return 123; },
  toString: () = > { console.log('toString'); return 'ConardLi'; }};console.log(obj - 1);   // valueOf 122
console.log(`${obj}ConardLi`); // toString ConardLiConardLi

const obj2 = {
  [Symbol.toPrimitive]: () = > { console.log('toPrimitive'); return 123; }};console.log(obj2 - 1);   // valueOf 122

const obj3 = {
  valueOf: () = > { console.log('valueOf'); return {}; },
  toString: () = > { console.log('toString'); return{}; }};console.log(obj3 - 1);  
// valueOf  
// toString
// TypeErrorCopy the codeCopy the code

In addition to automatic unpacking and automatic packing in the program, we can also carry out manual unpacking and packing operations. We can call valueOf or toString of wrapper type directly to implement unpacking operation:

var num =new Number("123");  
console.log( typeof num.valueOf() ); //number
console.log( typeof num.toString() ); //string
Copy the code

Type conversion

Because JavaScript is a weakly typed language, type conversions happen very frequently, and the boxing and unboxing mentioned above is actually a type conversion.

There are two types of type conversions. Implicit conversions are the type conversions that are performed automatically by programs, and cast conversions are the type conversions that are performed manually.

Without mentioning casts here, let’s take a look at a few situations where implicit casts can be a headache, and how to do them:

7.1 Type Conversion Rules

If implicit conversions occur, the following rules apply:

7.2 If Statements and logical Statements

In if statements and logical statements, if there is only a single variable, the variable is first converted to Boolean. Only the following cases are converted to false, and the rest are converted to true:

Null undefined '' NaN 0 false Copy codeCopy the code

7.3 Various operational mathematical operators

When we apply the mathematical operator (- * /) to various non-number types, we first convert non-number types to Number types;

1 -true // 0 1-null // 1 1 * undefined // NaN 2 * ['5'] // 10 Copy codeCopy the code

Note that + is an exception, when the + operator is executed:

  • 1. When one side isStringType that is recognized as string concatenation and will preferentially convert the other side to string type.
  • 2. When one side isNumberType, the other side is the original type, the original type is converted toNumberType.
  • 3. When one side isNumberType, on the other side is the reference type, which references the type andNumberType to string concatenation.
123 + '123' // 123123 (Rule 1) 123 + NULL // 123 (Rule 2) 123 + true // 124 (Rule 2) 123 + {} // 123[object object] (Rule 3) Copy the codeCopy the code

7.4 = =

When == is used, if both sides of the type are the same, then the comparison result is the same as ===, otherwise an implicit conversion will occur. The conversion that occurs when == is used can be divided into several different cases (only the two sides of the type are considered) :

  • 1.NaN

NaN always returns false when compared to any other type (including himself).

NaN == NaN // false Copies the codeCopy the code
  • 2.Boolean

Boolean is first converted to the Number type in comparison to any other type.

True == 1 // true true == '2' // false true == ['1'] // true true == ['2'] // false Copies the codeCopy the code

Undefined, null, and Boolean are compared. Although undefined, null, and false can be easily imagined to be false, they compare to false because false is first converted to 0:

Undefined == false // false null == false // false Copy codeCopy the code
  • 3. The String and Number

Compare String and Number. First, convert String to Number.

123 == '123' // true '== 0 // true copies the codeCopy the code
  • 4. A null, and undefined

Null == undefined the comparison result is true. Otherwise, the comparison value of null, undefined, and any other result is false.

null == undefined // true null == '' // false null == 0 // false null == false // false undefined == '' // false Undefined == 0 // false undefined == false // false Copies the codeCopy the code
  • 5. Primitive and reference types

When the original type and reference type are compared, the object type is converted to the original type according to ToPrimitive rules:

'[object object]' = = {} / / true '1, 2, 3 = = [1, 2, 3] / / true copy codeCopy the code

Consider the following comparison:

[] = =! [] // true copies the codeCopy the code

! The priority of the ==,! [] is first converted to false, and then, according to the second point above, false is converted to Number type 0, and left [] is converted to 0, which is more or less equal to both sides.

[null] == false // true [undefined] == false // true Copy codeCopy the code

According to the ToPrimitive rules for arrays, when an array element is null or undefined, the element is treated as an empty string, so both [null] and [undefined] are converted to 0.

So, having said that, it’s recommended to use === to determine if two values are equal…

An interview question is attached

const a = {
  num: 0.valueOf: function() {
    return this.num += 1}};const equality = (a==1 && a==2 && a==3);

const b = { value: [3.2.1].valueOf: function () {return this.value.pop(); }}
Copy the code

How to determine JavaScript data types

8.1 typeof

Applicable scenario

The typeof operator can determine exactly whether a variable is of any of the following primitive types:

typeof 'ConardLi' // string typeof 123 // number typeof true // boolean typeof Symbol() // symbol typeof undefined // Undefined copy codeCopy the code

You can also use it to determine the type of function:

Typeof function(){} // function copies codeCopy the code

Not applicable Scenario

When you use typeof to determine reference types it seems a bit weak:

typeof [] // object typeof {} // object typeof new Date() // object typeof /^\d*$/; // object copies the codeCopy the code

All reference types except functions are judged to be object.

Typeof NULL === ‘object’ also causes headaches. This is a bug that has been around since the first version of JavaScript, and has not been fixed since it caused a lot of compatibility issues…

8.2 instanceof

The instanceof operator helps us determine what type of object the reference type is:

[] instanceof Array // true new Date() instanceof Date // true new RegExp() instanceof RegExp // true copy codeCopy the code

Let’s review a few rules of the prototype chain:

  • 1. All reference types have object properties, that is, they are free to extend properties
  • 2. All reference types have one__proto__(Implicit stereotype) property, which is a normal object
  • 3. All functions haveprototype(Explicit stereotype) property, which is also a normal object
  • 4. All reference types__proto__The value refers to its constructorprototype
  • 5. When trying to get a property of an object, if the variable itself does not have the property, it is destroyed__proto__To look for in

[] instanceof Array determines whether array. prototype is on the prototype chain of [].

So using Instanceof to detect data types is not very accurate, which is not what it was designed for:

[] instanceof Object // true function(){} instanceof Object // trueCopy the code

Also, instanceof doesn’t detect basic data types, so instanceof isn’t a good choice.

See the handwriting instanceof function

reference

Juejin. Cn/post / 684490…