Interview: Let’s talk about data types in JavaScript

Preface

Please tell me about 

JavaScriptthe data types in ?

In front-end interviews, I think everyone has been asked this question.

Answer: JavascriptThe data types in include primitive types and reference types. The primitive types include nullundefinedbooleanstringsymbolbigIntnumber. Reference types refer to Object.

Yes, I answered the same way, but this is usually the first question. This question can lead to many, many questions, such as

  • NullWhat is the difference between and Undefined? What should we pay attention to when making short judgments on the front end?
  • typeof nullWhy object?
  • Why ES6bring it up Symbol?
  • BigIntWhat problem was solved?
  • Why 0.1 + 0.2 !== 0.3?How do you solve this problem?
  • How to tell if a value is an array?

weakly typed language

Because JavaScriptit is a weakly typed language or a dynamic language. This means that you do not need to declare the type of the variable in advance. The type will be automatically determined during the running of the program, which means that you can use the same variable to store values ​​of different types.

var foo = 42 ;   // foo is a Number now 
foo = "bar" ;   // foo is a String now 
foo = true ;    // foo is a Boolean now

While this feature brings us convenience, it also brings us a lot of type errors. Just imagine, if JSit is a strongly typed language, then there is no way to convert between types, and there will be a layer of isolation or a layer of protection. Will it be easier to maintain? ——This may be TypeScriptthe reason for its birth.

Mastering JavaScriptthe data type is the most basic knowledge point for a front-end

null or undefined

definition

undefinedRepresents an undefined variable. nullValue represents a null object pointer.

Going back to the source: At the beginning, 

JavaScriptthe designer 

Brendan Eichactually just defined 

null

nullas 

Javain, treated as an object. But because 

JavaScriptthere are two data types in : primitive data types and reference data types. 

Brendan EichI feel that the value representing “none” is best not an object.

Therefore Javascript, the design is that null is an object that represents “none” and is 0 when converted to a numerical value; undefined is a primitive value that represents “none” and is NaN when converted to a numerical value.

Number ( null )
 // 0

5 + null 
// 5

Number ( undefined )
 // NaN

5 + undefined 
// NaN

The difference and application of Null and Undefined

null means “no object”, that is, there should be no value there. , typical usage is as follows

  1. As a parameter of a function, it means that the parameter of the function is not an object.
  2. As the end point of the object’s prototype chain.
Object . getPrototypeOf ( Object . prototype )
 // null

undefined means “missing value”, that is, there should be a value here, but it has not been defined . Typical usage is:

  1. When a variable is declared but not assigned a value, it is equal to undefined.
  2. When calling a function, an argument that should have been provided was not provided, and the argument is equal to undefined.
  3. The object has no assigned property, and the value of this property is undefined.
  4. When the function does not return a value, it returns by default undefined.
var i;
i // undefined

function  f ( x ){ console . log (x)}
 f () // undefined

var   o = new  Object ();
o. p  // undefined

var x = f ();
x // undefined

What should you pay attention to when making a short call?

javaScriptFive types of empty values ​​and false values, namely undefined, null, false, “”, 0, NAN

This can sometimes easily lead to some problems, such as

let a = 0 ;
 console . log (a || '/' ); // The original intention is to output '/' as long as a is null or Undefined, but in fact, it will output ' as long as it is one of the five above. /'

Of course we can write

let a = 0 ;
 if (a === null || a === undefined ) {
   console . log ( '/' );
} else {
   console . log (a);
}

It’s not always very elegant, so the ES specification proposes the null value coalescing operator (??)

The null value coalescing operator (??) is a logical operator that returns the right operand when the left operand is null or undefined, otherwise it returns the left operand.

The above example can be written as:

let a = 0 ;
 console . log (a?? '/' ); // 0

typeof null——JS mistakes

typeof  null  // "object"

JavaScriptThe value in is represented by a label representing the type and the actual data value. The first version JavaScriptuses 32 bits to store values, and the type is identified by the low 1 or 3 bits of the value. The type label of the object is 000. as follows

  • 1: Integer type (int)
  • 000: Reference type (object)
  • 010: Double precision floating point type (double)
  • 100: string
  • 110: boolean

But there are two special values:

  • undefined, use the integer −2^30 (negative 2 to the 30th power, not within the range of integers)
  • null, machine code null pointer (C/C++ macro definition), the lower three bits are also 000

Since nullrepresents a null pointer (the lower three bits 000 ),因此,nullare also the type label of ) 000typeof nulltherefore “object” is returned.

This is considered JavaScripta design error, but it cannot be modified. After all, if modified, it will affect the existing code.

Number——0.1+0.2 !== 0.3

Phenomenon

There JavaScriptwill be a phenomenon similar to the following

0.1 + 0.2  
0.30000000000000004

reason

When we operate on floating point numbers, we need to convert decimal to binary. The rules for converting decimal to binary are as follows:

Multiply the number after the decimal point by 2, take the integer part of the result (either 1 or 0), then multiply the decimal part by 2, then take the integer part of the result…and so on, until the decimal part is 0 or 0 It’s OK if the number is enough. Then arrange the integer parts in order

According to the above rules, the final representation of 0.1 is as follows:

0.000110011001100110011 ( 0011 infinite loop)…

Therefore, the loss of precision is not a language problem, but an inherent flaw in floating-point number storage itself.

JavaScriptAll type values ​​are stored in 1-bit double-precision floating point numbers. According to the 64specification , the binary number of 0.1 only retains 52 significant digits, that isNumberIEEE754

1.100110011001100110011001100110011001100110011001101 * 2 ^(- 4 )

In the same way, the binary number of 0.2 is

1.100110011001100110011001100110011001100110011001101 * 2 ^(- 3 )

In this way, accuracy is lost in the conversion between bases. The operation is as follows

0.00011001100110011001100110011001100110011001100110011010 
+ 0.00110011001100110011001100110011001100110011001100110100
-------------------------------------------------- ----------
= 0.01001100110011001100110011001100110011001100110011001110

Therefore, the final calculation result is 0.1 + 0.2 !== 0.3

How to solve

  • Convert number to integer
function  add ( num1, num2 ) {
  const num1Digits = (num1. toString (). split ( '.' )[ 1 ] || '' ). length ;
  const num2Digits = (num2. toString (). split ( '.' )[ 1 ] || '' ). length ;
  const baseNum = Math . pow ( 10 , Math . max (num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}

  • NPMThere are many math libraries that support JavaScriptand on the class library , such as , , etc.Node.jsmath.jsdecimal.jsD.js
  • ES6 adds a very small constant on the
    ES6object——NumberNumber.EPSILON
Number . EPSILON 
// 2.220446049250313e-16 
Number . EPSILON . toFixed ( 20 )
 // "0.00000000000000022204"

The purpose of introducing such a small quantity is to set an error range for floating point calculations. If the error can be less than that Number.EPSILON, we can consider the result to be reliable.

function  withinErrorMargin (left, right) {
     return  Math . abs (left - right) < Number . EPSILON
}
withinErrorMargin ( 0.1 + 0.2 , 0.3 )

Future solution – TC39 Decimal proposal

Proposal currently in Stage 1. The expansion mentioned later BigIntis JSthe positive boundary of , exceeding 2^53 safe integer problems. DecimalIt is to solve the decimal problem of JS -2^53. This proposal introduces a new native type in JS: decimal(suffix m), which declares that the number is a decimal arithmetic.

let zero_point_three = 0. 1m + 0. 2m;
 assert (zero_point_three === 0. 3m);
 // Example function in the proposal 
calculateBill ( items, tax ) {
   let total = 0m;
   for ( let {price, count} of items) { 
    total += price * BigDecimal (count);
  }
  return  BigDecimal . round (total * (1m + tax), { maximumFractionDigits : 2 , round : "up" });
}

let items = [{ price : 1. 25m, count : 5 }, { price : 5m, count : 1 }];
 let tax = .0735m;
 console . log ( calculateBill (items, tax));

Extension—Storage of floating point numbers in memory

So what is the final storage of floating point numbers in memory? EEE754A definition is given for the representation of floating point numbers

(-1)^S M 2^E

The meaning of each symbol is as follows: S is the sign bit, which determines the positive and negative values. When 0, it is a positive number, and when it is 1, it is a negative number. M refers to the number of significant digits, which is greater than 1 and less than 2. E, is the index bit.

Javascript is a 64-bit double-precision floating point number. The highest 1 bit is the sign bit S, the next 11 bits are the exponent E, and the remaining 52 bits are the significant digit M.

You can use this visualization tool to see the binary representation of floating point numbers in memory)

BigInt – Breaking the Biggest Limits

JavaScriptThe Numbertype is a double precision IEEE 754 64-bit floating point type.
The maximum value in JavaScript is 2^53.

BigInt Arbitrary precision numeric type has entered the stage3 specification. BigIntCan represent any large integer. To create one BigInt, we just need to add an n suffix to any integer literal. For example, write 123 as 123n. This global BigInt(number) can be used to convert a Number into a BigInt. The implication is that BigInt(123) === 123n. Now let me use these two points to solve the problems we mentioned earlier:

Symbol——I am the unique and most beautiful boy

definition

ES6 introduces a new primitive data type Symbolto represent unique values

let s = Symbol ();

typeof s
 // "symbol"

Application scenarios

  • Define a set of constants to ensure that the set of constants are not equal. Remove magic string
  • Ensure different property names in objectslet mySymbol = Symbol (); //The first way of writing let a = {}; a[mySymbol] = ‘Hello!’ ; // The second way of writing let a = { [mySymbol]: ‘Hello!’ }; // The third way of writing let a = {}; Object . defineProperty (a, mySymbol, { value : ‘Hello!’ }); // The above writing methods all get the same result a[mySymbol] // “Hello!”
  • Vuein provideand injectprovideand injectallows an ancestor component to inject a dependency into all its descendants, no matter how deep the component hierarchy is, and it will always take effect from the time the upstream and downstream relationships are established. But this is also very intrusive. Using Symbolsas keycan avoid interference with component code and avoid problems such as the same naming.

Array – a special existence in an object

Please tell me how to judge Array?

Why is this question asked?

Because the array is a special existence, it is one of the data structures we usually come into contact with the most. It is a special object, and its index is keythe value of the “ordinary object”. But it also has some methods that “ordinary objects” do not have, such as mapetc.

typeofIt is javascripta natively provided operator to determine the data type. It will return a string representing the data type of the parameter. But we can’t typeofjudge whether it is an array by . Because typeofarrays and ordinary objects nullboth return “object”

const a = null ;
 const b = {};
 const c= [];
 console . log ( typeof (a)); //Object 
console . log ( typeof (b)); //Object 
console . log ( typeof (c )); //Object

How to judge an array

  • Object.prototype.toString.call().
    Every Objectobject that inherits has toStringa method. If toStringthe method is not overridden, it will be returned [Object type], where typeis the type of the object.
const a = [ 'Hello' , 'Howard' ];
 const b = { 0 : 'Hello' , 1 : 'Howard' };
 const c = 'Hello Howard' ;
 Object . prototype . toString . call (a); / /"[object Array]" 
Object . prototype . toString . call (b); //"[object Object]" 
Object . prototype . toString . call (c); //"[object String]"
  • Array.isArray()const a = []; const b = {}; Array . isArray (a); //true Array . isArray (b); //falseArray.isArray()It is ES5a newly added method. If it does not exist Array.isArray(), it can be Object.prototype.toString.call()implemented using
if (! Array . isArray ) {
   Array . isArray = function ( arg ) {
     return  Object . prototype . toString . call (arg) === '[object Array]' ;
  };
}
  • instanceofinstanceofThe operator can be used to determine whether the object pointed to by a constructor’s prototypeproperty exists on the prototype chain of another object to be detected. Because the constructor of the array is Array, it can be judged by the following. Note: Because arrays are also objects, so a instanceof Objectis alsotrue
const a = [];
 const b = {};
 console . log (a instanceof  Array ); //true 
console . log (a instanceof  Object ); //true, the Object constructor console can also be found on the prototype chain of the array
 .log ( b instanceof Array ); //false 
  • constructor. An instance instantiated through a constructor has a constructorproperty.
function  B () {};
 let b = new  B ();
 console . log (b. constructor === B) // true

And arrays are Arrayinstantiated by a function called . so ok

let c = [];
 console . log (c. constructor === Array ) // true

Note: the constructor will be changed. Therefore, it is not recommended to judge this way

let c = [];
c. constructor = Object ;
 console . log (c. constructor === Array ); // false

in conclusion

According to the above description, the personally recommended judgment methods have the following priorities:

isArray>>>Object.prototype.toString.call()instanceofconstructor

Summarize

JavaScriptThis article discusses and analyzes some common data type issues in . I hope it can be helpful to everyone in interviews or daily work. Other things that may not have been mentioned, such as type conversion, etc., will be discussed again when we have the opportunity.

Finally, everyone is welcome to pay attention to my official account – Front-end Grocery Store, and discuss more technical issues~

refer to