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 totrue orfalse |
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
ε undefined
The difference between
null
: It shouldn’t be worth it- When we set a variable to PI
null
.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.
- When we set a variable to PI
undefined
: Expected value but not assigned- When a variable has a value of
undefined
When,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 value
undefined
It is expected to have valueThe default value.
- When a variable has a value of
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,
- Creates a value
xxx
- A variable is created
a
- 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
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 >
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
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
-
Object. The prototype. ToString. Call (variable) : in the case of write toString can accurate judgment
-
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 zero
null
, only when the other isundefined
Is equal to. - The following cases of type conversions occur
number
εstring
Will:string
Cast to the correspondingnumber
Type value, and then numerical comparison.number
εboolean
Will:boolean
Convert the variable tonumber
Type value (true = 1
.false = 0
), and then make numerical comparison.number/string
εobject
: The object type will passvalueOf
εtoString
Convert 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:
- Allocate Memory as needed
- Read/write operations using allocated Memory (Use Memory)
- 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 10100
β : 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 π π€