This is the first day of my participation in the August More text Challenge. For details, see: August More Text Challenge

This article will be updated at any time, with the date in the title for easy viewing. Collect column or article not afraid to get lost oh ~ wish to see this little friend happy rich ~

How many data types does JavaScript specify?

8 (7 raw data types + 1 reference data types)

Primitive type value (stored in stack memory) describe
null The variable value is null (typeof null === 'object', variables with a value of null will be garbage collected.)
undefined A variable that has not been assigned defaults to undefined (in the case of variable promotion, the variable is printed before the assignment statement, and the default value is undefined as well)
boolean A variable’s value totrueorfalse
number Numerical type (accuracy loss! The browser returns data to the request. Json is numeric data, and there is an action to erase 0 at the end.
string String (in js addition, the variable is first cast to its original type. If either of the variables on either side of the plus sign is a string, the other variable is also cast to a string.)
symbol A value that is never equal to another value (can be uniquely identified by a private variable inside an object)
bigInt Arbitrary precisionThe integer(Many people think that only decimals experience accuracy loss, but integers do, too.BigInt(n)The generated integer will not suffer accuracy loss)
Reference type value (stored in heap memory) describe
object A set of key-value pairs (with subtypes such as Date/Function/Array)

Ps: It is important to note that the type refers to the type of the value (see πŸ‘‡ for this point).

e.g.

typeof undefined= = ="undefined" // true
typeof true= = ="boolean" // true
typeof 32= = ="number" // true
typeof "js"= = ="string" // true
typeof {
    name: 'Mike'} = = ="object" // true
// ES6 is new
typeof Symbol() = = ="symbol" // true
// ES2020 is new
typeof BigInt(123) = = ="bigint" // true
/ / note!!!!!
typeof null= = ="object" // true
// Subtypes under object
typeof(function() = = = {})"function" // true
typeof(new Date()) = = ="object" // true
typeof[] = = ="object" // true
Copy the code

null ε’Œ undefinedThe difference between

  • null: It shouldn’t be worth it
    • When we set a variable to PInull.It indicates that the variable has been used and will not be related to other variables. The memory occupied by the variable will be released.
  • undefined: Expected value but not assigned
    • When a variable has a value ofundefinedWhen,That means the variable hasn’t been assigned yet, it expects another assignment, and the variable is still taking up memory.(ps: The garbage collection may be updated later in the following, please stay tuned ~)
    • When we were doingA read operationFor example, when we get an object property, a variable, a function, we find that when we expect a value, we get no valueundefinedIt is expected to have valueThe default value.

A variable type is the type of the value of a variable

JavaScript is a weakly typed and dynamic language. Weakly typed means that there is no need to specify the type of the variable in advance. The JS engine will calculate the data type of the variable during the operation. Dynamic means that the same variable can hold different types of data.

One concept to be clear is that when you see var a = 1, it is common to say “I created a numeric variable a”.

The simple assignment statement var a = XXX is actually three steps,

  1. Creates a valuexxx
  2. A variable is createda
  3. Associate values with variables

It’s actually more accurate to say that we created a variable, a, whose value is of numerical type. JavaScript dynamic language features, in fact, there is no concept of variable type, when we describe a variable type, we do not say that the variable is type XXX, but the value of the variable is XXX when we use the current (js engine will calculate the value of the data type – weak type characteristics).

Let’s look at another example that clearly illustrates the dynamic nature of JavaScript.

var a = 1 / / value of 1
console.log(a)
a = 10 / / number 10
console.log(a)
a = 'δΈ€δΈͺθ£…η‘ηš„δΊΊ' // String 'a fool'
console.log(a)
Copy the code

Run code The following output is displayed:

[Done] exited with code=0 in 1.46 secondsCopy the code

Method to determine the type of variable (plus how to accurately determine the type of array)

Three ways to judge variables, their advantages and disadvantages

  1. typeof :

βœ… benefits: output type literals directly via typeof XXX (“undefined”, “object”, etc.)

typeof 123 // "number"
typeof true // "boolean"
typeof {} // "object"
Copy the code

❎ Disadvantages: Cannot accurately determine null type, and some subtypes derived from object (e.g., Date, Array)

/ / note!!!!!
typeof null // "object"
// Subtypes under object
typeof(function() {}) // "function"
typeof(new Date()) // "object"
typeof [] // "object"
Copy the code

πŸ“Œ For more information, see MDN typeof >

  1. instanceof :

βœ… advantage: using XXX instanceof ConstructorPrototype to determine whether there is a prototype of a constructor on XXX’s prototype chain, that is, you can detect whether an object is inherited from a type, and you can accurately determine its real type for reference types

123 instanceof Number // false
new Number(123) instanceof Number // true
Copy the code

❎ Disadvantages: the need to provide a constructor to the right of instanceof means that we must know in advance that the variable being tested is related to a data type, and ** this judgment is invalid if the instance was not created by the constructor (i.e., the value of the original type), especially if there is no constructor involved. And true for any constructor stereotype that has ever existed on the stereotype chain, which can be inaccurate for complex reference types. (While exploring here, you can relate to the original types and their wrapper objects. For more information, go to πŸ‘‡.)

πŸ“Œ For more information, see MDN Instanceof >

handwritteninstanceof

Ps: This implementation will involve some concepts of prototype chain instance objects, if you feel need to talk about the friends can give me a comment, I can add one.

Use: Number(123) instanceof Number

  • The left side receives an instance object, and the right side receives a constructor

  const myInstanceof = (inst, Constructor) = > {
      // ...
  }
Copy the code
  • Traverse the prototype chain of the instance object (up to null) to see if the constructor prototype exists

    Relationship between constructors and instance objects: constructor === constructor

    Constructor prototype: instance object: __proto__ === constructor

  const myInstanceof = (inst, Constructor) = > {
      // The prototype object for the constructor
      const prototype = Constructor && Constructor.prototype
      / / prototype chain
      const proto = inst && inst.__proto__
      // There is also a prototype chain to traverse
      while (proto) {
          if (proto === prototype) {
              / / there
              return true
          }
          // Does not exist -> another layer of prototype chain
          proto = proto.__proto__
      }
      // The prototype chain traversal has ended and does not exist
      return false
  }
Copy the code
  • Perfect the function, complete some checks, and can be preconditioned

    • Constructor must be a function

    • Inst air defense

    • The drawback of instanceof, as mentioned in the previous paragraph, is that it is not possible to determine the value type

  const myInstanceof = (inst, Constructor) = > {
      // Constructor must be a function
      if (typeofConstructor ! = = 'function'){
          // Typeof is used to determine whether a variable is a function, as mentioned in the following paragraphs
          throw new Error('Type Error')}// Instanceof is an instanceof value type
      const baseTypes = ['null'.'undefined'.'boolean'.'string'.'Symbol'.'BigInt'.'number']
      / / inst air defense
      if(! inst || baseTypes.includes(typeof inst)) {
          return false
      }
      // The prototype object for the constructor
      const prototype = Constructor && Constructor.prototype
      / / prototype chain
      const proto = inst && inst.__proto__
      // There is also a prototype chain to traverse
      while (proto) {
          if (proto === prototype) {
              / / there
              return true
          }
          // Does not exist -> another layer of prototype chain
          proto = proto.__proto__
      }
      // The prototype chain traversal has ended and does not exist
      return false
  }
Copy the code

Almost the implementation is like this, small friends can follow the steps, try it yourself, after writing more Test Case, and instanceof keyword comparison results, complete code has been uploaded to the warehouse, there are questions also welcome comments to discuss ~

What types can Typeof determine? What did Instanceof do?

Typeof can be used to determine primitive types (except null), and reference types can be used to determine function.

Instanceof takes an instance object on the left and a constructor on the right, and it checks to see if there is a prototype object for the constructor by traversing the prototype chain of the instance object.

typeof null? null instanceof Object?
typeof null // "object"
null instanceof Object // false
Copy the code
  1. Object.prototype.toString.call() :

βœ… advantage: the call Object. The prototype. ToString. Call () can accurately output variable types, including null, and undefined, and the specific reference types

❎ faults: no (note: after rewriting Object. The prototype. The toString () may be failure)

πŸ“Œ use can refer to MDN toString >, specification can refer to ECMA262 toString >

How to accurately determine the array type

  1. Object. The prototype. ToString. Call (variable) : in the case of write toString can accurate judgment

  2. Array. IsArray (variable) : It can be accurately determined in an ES5-supported environment (see ECMA262 isArray > for the πŸ“Œ specification).

Packaging object

Packing objects, packing objects, unpacking objects, unpacking objects, unpacking objects, unpacking objects, unpacking objects, unpacking objects, unpacking objects

What is the object?

In computer languages an object is a set of key-value pairs, so why do we have an object in the first place? This goes back to the feature of object-oriented languages:

Object-oriented keywords: object, encapsulation, inheritance, polymorphism

  • The abstraction of objects in the real world into objects (models) that can describe them through encapsulation.
  • Grouping objects that have the same meaning into classes
  • Generalization (code reuse, attribute sharing) and extensibility (attribute extension) of classes are realized by inheritance.
  • The same method can have different execution results to form polymorphism

Therefore, object is essentially a model that we use to describe a thing. On the basis of class, it can realize the property sharing and method reuse among multi-instance objects.

What’s the difference between new Number(1) and 1

First, new Number(1) uses the new keyword, so it must generate an object-reference type, and 1 is a primitive type.

So what is Number? Let’s introduce:

JavaScript standard built-in objects

Built-in objects are the basic functionality that every language has. Built-in objects define some properties and methods that are available to certain types of objects, as well as some basic objects (such as error objects) that can be used globally.

πŸ“Œ For all built-in objects, see MDN >

The focus here is on the built-in objects associated with primitive types

Not all primitive types have a wrapper object. Currently, all primitive types have a wrapper object except null and undefined. (Note that Symbol and BigInt have no literals created, so they can be used as wrapper objects without using the new keyword.)

The purpose of wrapper objects is to “share attributes and reuse methods before implementing similar objects”, so they must have a common underlying object model, or what we call a class. The underlying object model defines the properties and methods that each class of model should have **, so that each instance object does not need to repeatedly define properties and methods.

Primitive type and wrapper object relationship

Primitive type (string/number/ Boolean) -> Wrapper object: 123 -> new number (123)

Wrapper object -> Primitive type (string/number/ Boolean) : new number (123) -> (new number (123)).valueof ()

Instanceof: Instanceof: Instanceof: Instanceof: Instanceof: Instanceof

123 instanceof Number // false
new Number(123) instanceof Number // true
Copy the code

When we create a wrapper object, it is essentially generating an object, so both typeof and Instanceof tests will differ from the original type. When comparing objects, the heap memory addresses are compared according to the reference type comparison principle. Here are some examples to give you an intuition:

typeof 123 // "number"
typeof new Number(123) // "object"
typeof Number(123) // "number"

123 instanceof Number // false
new Number(123) instanceof Number // true
Number(123) instanceof Number // false

123= = =Number(123) // true
123= = =new Number(123) // false
Copy the code

The example above should make sense in the context of the above explanation, one of which is that when we call the wrapper object to create an object without the new keyword, we find that it is not a reference type. When we call the wrapper object without the new keyword, we will convert the value passed in to the value of the wrapper object’s original type, so we can think of it as a type conversion function.

I’ll leave you with a special example πŸ₯Š :

if (new Boolean(false)) {
    alert('true!! ');
}
Copy the code

Packing/unpacking

The JavaScript built-in object allows us to use properties and methods that are public to objects of that type. After we use literals to create values of a primitive type, we find that we can also use methods and properties on built-in objects of that type. How can we do this? Let’s revert to the process:

When we calculate using an operator:

const a = new Number(1) // object
const b = a * 5
const c = a + 3
typeof b // "number"
typeof c // "number"
Copy the code

In the example above, we can see that the type of the value of the variable after the operation is always the original type. This phenomenon is called implicit conversion, JS will check whether the value of the variable on both sides of the operator is the original type, if not, then do a reference type (object) automatically convert to the original type operation, based on the operator and the value of the variable on both sides, There may be different conversion scenarios (for example, + operation some scenarios to number and some scenarios to string).

βœ… : This implicit process of automatically converting an object to a value of its original type is called unboxing.

Two methods are used in the unpacking process:

ToString () : Returns the string representation of an object, used preferentially in scenarios where a string transfer is required.

ValueOf () : Returns the original type (Boolean /number/string) representation of the object, preferred in numeric operation scenarios.

Null and undefined are neither objects nor corresponding wrapper objects, so neither method is used.

A famous problem
[] + [] {} + {}
0 * []
    [1.2.3] + [1.2.3]
Copy the code

When accessing properties or methods using variables as objects:

const a = 123.342
a.toFixed(2) / / "123.34"
const b = 'test'
b.chat(1) // "e"
Copy the code

Through the analysis of the similar to the last paragraph, we can guess, when we will visit the object as a primitive type value methods, or properties, there is an automatic implicit action, and the process and not directly will split open a case is not the same type conversion, but in the original type value corresponding to the built-in objects to create a corresponding temporary built-in object instance variables, Allows temporary variables to act as surrogates for values of primitive types, inheriting all properties and methods from built-in objects.

// In the case of a, the actual JS internal operation looks like this
// Create an instance of the built-in object Number
const a = new Number(123.342)
// The prototype objects for num and Number are associated
a.__proto__ === Number.prototype
// See what properties and methods are on the Number prototype object
Number.prototype
// You can see that there is a toFixed method, i.e. you can access num. ToFixed
num.toFixed(2)
Copy the code

βœ… : This implicit process of automatically associating a primitive type value with a prototype object of a built-in object is called boxing.

How to avoid scenarios where implicit type conversions occur?

The last paragraph covered scenarios where implicit type conversions occur when operations and variable attributes or methods are accessed. This paragraph will add the == scenario.

The == operator compares two variables for equality, returns a Boolean value, and casts if the operands are not of the same type. Here are some rules for comparing equals:

  • If two operands are of the same basic type

    • Boolean: Equal when both are true or both are false.
    • String: The strings are equal when they are arranged in the same order.
    • Number: The value is the same when the value is the same.
  • If the two operands are of different types

    • If both sides of the operator are objects, they are equal only if they refer to the same object.
    • If one of the operands is zeronull, only when the other isundefinedIs equal to.
    • The following cases of type conversions occur
      • number ε’Œ stringWill:stringCast to the correspondingnumberType value, and then numerical comparison.
      • number ε’Œ booleanWill:booleanConvert the variable tonumberType value (true = 1.false = 0), and then make numerical comparison.
      • number/string ε’Œ object: The object type will passvalueOf ε’Œ toStringConvert to the corresponding base type and then compare (subsequent comparisons will follow the same comparison rules as above).

βœ… : Avoid the above implicit conversions. We generally use the strict congruence notation for comparison === or! ==, congruent comparisons require that the type and value of the operand be equal. There is no automatic type conversion. For other scenarios, use the same type of value (number/string/ Boolean) that is suitable for the operation.

How are variables stored in memory

The JavaScript memory space is divided into two pieces — stack memory and heap memory.

The values for the primitive data types are the variable values stored in the stack memory directly (local, take up the space data, facilitate fast reading and writing and memory release), for the value of the reference data type of the variable is the value itself in heap memory, corresponding to produce heap memory address of a pointer to the value (hashCode), The value of this variable in stack memory stores the address. When we access a variable, we will first find the memory address of the variable stored in the stack memory, and then find the corresponding data value in the heap memory according to this address.

πŸ“Œ Reference article MDN >

How do variables behave differently when they are primitive and reference types?

The last step is to make the value associated with the variable. There are two ways to do this: to store the value and to store the reference. After the code is run, each variable will have a memory space in the stack memory for it to store the variable and its value. A variable is a name used for identification, and the value of a variable is stored differently depending on the data type of the value. The different manifestations are analyzed in terms of usage and storage,

Different storage performance

The value of the primitive type is itself stored in the stack memory to support fast reads and reclaims to clear memory free. The value of a reference type is stored in the heap memory, which has a larger space and a longer shelf life, and is slower to read and write than the stack memory, making it suitable for complex types.

When we encounter a value of a reference type, the value itself is stored in the heap memory, and the heap memory provides a memory address (hashCode) for external access to the value. In stack memory, the value of the variable is stored in the reference memory address given by the heap memory.

Different usage performance

Because of the different storage, in the use of nature is also different.

Raw data types are accessed by value, because the value of a variable is the value of the data stored directly in stack memory, and when we manipulate a variable, we are directly manipulating the value of the variable.

In contrast, the value of a variable of reference type is not stored in the stack memory at all. We can directly access only the reference address stored in the stack memory, while the real value of the data can only be indirectly queried in the heap memory through the memory address. So when we manipulate a variable with a reference value, we’re essentially just manipulating a memory address.

Understand value types and reference types

  • Value types and reference types are stored in different places and in different ways
  • Value types and reference types use different memory sizes and periods
  • Value types and reference types behave differently in read-write and assignment operations

To this end, I think my opening description of the text is not too rigorous, ✍🏻 slightly modified

When was memory allocated

Regardless of the programming language, the life cycle of memory is basically the same:

  1. Allocate Memory as needed
  2. Read/write operations using allocated Memory (Use Memory)
  3. Release Memory when it is not needed

In general, low-level languages (e.g. C) memory allocation needs to be handled manually by the developer. In a high level language like JavaScript, the action of memory allocation is done by the JS layer. With variable declarations, the underlying layer senses that memory needs to be allocated and allocates different sizes of memory depending on the type of variable. For raw data types, memory is static (once created, cannot be modified twice) and is generally fixed in size and relatively small, while for reference types, memory is dynamic, allocating memory in real time as variables change.

πŸ“Œ More details can be found: MDN Memory Management >

Example: Variable assignment behaves differently in stack/heap memory

First, assignment comes in two forms: copy and reference

For values of primitive type, assignment is making a copy of the value, that is, making a copy of the value.

For values of reference type, the assignment operation is a reference to the heap memory address of the value, i.e. a heap memory address to be passed.

Stack memory

var a = 10
var b = a // b is a copy of a
b = 100 // Modify the value of b

// A,b belong to the original data type, stored in stack memory
console.log({
    a
}, {
    b
})
Copy the code

The running results are as follows:

You can see that after we copy A to B, when we change the value of B, it doesn’t affect A. Why is that?

As we said above, the raw data type is stored in stack memory, and the property value is the raw value. ** What does that mean? ** We analyze by row.

  • var a = 10

    JS allocates memory (stack memory) to a, and stores the original value of a (10) in the allocated memory.

    The Variables (stack) Values
    a 10
  • var b = a

    Declare a variable b, b = a is a reference assignment operation, because a is the original data type (number), for the original data type variable, the reference assignment operation is to directly find the value of the variable in memory and assign it to the newly declared variable. Var b = 10; var b = 10; var b = 10; var b = 10;

    The Variables (stack) Values
    a 10
    b 10
  • b = 100

    This step is the second step in the memory life cycle described above — reading and writing to the allocated memory. JS finds the memory allocated to B and writes the value 100 to it.

    The Variables (stack) Values
    a 10
    b 10 100

βœ… : although it is a copy operation, it is equivalent to writing a value to the original data type. The two variables are independent of each other and do not affect each other.

Heap memory

var personA = {
    name: 'Peter'
}
var personB = personA PersonB is a reference to {name:'Peter'}
personB.name = 'Alice' // Change the value of personB

PersonA, personB is a reference data type and is stored in heap memory
console.log({
    personA
}, {
    personB
})
Copy the code

The running results are as follows:

You can see that after we copy A to B, when we modify B, it affects A as well. Why is that?

As we said above, the value of a reference type is stored in heap memory, and the value of a variable on the stack is stored in the heap memory address (hashCode) corresponding to that value. What does that mean? Let’s do the row analysis again.

  • var personA = { name: 'Peter' }

    If you declare a variable personA with a value of type object, JS will allocate memory (heap memory) for the object {name: ‘Peter’} and also memory (stack memory) for the personA. The heap memory address allocated by ‘Peter’} is assigned to the personA.

    This is just to show the stack memory roughly corresponding to the heap memory, the actual memory storage is more complicated (read some articles about the lower level, I have not been able to draw πŸ€¦πŸ»β™€οΈ, want to know the details of the students can search)

    The Variables (stack) Values
    personA 0x167
    The Variables (heap) Value
    0x167 { name: ‘Peter’ }
  • var personB = personA

    Declare a variable personB, personB = personA because personA is a variable with a value of reference type, and assign a value of reference type by copying the heap memory address of the referenced variable directly to the newly declared variable. (@0716 changed “copy” to “pass” to avoid confusion with the concept of “copy of primitive type values”)

    That is, the heap memory address that was just stored in personA (say 0x167) is written to the stack memory allocated for the personB variable, var personB = 0x167.

    The Variables (stack) Values
    personA 0x167
    personB 0x167
    The Variables (heap) Values
    0x167 { name: ‘Peter’ }
  • personB.name = 'Alice'

    To find the value of the variable stored by personB, first find the value of personB in stack memory as heap memory address 0x167. Then find the data value {name: ‘Peter’} from the heap memory corresponding to 0x167 and change its name attribute to Alice.

    The Variables (stack) Values
    personA 0x167
    personB 0x167
    The Variables (heap) Values
    0x167 { name: ‘Peter’ ‘Alice’ }

    Since an assignment to a variable that references a data type is equivalent to passing a heap address to the same memory address, if either of the parties reads or writes the referenced value from that address, the other parties will find the modified value at the stored memory address.

βœ… : The replication operation between variables of reference data type is to pass the memory address to the newly declared variables. The variables share the same reference to the heap memory variable. Any changes to the variable pointed to by either side will affect each other.

To further elaborate, a variable of reference type holds the heap memory address of the referenced variable, and a reference assignment only modifies the referenced object to which the variable points, not the value of the referenced object.

Follow the example above:

var personA = {
    name: 'Peter'
}
var personB = personA PersonB is a reference to {name:'Peter'}
personB.name = 'Alice' // Change the value of personB

PersonA,personB is a reference data type and is stored in heap memory
console.log({
    personA
}, {
    personB
})

// Assign the reference again
personB = [1.2.3] PersonB is a reference to [1,2,3]

// Print to see if it affects the personA values
console.log({
    personA
}, {
    personB
})
Copy the code

As you can see, as we said above, we changed the orientation of personB, but it has no effect on the personA.

βœ… : When we copy, there are different situations depending on the type of the value. Basic types use value replication, and reference types use reference replication.

How to define a constant (variable ES5/ES6 that cannot be modified)

// todo

ES6: Const does not allow repeated declarations

const temp;
temp = 1;
// The above code will report an error

// Const can be declared and assigned only once
const temp = 1;
Copy the code

When you see a question about variable storage, hang πŸ€™πŸ»

var a = {
    n: 1
}
a.x = a = {
    n: 2
}
console.log(a.x)
console.log(a)
Copy the code

// todo

Why design reference types to be stored in heap memory?

I don’t know if you are curious about this, I was learning when I was pretty curious so I specifically went to check (bar fine namely visual sense 🀳🏻)

Why design two memory Spaces, heap memory and stack memory?

You can imagine that if there is only one stack memory, and all the data is stored in one space, as the volume gets bigger and bigger, there will be some impact on the efficiency of the program.

Why store reference types in heap memory?

Reference type relative to original type is a complex type, it can be referenced be modified at any time and place, for memory because don’t know when it will be used, will be big change is small, so they will take up more than the original type for a longer time of memory, the footprint for the need for the stack memory of efficiency, it is not cost-effective, Therefore, reference types are designed to be in larger and farther heap memory.

Conclusion βœ… :

In order not to affect the running efficiency, the use of the advantage of stack operation fast, will be more simple and less memory footprint of primitive data types in the stack memory, but more complex and larger memory footprint of a reference type (may be modified at any time or be used, and can be infinite expansion) stored in the heap memory, if the complex type is stored in the stack memory, The frequent operation of the object will affect the efficiency of the stack operation, the gain is not worth the loss. πŸ“Œ Stack related content can refer to this article >

How exactly are reference types stored in heap memory?

// todo

I’m going to write a map loop and I’m going to change it to a reference.

// todo

What exactly is the underlying data structure of JavaScript objects

// todo

Is there a scenario for ES6 Symbol?

// todo

What problem does BigInt solve?

// todo

Accuracy loss in Numeric Type Data

Where is the accuracy missing (integer/decimal)

// todo

Why does the browser erase automatically

// todo

Why is JavaScript designed this way (even if it causes accuracy loss)

// todo

A way to avoid loss of accuracy

// todo

THE END

Thank you for reading πŸ‘πŸ» / Thank you for reading πŸ€™πŸΌ / thank you for reading πŸ‘‹ πŸ€™