In JavaScript, data types can be divided into basic data types and reference data types:

  • Basic data types: Undefined, Null, Boolean, Number, String, Symbol
  • Reference data types: Object, Function, Array, Date, etc

Basic data types

1, Undefined and Null

🐰 Similarities:

  • They both have a single literal value, undefined and NULL;
  • Boolean values are false;
  • If an object is Undefined or Null, a reference error will occur when accessing the property:
let a
let b = null
console.log(a.name)
// Uncaught TypeError: Cannot read property 'name' of undefined
console.log(b.name)
// Uncaught TypeError: Cannot read property 'name' of null
Copy the code

🦁️ Differences:

  • Typeof returns different types:
let a
let b = null
typeof a // 'undefined'
typeof b // 'object'
Copy the code
  • Through the call call Object. The prototype. ToString function returns the results:
let a
let b = null
Object.prototype.toString.call(a)
// '[object Undefined]'
Object.prototype.toString.call(b)
// '[object Null]'
Copy the code
  • When converted to a string, null is converted to the string ‘null’ and undefined to the string ‘undefined’.
  • When converted to a numeric type, undefined is converted to NaN and cannot participate in the calculation. Null is converted to 0 and can participate in the calculation.

PS: Do not explicitly set a variable to undefined! If you need to define a variable to hold objects to be used in the future, you should initialize it to NULL. This not only makes null the convention of null object Pointers, but also helps distinguish null from undefined.

2. What are the “phantom false values”

A false value is a non-Boolean value that has been converted to false after Boolean().

  • An empty string
  • 0 and NaN
  • Null (note that the empty object {} is not a phantom value)
  • undefined

3. Some things to know about the Number type

1. Hidden pits in map() and parseInt() functions

Imagine an array where each element is a string of type Number [‘1′,’2’, ‘3’, ‘4’]. What if we wanted to convert all the elements of the array to integers? We might think of calling the parseInt() function in Array’s map() function:

let arr = ['1'.'2'.'3'.'4']
let result = arr.map(parseInt)
console.log(result)
// [1, NaN, NaN, NaN]
Copy the code

Not exactly what we were expecting [1, 2, 3, 4] ah, why is that?

This is a hidden pit in the map() and parseInt() functions! The above code is equivalent to the following code:

let result = arr.map(function(value, index) {
    return parseInt(value, index)
})
Copy the code

The parseInt() function takes the second argument as the index of the array, but it converts the second argument to the base of the Number type, so the actual processing looks like this:

parseInt('1'.0) / / 1
// Any integer rounded from base 0 returns itself
parseInt('2'.1) // NaN
parseInt('3'.2) // NaN
parseInt('4'.3) // NaN
// The cardinality of parseInt() can only be 2 to 36
// And the value cannot be larger than the base number, so the conversion fails
Copy the code

So we can modify it for our purposes:

let result = arr.map(function(value) {
    return parseInt(value, 10)})Copy the code
IsNaN () and number.isnan ()

ES5 provides the isNaN() function for determining NaN, and ES6 adds the static isNaN() function for Number. If isNaN() provides NaN, why should ES6 add another function? What’s the difference between them?

Let’s look at the use of isNaN to determine if a variable is a NaN. If you pass data of type Number, you can easily determine if it is a NaN. If the argument is not of type Number, it often returns confusing results. Such as:

isNaN({}) // true
Copy the code

The reason why an empty object is a NaN is that isNaN does a type conversion on the data. It checks to see if the variable value passed in can be converted to a number, returning “false” if it can be converted to a number, and “true” if it cannot.

If there are isNaN functions in the global environment, why did ES6 add an isNaN function specifically for the Number type? This is because the previous isNaN function itself was misleading, whereas the number.isnan () function in ES6 determines whether a variable is a NaN in the real sense and does not cast. “True” is returned only if a value of NaN is passed in, and “false” is returned if any other type of value is passed in.

If you want to use the number.isnan () function in ES6 in a non-ES6 environment, there are compatibility solutions:

if(!Number.isNaN){
  Number.isNaN = function(n) {
    // False is returned only if the variable value is NaN
    returnn! ==n } }Copy the code
3. How can 0.1+0.2 not equal 0.3?

We know that the total length of the representation of a floating-point number on a computer is 64 bits, with the highest bit being the sign bit, the next 11 exponent bits, and the lastFifty-two is the decimal placeIs the significant number.Because when floating point numbers use 64-bit storage,It can store up to 52 decimal digitsFor some floating-point decimal numbers with an infinite loop, the first 52 bits are truncated, thusLoss of accuracy0.1+0.2===0.3 is false.

First, the decimal of each floating point number is converted into binary representation according to the method of “integer by 2, order by order”. What you do is you multiply 2 by a decimal number, you get the product, you take the whole part of the product out; And then you multiply 2 times the rest of the decimal, and you get another product; The integral part of the product is removed, and so on, until the fractional part of the product is zero. Then the integer parts are arranged in order, the first integer is taken as the high significant bit of the binary decimal, and the second integer is taken as the low significant bit, and the final result is obtained.

For example, 🌰, the binary representation of 0.1 is:

0.1*2=0.2 // Take the integer part 0
0.2*2=0.4 // Take the integer part 0
0.4*2=0.8 // Take the integer part 0
0.8*2=1.6 // Take the integer part 1
0.6*2=1.2 // Take the integer part 1
0.2*2=0.4 // Take the integer part 0
0.4*2=0.8 // Take the integer part 0
0.8*2=1.6 // Take the integer part 1
0.6*2=1.2 // Take the integer part 1.// Infinite loop
Copy the code

So 0.1 is converted to binary representation as 0.0 0011 0011 0011 0011 0011 0011 0011 0011…… (Infinite loop).

In the same way, the binary conversion of 0.2 is similar to that of 0.1. The calculation process is similar. It starts from 0.2 directly. (Infinite loop).

Will be 0.1 and 0.2 together, and then converted to 52 precision floating-point representation, the result of 0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 11001100, The decimal value is 0.30000000000000004.

How do we solve this loss of precision? One possible idea is to multiply a floating-point number by a certain value (such as its length after decimal places) and convert it to an integer, operate on the integer, and then divide the result by the same value to convert it to a floating-point number and return it. For specific implementation, please refer to “JavaScript Key and Difficult Examples in Detail”.

4. Common algorithm of String type

1. Output the string in reverse order
Idea 1: Reverse () with an array
function reverseStr(str) {
  return str.split(' ').reverse().join(' ')}Copy the code
Idea 2: Use the string’s own charAt()
function reverseStr(str) {
  let res = ' '
  let len = str.length
  for(let i=len-1; i > -1; i--){ res += str.charAt(i) }return res
}
Copy the code
Idea 3: Use the first in last out of the stack
// Write your own code
Copy the code
2. Count the characters that occur most frequently in a string and the number of occurrences
3. Remove duplicate characters from the string
4. Determine if a string is a palindrome string
// LeetCode has both, go to LeetCode slowly brush ~
Copy the code

5. Considerations when using typeof operators

1. The typeof operator distinguishes between Object and Function types

As stated in JavaScript Advanced Programming, functions in ES are technically objects, not data types. However, functions do have some special properties, so it is necessary to distinguish functions from other objects through typeof operators.

Typeof operator handling of NULL
typeof null // 'object'
Copy the code

This is a problem that has existed since the beginning of JavaScript design. In JavaScript, each data type is represented by three bits:

  • 000 indicates the data of Object type
  • 001 indicates data of the Int type
  • 010 represents data of type Double
  • 100 indicates String data
  • 110 represents Boolean data

Since null represents a null pointer, and the median value is 0x00 on most platforms, null’s type label is 0. Therefore, typeof operator is judged as object when used, and “object” is returned. Although a repair solution was proposed in a later proposal, it was not adopted because of its large impact. And so the problem persists.

Reference data types in JS

1. Object types and their instances and static functions

1. What does the new operator do
function Cat(name, age) {
  this.name = name
  this.age = age
}
let cat = new Cat()
Copy the code

On the surface, the main purpose of new is to create an instance of Cat and assign that instance value to the Cat variable, which contains the attributes and functions of the Cat object.

The new operator does four things:

  • First create an empty object, which will be returned as an instance of the object after executing the new constructor () :
let cat = {}
Copy the code
  • Point the proTO of the empty object created above to the constructor’s Prototype property:
cat.__proto__ = Cat.prototype
Copy the code
  • Assign the empty object to this inside the constructor and execute the constructor logic:
Cat.call(cat)
Copy the code
  • Return the object created in the first step or the explicit return value of the constructor according to constructor execution logic:
return cat
Copy the code
Static functions of type Object
1, the Object. The create ()

This function creates and returns an object with the specified stereotype and the specified attribute:

let obj = Object.create(prototype, property)
// prototype will be used as the obj prototype, and property will be used to specify the properties of obj
/ / an 🌰
let obj = Object.create(null, {name: 'Hah'})
Copy the code

If we want to implement an Object.create() ourselves, the core is:

Object.create = function(proto, property){
  function F(){}
  // The middle part is omitted
  F.prototype = proto
  return new F()
}
Copy the code

2. Array type

1. Determine if a variable arr is an array
Array.isArray(arr)
Object.prototype.toString.call(arr) //'[object Array]'
Copy the code
2. Reduce function

Reduce () takes a function as an accumulator, executes the accumulator for each element in the array from left to right, and returns the final result.

array.reduce(callback[,initialValue])
Copy the code

InitialValue is used as the first parameter value for the callback, or if not set, the first element value of the array is used.

Callback receives four parameters (Accumulator, currentValue, currentIndex, and Array).

  • Accumulator represents the return value from the last call to the accumulator, or the initialValue value set. If initialValue is set, accumulator=initialValue; Otherwise, accumulator= the first element value of the array.
  • CurrentValue represents the value that the array is processing.
  • CurrentIndex represents the index of the value currently being processed. If initialValue is set, currentIndex starts at 0, otherwise it starts at 1.
  • Array represents the array itself.

Usage examples 🌰 :

Find the sum of each element of the array
let arr = [1.2.3.4.5]
arr.reduce(function(accumulator,currentValue){
  return accumulator + currentValue
})
/ / 15
Copy the code
Count the number of occurrences of each element in the array
let arr = [1.2.2.3.4.4.4.4.5]
arr.reduce(function(accumulator,currentValue){
  accumulator[currentValue] ? accumulator[currentValue]++ : accumulator[currentValue] = 1
  return accumulator
},{})
{1: 1, 2: 2, 3: 1, 4: 4, 5: 1}
Copy the code

3. Date type

1. Compare date sizes

In actual development, there is often a need to determine whether the start time is before the end. How to achieve this?

General idea: A time string delimited by a slash (/) can be converted directly to an object of type Date and compared directly.

function compareDate(start, end){
  // Replace the passed "-" delimited time string with "/" by regular expression matching
  // The main reason for conversion is to be compatible with various browsers
  let date1 = start.replace('/-/g'.'/ /')
  let date2 = end.replace('/-/g'.'/ /')
  return new Date(date1) < new Date(date2)
}
Copy the code
2. Calculate the date N days before and after the current date

The main idea of getting the date before and after N days is to set the date value. The setDate function is provided in the instance function of the date object to set the date value.

function getDate(count){
  // count indicates the value of N days. Eg: 30 indicates one month later, and -90 indicates three months ago
  let date = new Date(a)// You can pass the parameter to get the specified date before and after N days
  date.setDate(date.getDate() + count)
  let y = date.getFullYear()
  let m = date.getMonth() + 1
  let d = date.getDate()
  return y + The '-' + m + The '-' + d
}
Copy the code

Note: Refer to “JavaScript Key and Difficult Examples”