1. Data types

(1) Six data types

JavaScript has six data types: Number, String, Boolean, Null, Undefined, and Object

Except for Object, which is a reference type, the rest are primitive types (also known as primitive types). Null and Undefined are two special types

(2) Memory model

When a method is executed, a stack is created, and all variables defined in the method are placed on the stack. The stack is destroyed when the method is called

The stack holds the value of the primitive type and the reference variable of the reference type. The value of the reference variable points to the address of the object in the heap memory

When an object is created, reference variables are stored in the stack, and the object itself is stored in the heap, meaning that the object is not destroyed when the method call ends

In fact, the destruction of objects is determined by the garbage collection mechanism and is only destroyed if the object is not referenced by any reference variable

(3) Pass by value

All function parameters are passed by value in JavaScript, but some interesting behavior can occur due to the memory model

  • When we modify a base variable in a function, we do not modify the value passed in
  • When we modify a reference variable in a function, we modify the value passed in (think about why, as explained by the memory model)
let number = 123 // Basic type
let object = { // Reference type
    name: 'Steve'.age: 18
}

function changeNumber(num) {
    num = 456
}

function changeObject(obj) {
    obj.age = 20
}

changeNumber(number)
changeObject(object)

console.log(number)
console.log(object.age)

/* * Result: * 123 * 20 **/
Copy the code

2. Type detection

(1) the typeof

Used to detect the typeof a variable. Note that for null and array variables, typeof returns object

In addition, typeof returns object for almost any typeof object, meaning that typeof cannot be used to determine exactly what type an object is

let a = 0
let b = 'hello'
let c = true
let d = null
let e = undefined
let f = {}
let g = []
let h = function(){}

console.log('a: ' + typeof a)
console.log('b: ' + typeof b)
console.log('c: ' + typeof c)
console.log('d: ' + typeof d)
console.log('e: ' + typeof e)
console.log('f: ' + typeof f)
console.log('g: ' + typeof g)
console.log('h: ' + typeof h)

* a: number * b: string * C: Boolean * d: object * e: undefined * f: object * g: object * h: function **/
Copy the code

(2) the Object. The prototype. ToString. Call ()

Used to detect the typeof a variable. Typeof cannot detect null and array

let a = 0
let b = 'hello'
let c = true
let d = null
let e = undefined
let f = {}
let g = []
let h = function(){}

function typeOf(value) {
    return Object.prototype.toString.call(value).slice(8, -1)}console.log('a: ' + typeOf(a))
console.log('b: ' + typeOf(b))
console.log('c: ' + typeOf(c))
console.log('d: ' + typeOf(d))
console.log('e: ' + typeOf(e))
console.log('f: ' + typeOf(f))
console.log('g: ' + typeOf(g))
console.log('h: ' + typeOf(h))

/* * Result: * a: Number * b: String * C: Boolean * D: Null * E: Undefined * f: Object * G: Array * h: Function **/
Copy the code

(3) instanceof

It is used to determine whether an instance belongs to a certain type and can solve the problem that Typeof cannot accurately determine object types

var Message = function(descrition) { // constructor
    this.detail = descrition
}

let message = new Message('Hello')
let object = new Object(a)console.log(message instanceof Message)
console.log(message instanceof Object)
console.log(object instanceof Message)
console.log(object instanceof Object)
console.log(Message instanceof Object)

/* * Result: * true * true * false * true * true **/
Copy the code

Instanceof works by determining whether the prototype of the right object exists in the prototype chain of the left object

Once we understand how this works, we can implement an Instanceof ourselves

function myInstanceOf(left, right) {
    let lValue = left.__proto__
    let rValue = right.prototype
    while (true) {
        if (lValue === null) return false
        if (lValue === rValue) return true
        lValue = lValue.__proto__
    }
}
Copy the code

3. Type conversion

(1) Cast type

① String -> Number: parseInt(String [, radix]) & parseFloat(String)

ParseInt parseInt parseInt parseInt parseInt parseInt parseInt parseInt parseInt parseInt parseInt parseInt parseInt ParseFloat is used to parse strings into floating point numbers

Both parse from the first non-space character to the end of the string or to an invalid numeric character

If parsing succeeds, a number is returned; NaN is returned if the first non-space character is not a number or symbol

let a = parseInt(' 123abc')
let b = parseFloat(' 123.456abc')

console.log(a)
console.log(b)

/* * Result: * 123 * 123.456 **/
Copy the code

② Any -> Number: Number()

The conversion rules are as follows:

value Transformation rules
Number Direct output
String If the string is empty, it is converted to 0 if the string contains a valid integer format, it is converted to decimal if the string contains a valid floating-point format, it is converted to hexadecimal otherwise, it is converted to NaN
Boolean If true, convert to 1 and if false, convert to 0
Null Converted to 0
Undefined Converted to NaN
Object Call the Object’svalueOf()Method to convert to NaN according to the above rules, call Object’stoString()Method is converted according to the above rules
let a = Number(123)
let b = Number('456')
let c = Number(true)
let d = Number(false)
let e = Number(null)
let f = Number(undefined)
let g = Number(new Date())
let h = Number(new Array())

console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
console.log(f)
console.log(g)
console.log(h)

/* * Result: * 123 * 456 * 1 * 0 * 0 * NaN * 1578556849729 * 0 **/
Copy the code

③ Any -> String: String()

The conversion rules are as follows:

value Transformation rules
Number Convert to a numeric value
String Direct output
Boolean If true, convert to true and if false, convert to false
Null Converts a null
Undefined Convert undefined
Object Convert according to specific rules
let a = String(123)
let b = String('asdf')
let c = String(true)
let d = String(false)
let e = String(null)
let f = String(undefined)
let g = String({})
let h = String([123.'asdf'.true.false.null.undefined])

console.log(a)
console.log(b)
console.log(c)
console.log(d)
console.log(e)
console.log(f)
console.log(g)
console.log(h)

/* * Result: * 123 * asdf * true * false * null * undefined * [Object object] * 123, ASdf,true,false,, **/
Copy the code

(2) Automatic type conversion

① Conditional judgment

In the condition judgment, the value of the variable will be automatically converted into a Boolean value according to the following rules:

value Transformation rules
Number 0, NaN is converted to false and the rest is converted to true
String The empty string is converted to false and the rest is converted to true
Null Converted to false
Undefined Converted to false
Object Converted to true
let a = 123
let b = ' '
let c = null
let d = undefined
let e = {}

a ? console.log('true') : console.log('false')
b ? console.log('true') : console.log('false')
c ? console.log('true') : console.log('false')
d ? console.log('true') : console.log('false')
e ? console.log('true') : console.log('false')

/* * Result: * true * false * false * false * true **/
Copy the code

② Numerical operation

  • For the + operator, the conversion rules are as follows:

    If none of the operands are strings or objects, they are converted to Number by the Number() function

    If the operand contains a String or object, it is converted to String by the String() function

let a = 1 + true
let b = 1 + null
let c = 1 + undefined

let d = '1' + true
let e = '1' + null
let f = '1' + undefined

let g = '1' + 1
let h = {} + true
let i = [] + false

console.log(a)
console.log(b)
console.log(c)

console.log(d)
console.log(e)
console.log(f)

console.log(g)
console.log(h)
console.log(i)

/* * Result: * 2 * 1 * NaN * 1true * 1NULL * 1undefined * 11 * [object object]true * false **/
Copy the code
  • for-,*,/,%Operator, all pass firstNumber()The function is converted to a number and then evaluated

Based on the implicit conversion rule of numerical operation, we can get a convenient shortcut to convert numbers and strings

// number -> string
let number = 123
let string = number + ' '
console.log(typeof string)
console.log(string)

/* * Result: * string * 123 **/
Copy the code
// string -> number
let string = '123'
let number = string - 0
console.log(typeof number)
console.log(number)

/* * Result: * number * 123 **/
Copy the code

③ Equal operation

When using the == operator to compare whether two variables are equal, if the variables are of different types, an implicit conversion is performed before the comparison, as follows:

The order Variable 1 The variable 2 operation
1 NaN * Returns false
2 Null Undefined Returns true
3 Null Null Returns true
4 Undefined Undefined Returns true
5 Null Except Null, Undefined Returns false
6 Undefined Except Null, Undefined Returns false
7 Boolean * Convert Boolean to Number for comparison
8 Object Object Two objects are equal only if they point to the same memory address
9 Object Number, a String, After converting Object to its original value (Number/String), comparison is used firstvalueOf()Method returns the result if the object can be converted to its original value otherwisetoString()Method returns the result if the object can be converted to its original value, otherwise thrownTypeErrorabnormal
10 Number Number A direct comparison
11 String String A direct comparison
12 String Number Convert String to Number and compare

Practice some questions:

false= =0   // true
false= ='0' // true
false= = []// true
false= = {}// false
123= = [123] // true
2= = {valueOf(){ return 2 }} // true
1= = {valueOf: function(){ return[]},toString: function(){ return 1 }} // true
Copy the code

Because of the implicit conversion when using == for judgment, === is commonly used in projects

4. Assignment, shallow copy and deep copy

For primitive types, assignment, shallow copy, and deep copy copy the value of the old variable to the new variable

let number = 159
let string = 'hello'

let number_copy = number
let string_copy = string

number_copy = 258
string_copy = 'hi'

console.log('number: ' + number) // The value of the original variable does not change
console.log('string: ' + string) // The value of the original variable does not change
console.log('number_copy: ' + number_copy)
console.log('string_copy: ' + string_copy)

/* * Result: * number: 159 * string: hello * number_copy: 258 * string_copy: hi **/
Copy the code

For reference types:

  • After the assignment, the old and new variables point to the same memory address. Changing the new variable will affect the value of the old variable
let object = { first_name: 'Steve'.last_name: 'Jobs' }
let array = [1.3.5]

let object_copy = object
let array_copy = array

object_copy['first_name'] = 'Steven'
array_copy[0] = 2

console.log('object["first_name"]: ' + object['first_name']) // The value of the original variable has changed
console.log('array[0]: ' + array[0]) // The value of the original variable has changed
console.log('object_copy["first_name"]: ' + object_copy['first_name'])
console.log('array_copy[0]: ' + array_copy[0])

/* * Result: * object["first_name"]: Steven * array[0]: 2 * object_copy["first_name"]: Steven * array_copy[0]: 2 **/
Copy the code
  • A shallow copy of a new variable assigns the first layer of data from the old variable to the new variable
// Common shallow copy methods for objects are object.assign
// Common shallow copy methods for arrays are array. from, slice, concat

let object = {
    name: {
        first_name: 'Steve'.last_name: 'Jobs'
    },
    age: 18
}
let array = [[1.3].5.7]

let object_shallow_copy = Object.assign({}, object)
let array_shallow_copy = Array.from(array)

object_shallow_copy['name'] ['first_name'] = 'Steven'
object_shallow_copy['age'] = 20
array_shallow_copy[0] [0] = 2
array_shallow_copy[2] = 11

console.log('object["name"]["first_name"]: ' + object['name'] ['first_name'])
console.log('object["age"]: ' + object['age'])
console.log('array[0][0]: ' + array[0] [0])
console.log('array[2]: ' + array[2])

console.log('object_shallow_copy["name"]["first_name"]: ' + object_shallow_copy['name'] ['first_name'])
console.log('object_shallow_copy["age"]: ' + object_shallow_copy['age'])
console.log('array_shallow_copy[0][0]: ' + array_shallow_copy[0] [0])
console.log('array_shallow_copy[2]: ' + array_shallow_copy[2])

/ execution results: * * * object (" name "] [" first_name "] : Steven * object [] "age" : 18 * array [0] [0] : 2 * array [2] : 7 * object_shallow_copy["name"]["first_name"]: Steven * object_shallow_copy["age"]: 20 * array_shallow_copy[0][0]: 2 * array_shallow_copy[2]: 11 **/
Copy the code

Implement a shallow copy function yourself:

function shallowCopy(object) {
    if (typeofobject ! = ='object') return
    let newObject = object instanceof Array ? [] : {}
    for (let key in object) {
        if (object.hasOwnProperty(key)) {
            newObject[key] = object[key]
        }
    }
    return newObject
}
Copy the code
  • All data of the old variable (no matter how deeply nested) is copied to the new variable. The old and new variables do not affect each other
Parse (json.stringify ())) is the easiest way to do this, but it has some drawbacks
// If the attribute has undefined, NaN, Infinity, function, then it will be converted to null
// If there are RegExp or Error objects in the property, they will be converted to empty objects

let object = {
    name: {
        first_name: 'Steve'.last_name: 'Jobs'
    },
    age: 18
}
let array = [[1.3].5.7]

let object_deep_copy = JSON.parse(JSON.stringify(object))
let array_deep_copy = JSON.parse(JSON.stringify(array))

object_deep_copy['name'] ['first_name'] = 'Steven'
object_deep_copy['age'] = 20
array_deep_copy[0] [0] = 2
array_deep_copy[2] = 11

console.log('object["name"]["first_name"]: ' + object['name'] ['first_name'])
console.log('object["age"]: ' + object['age'])
console.log('array[0][0]: ' + array[0] [0])
console.log('array[2]: ' + array[2])

console.log('object_deep_copy["name"]["first_name"]: ' + object_deep_copy['name'] ['first_name'])
console.log('object_deep_copy["age"]: ' + object_deep_copy['age'])
console.log('array_deep_copy[0][0]: ' + array_deep_copy[0] [0])
console.log('array_deep_copy[2]: ' + array_deep_copy[2])

/ execution results: * * * object (" name "] [" first_name "] : Steve * object [] "age" : 18 * array [0] [0] : 1 * array [2] : 7 * object_deep_copy["name"]["first_name"]: Steven * object_deep_copy["age"]: 20 * array_deep_copy[0][0]: 2 * array_deep_copy[2]: 11 **/
Copy the code

Implement a deep-copy function yourself:

Note that this simple implementation still has some drawbacks

function deepCopy(object) {
    if (typeofobject ! = ='object') return
    let newObject = object instanceof Array ? [] : {}
    for (let key in object) {
        if (object.hasOwnProperty(key)) {
            let item = object[key]
            newObject[key] = typeof item === 'object' ? deepCopy(item) : item
        }
    }
    return newObject
}
Copy the code