Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the basic knowledge points of JavaScript?

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

This article will explain in detail what are the underlying knowledge points about JavaScript. The editor thinks it is very practical, so I share it for you as a reference. I hope you can get something after reading this article.

JavaScript is a literal interpretive scripting language, which is dynamic, weakly typed, prototype-based and so on. JavaScript is rooted in the Web browser we use, and its interpreter is the JavaScript engine in the browser. This scripting language, which is widely used in the client, was first used to deal with some input verification operations previously undertaken by the server-side language. with the development of the Web era, JavaScript continues to grow and become a fully functional programming language. Its use is no longer limited to simple data validation, but has the ability to interact with almost all aspects such as browser windows and their contents. It is not only a very simple language, but also a very complex language. If we want to really master JavaScript, we must deeply understand some of its underlying design principles.

Data type

According to the mode of storage, the data types of JavaScript can be divided into two types, the original data type (original value) and the reference data type (reference value).

There are currently six primitive data types, including Number, String, Boolean, Null, Undefined, and Symbol (ES6), which are actual values that can be directly manipulated and stored in variables. The original data types are stored in the stack, the data size is determined, and they are stored directly by value, so they can be accessed directly by value.

The reference data type is Object. In JavaScript, except for the original data type, all are Object types, including arrays, functions, regular expressions, and so on. A reference type is an object stored in heap memory, and a variable is a reference address that points to an object in heap memory. When a variable is defined and initialized to a reference value, if it is assigned to another variable, the two variables hold the same address and point to the same memory space in the heap memory. If you use one of the variables to modify the value of the reference data type, the other variable will also change.

For the original data type, except that null is special (null will be considered as an empty object reference), we can use typeof to accurately judge other data types:

Expression.

Return value

Typeof 123

'number'

Typeof "abc"

'string'

Typeof true

'boolean'

Typeof null

'object'

Typeof undefined

'undefined'

Typeof unknownVariable (undefined variable)

'undefined'

Typeof Symbol ()

'symbol'

Typeof function () {}

'function'

Typeof {}

'object'

Typeof []

'object'

Typeof (/ [0-9 Ma'amurz] /)

'object'

For null types, you can use the congruence operator to determine. A variable value that has been declared but not initialized is assigned to undefined by default (or manually to undefined). In JavaScript, using the equality operator = = cannot distinguish between null and undefined,ECMA-262 requires that their equality tests return true. To accurately distinguish between the two values, you need to use the congruence operator =.

For reference data types, except for function, which is special in method design and can be accurately judged by typeof, all of them return the object type. We can use instanceof to determine the reference type value. Instanceof detects whether one object An is an instance of another object B, and at the bottom it checks to see if object B exists on the prototype chain of object A (discussed later in the instance and prototype chain article). Returns true if it exists and false if it is not present.

Expression.

Return value

[1,2,3] instanceof Array

'true'

Function foo () {} instanceof Function

'true'

/ [0-9 Magi amerz] / instanceof RegExp

'true'

New Date () instanceof Date

'true'

{name: "Alan", age: "22"} instanceof Object

'true'

Since all reference type values are instances of Object, they are judged by Object with the instance operator, and the result also returns true.

Expression.

Return value

[1,2,3] instanceof Object

'true'

Function foo () {} instanceof Object

'true'

/ [0-9 Magi amerz] / instanceof Object

'true'

New Date () instanceof Object

'true'

Of course, there is a more powerful way to accurately determine any data type in any JavaScript, and that is the Object.prototype.toString.call () method. In ES5, all objects (native and host) have an internal property [[Class]] whose value is a string that records the type of the object. Currently, it includes "Array", "Boolean", "Date", "Error", "Function", "Math", "Number", "Object", "RegExp", "String", "Arguments", "JSON" and "Symbol". The Object.prototype.toString () method can be used to view this internal property, but there is no other way.

When the Object.prototype.toString () method is called, the following steps are performed: 1. Get the [[Class]] property value of the this object (which will be discussed later in the article on this objects). two。 Place the value between the two strings "[object" and "]" and concatenate it. 3. Returns the spliced string.

When the value of this is null, the Object.prototype.toString () method directly returns "[object Null]". When the value of this is undefined, return "[object Undefined]" directly.

Expression.

Return value

Object.prototype.toString.call (123)

[object Number]

Object.prototype.toString.call ("abc")

[object String]

Object.prototype.toString.call (true)

[object Boolean]

Object.prototype.toString.call (null)

[object Null]

Object.prototype.toString.call (undefined)

[object Undefined]

Object.prototype.toString.call (Symbol ())

[object Symbol]

Object.prototype.toString.call (function foo () {})

[object Function]

Object.prototype.toString.call ([1pm 2pm 3])

[object Array]

Object.prototype.toString.call ({name: "Alan"})

[object Object]

Object.prototype.toString.call (new Date ())

[object Date]

Object.prototype.toString.call (RegExp ())

[object RegExp]

Object.prototype.toString.call (window.JSON)

[object JSON]

Object.prototype.toString.call (Math)

[object Math]

The call () method can change the direction of the this when the Object.prototype.toString () method is called so that it points to the object we pass in, so we can get the [[Class]] property of the object we pass in (the same effect can be achieved with Object.prototype.toString.apply ()).

The data type of JavaScript can also be converted, and there are two ways of data type conversion: display type conversion and implicit type conversion.

The methods that display type conversions can call are Boolean (), String (), Number (), parseInt (), parseFloat (), and toString () (null and undefined values do not have this method), and their respective uses are clear at a glance, so I won't cover them all here.

Because JavaScript is a weakly typed language, the data types on both sides of the operator can be arbitrary when using arithmetic operators. Instead of specifying the same type as the Java or C language, the engine automatically converts them. Implicit type conversion is not as intuitive as display type conversion, there are three main ways of conversion:

1. Convert the value to the original value: toPrimitive ()

two。 Convert a value to a number: toNumber ()

3. Convert the value to a string: toString ()

Generally speaking, when adding a number and a string, the number will be converted to a string; when the truth is judged (such as if, |, & &), the parameter will be converted to a Boolean value; when the comparison operation, arithmetic operation, or self-subtraction operation, the parameter will be converted to the number value; when the object needs to undergo implicit type conversion, it will get the return value of the object's toString () method or valueOf () method.

About NaN:

NaN is a special numerical value that represents a non-numeric value. First, any operation involving NaN returns NaN. Second, NaN is not equal to any value, including NaN itself. ECMAScript defines an isNaN () function that can be used to test whether a parameter is "non-numeric". It first attempts to implicitly convert the parameter to a numeric value, and returns true if it cannot be converted to a numeric value.

We can first determine whether it is Number by typeof, and then determine whether the current data is NaN by isNaN.

About strings:

Strings in JavaScript are immutable, and once strings are created, their values cannot be changed. To change the string saved by a variable, first destroy the original string, and then populate the variable with another string containing the new value. This process takes place in the background, which is why some older browsers are slow to concatenate strings.

In fact, to facilitate the manipulation of basic type values, ECMAScript also provides three special reference types: Boolean, Number, and String. The primitive data type has no properties and methods, and when we call the method on the primitive type value to read them, the access process is in a read mode, and the backstage creates an object of the corresponding original wrapper type. This allows us to call some methods to manipulate the data. This process is divided into three steps: 1. Create an instance of the original packaging type 2. Call the specified method 3. Destroy this instance.

The main difference between the reference type and the original wrapper type is the life cycle of the object, and the automatically created original wrapper type object exists only at the moment of the execution of a line of code and then is destroyed immediately. so we can't add properties and methods to the original type value at run time.

Pre-compilation

In the book JavaScript you don't know, the author says that although JavaScript is classified as a "dynamic language" or an "interpreted execution language", it is actually a compiled language. The operation of JavaScript is divided into three steps: 1. Grammatical analysis II. Pre-compiled 3. Explain and execute. Parsing and interpretation execution are not difficult to understand, one is to check the code for syntax errors, and the other is responsible for executing the program line by line, but the precompilation phase in JavaScript is slightly more complex.

Any JavaScript code is compiled before it is executed, and the compilation process mostly occurs within a few microseconds before the code is executed. The compile-phase JavaScript engine starts with the current code execution scope and performs an RHS query on the code to get the value of the variable. The engine then executes a LHS query at run time to assign values to the variables.

During the compile phase, part of the JavaScript engine's job is to find all the declarations and associate them with the appropriate scope. During the precompilation process, if it is in the global scope, the JavaScript engine first creates a global object (GO object, Global Object) on the global scope, and promotes the variable declaration and function declaration. The promoted variables are initialized to undefined by default, while the function promotes the entire function body (if the function is defined in the form of a function expression, the rules of variable promotion are applied), and then store them in the global variable. The promotion of the function declaration will take precedence over the promotion of the variable declaration. For the variable declaration, the repeated var declaration will be ignored by the engine, and the later function declaration can override the previous function declaration (ES6's new variable declaration syntax let is slightly different, so let's not discuss it here for the time being).

There is an independent scope inside the function body, and the pre-compilation stage will also be carried out within the function body. Inside the function body, an active object (AO object, Active Object) is first created, and the formal parameters and variable declarations as well as the function declarations inside the function body are promoted. The parameters and variables are initialized to undefined, and the internal functions are still the internal function body itself, and then they are stored in the active object.

When the compilation phase is over, the JavaScript code is executed. The execution process assigns values to variables or parameters according to the order. The engine looks for corresponding variable or parameter declarations on the scope and assigns them if they are found. For non-strict mode, if a variable is assigned undeclared, the engine automatically implicitly creates a declaration for the variable in the global environment, but for strict mode, assigning undeclared variables will report an error. Because JavaScript execution is single-threaded, if you get the variable (RHS query) and output it before the assignment operation (LHS query) is executed, you will get the result of undefined, because the variable has not been assigned yet.

Execution environment and scope

Each function is an instance of a Function object, and in JavaScript, each object has an internal property that is accessible only to the JavaScript engine-- [[Scope]]. For a function, the [[Scope]] attribute contains a collection of objects in the scope in which the function is created-the scope chain. When you create a function in the global environment, the function's scope chain inserts a global object containing all variables defined in the global scope.

The internal scope can access the external scope, but the external scope cannot access the internal scope. Because JavaScript has no block-level scope, variables defined in if statements or for loop statements are accessible outside the statement. Before ES6, javascript only had global scope and function scope, and ES6 added a block-level scope mechanism.

When the function is executed, an internal object called execution environment (execution context, also known as execution context) is created for the execution function. Each execution environment has its own scope chain, and when the execution environment is created, the top of its scope chain is first initialized to an object in the [[Scope]] attribute of the current runtime function. Immediately after that, the active object of the function runtime (including all local variables, named parameters, arguments parameter sets, and this) is also created and pushed to the top of the scope chain.

Each time a function is executed, the corresponding execution environment is unique, and multiple calls to the same function will result in the creation of multiple execution environments. When the function is finished, the execution environment is destroyed. When the execution environment is destroyed, the active object is also destroyed (the global execution environment is not destroyed until the application exits, such as closing a web page or browser).

During the execution of a function, each variable encountered will go through an identifier parsing process to determine where to get or store the data. Identifier parsing is a process of searching identifiers along the scope chain, and the global variable is always the last object of the scope chain (that is, window object).

In JavaScript, there are two statements that can temporarily change the scope chain at execution time. The first is the with statement. The with statement creates a mutable object that contains all the properties of the object specified by the parameter and pushes the object to the first place in the scope chain, which means that the active object of the function is squeezed into the second place in the scope chain. This makes it very fast to access the properties of a mutable object, but slower to access local variables, and so on. The second statement that can change the execution environment scope chain is the catch clause in the try-catch statement. When an error occurs in the try code block, the execution automatically jumps to the catch clause and then pushes the exception object into a variable object and puts it in the first place of the scope. Within the catch code block, all local variables of the function will be placed in the second scope chain object. Once the catch clause is executed, the scope chain returns to its previous state.

Constructor function

Constructors in JavaScript can be used to create specific types of objects. To distinguish them from other functions, constructors generally start with uppercase letters. However, this is not necessary in JavaScript because there is no special syntax for defining constructors in JavaScript. In JavaScript, the only difference between constructors and other functions is the way they are called. Any function can be used as a constructor as long as it is called through the new operator.

There are four call modes for JavaScript functions: 1. Independent function call mode, such as foo (arg). two。 Method invocation mode, such as obj.foo (arg). 3. The constructor calls the pattern, such as new foo (arg). 4.call/apply invocation patterns, such as foo.call (this,arg1,arg2) or foo.apply (this,args) (where args is an array).

To create an instance of the constructor and play the role of the constructor, you must use the new operator. When we instantiate the constructor using the new operator, the following steps are performed inside the constructor:

1. Implicitly create an empty this object

two。 Execute the code in the constructor (add properties to the current this object)

3. Implicitly returns the current this object

If the constructor returns an object, then the instance is the returned object, otherwise it is the implicitly returned this object.

When we call the constructor to create the instance, the instance has all the instance properties and methods of the constructor. For different instances created through the constructor, the instance properties and methods between them are independent. Even if it is a reference type value of the same name, different instances will not affect each other.

Prototype and prototype chain

Prototype and prototype chain is not only one of the quintessence of JavaScript, but also one of the difficulties of the language. The prototype prototype (explicit prototype) is a function-specific property, and whenever a function is created, the function automatically creates a prototype property and points to the function's prototype object. All prototype objects automatically get a constructor (constructor, which can also be translated as a constructor) property, which contains a pointer to the function where the prototype property resides (that is, the constructor itself). When we create an instance through the constructor, the instance will contain an internal property (implicit prototype) of [[Prototype]], which also points to the prototype object of the constructor. In Firefox, Safari, and Chrome browsers, each object can access its [[Prototype]] property through the _ _ proto__ property. For other browsers, this property is completely invisible to the script.

The prototype property of the constructor and the [[Prototype]] of the instance are prototype objects that point to the constructor, and there is no direct relationship between the [[Prototype]] property of the instance and the constructor. To know whether the instance's [[Prototype]] property points to the prototype object of a constructor, we can use the isPrototypeOf () or Object.getPrototypeOf () method.

Whenever a property of an object instance is read, a search is performed, targeting a property with a given name. The search starts with the object instance itself, and if a property with a given name is found in the instance, the value of that property is returned; if not, continue to search for the prototype object pointed to by the object's [[Prototype]] property, look for the property with the given name in the prototype object, and return the value of that property if found.

To determine which constructor the object is, you can access the constructor property directly on the instance, and the instance returns the constructor itself by reading the constructor property on the prototype object through [[Prototype]].

The values in the prototype object can be accessed through the object instance, but cannot be modified by the object instance. If we add a property with the same name as the instance prototype object to the instance, we create the property in the instance, which prevents us from accessing that property in the prototype object, but does not modify that property. Simply setting the instance property to null does not restore the connection that accesses the property in the prototype object. To restore access to the property in the prototype object, you can use the delete operator to completely delete the property of the object instance.

You can use the hasOwnProperty () method to detect whether a property exists in the instance or in the prototype. This method returns true only if the given property exists in the object instance. To get all the enumerable instance properties of the object itself, you can use the Object.keys () method of ES5. To get all instance properties, whether enumerable or not, you can use the Object.getOwnPropertyNames () method.

The prototype is dynamic, and any changes made to the prototype object can be immediately reflected in the instance, but if the entire prototype object is rewritten, the situation is different. Calling the constructor adds a [[Prototype]] pointer to the original prototype object to the object instance, and after rewriting the entire prototype object, the constructor points to the new prototype object, and all prototype object properties and methods exist on the new prototype object. The object instance also points to the original prototype object, so that the connection between the constructor and the original prototype object pointing to the same prototype object is cut off, because they point to different prototype objects.

To restore this connection, you can instantiate the object instance after the constructor prototype is rewritten, or modify the _ _ proto__ property of the object instance to repoint to the constructor's new prototype object.

JavaScript uses prototype chains as the main way to implement inheritance, using prototypes to make one reference type inherit the properties and methods of another reference type. The instance of the constructor has a [[Prototype]] property that points to the prototype object. When we make the prototype object of the constructor equal to an instance of another type, the prototype object will also contain a [[Prototype]] pointer to another prototype, if the other prototype is an instance of another type. In this way, the chain of examples and prototypes is formed. This is the basic concept of so-called prototype chain.

The prototype chain extends the prototype search mechanism so that when an instance property is read, it is first searched in the instance. If the property is not found, the search for the prototype object pointed to by the instance [[Prototype]] continues, and the prototype object becomes an instance of another constructor. If it is not found on the prototype object, it continues to search for another prototype object pointed to by the prototype object [[Prototype]]. The search process continues to search up the prototype chain, and if the specified property or method is not found, the search process will not stop until the end of the prototype chain.

If you do not modify the prototype object of the function, all reference types have a [[Prototype]] property that points to the prototype object of Object by default. Therefore, the default prototype for all functions is an instance of Object, which is the root reason why all custom types inherit default methods such as toString (), valueOf (), and so on. You can use the instanceof operator or the isPrototypeOf () method to determine whether there is a prototype of a constructor in the prototype chain of the instance.

Although the prototype chain is very powerful, it also has some problems. The first problem is that the reference type value on the prototype object is shared by all instances, which means that the reference type properties or methods of different instances point to the same heap memory. Modifying the reference value of one instance on the prototype affects the reference value of all other instances on the prototype object at the same time, which is why private properties or methods are defined in the constructor rather than on the prototype. The second problem with the prototype chain is that when we equate the prototype prototype of one constructor to the instance of another constructor, if we pass a parameter to another constructor at this time, then all instances of the property based on the original constructor will be given the same value because of the prototype chain, which is sometimes not the result we want.

Closure

Closure is one of the most powerful features of JavaScript. In JavaScript, closure refers to a function that has access to variables in the scope of another function, which means that a function can access data outside the local scope. A common way to create closures is to create another function inside one function and return that function.

Generally speaking, when the function is executed, the locally active object will be destroyed and only the global scope will be saved in memory. However, the situation of closures is different.

The [[Scope]] attribute of the closure function is initialized to the scope chain of the function that wraps it, so the closure contains references to the same objects as the execution environment scope chain. Generally speaking, the active object of a function is destroyed along with the execution environment. However, when a closure is introduced, the active object of the original function cannot be destroyed because the reference still exists in the [[Scope]] property of the closure. This means that closure functions require more memory overhead than non-closure functions, resulting in more memory leaks. In addition, when the closure accesses the active object of the original wrapper function, it needs to step over the identifier parsing of its own active object on the scope chain to find a higher layer, so the variables in which the closure uses the original wrapper function also have a great impact on performance.

In timers, event listeners, Ajax requests, cross-window communication, Web Workers, or any other asynchronous or synchronous task, whenever a callback function is used, you are actually using closures.

A typical closure problem is to use a timer to output loop variables in a for loop:

This code, for those who are not familiar with the JavaScript closure, may take it for granted that the result will output 0, 1, 2, 3 in turn, however, the actual situation is that the four digits output by this code are all 4.

This is because, because the timer is an asynchronous loading mechanism, it will not be executed until the for loop is traversed. Each time a timer is executed, the timer looks for the I variable in its external scope. Because the loop has ended, the I variable of the external scope has already been updated to 4, so the I variable obtained by the four timers is 4, instead of the ideal output of 0mem1mem2pyr3.

To solve this problem, we can create a new scope that wraps the immediate execution function, save the I variable of the external scope in each loop to the newly created scope, and let the timer take the value from the new scope first each time. We can create this new scope with the immediate execution function:

In this way, the result of the loop execution will output 0Power1 and 2J3 in turn. We can also simplify the immediate execution function and pass the I action argument directly to the immediate execution function, so that we don't have to assign a value to j:

Of course, it is not necessary to execute a function immediately, but you can also create a non-anonymous function and execute it each time you loop, but this will take up more memory to save the function declaration.

Since there was no block-level scope setting before ES6, we had to manually create a new scope to solve this problem. ES6 begins to set block-level scopes, and we can use let to define block-level scopes:

The let operator creates a block-level scope, and variables declared through let are saved in the current block-level scope, so each immediately executed function looks for variables from its current block-level scope each time.

Let also has a special definition that allows variables to be declared more than once during the loop, each time the loop is redeclared, and initializes the newly declared variable with the value at the end of the last loop, so we can also use let directly in the for loop header:

This points to

The this keyword is one of the most complex mechanisms in JavaScript and is automatically defined in the scope of all functions. It is easy to understand this as pointing to the function itself. However, in ES5, this is not bound when the function is declared, it is bound when the function is running, and its direction only depends on how the function is called and has nothing to do with the location of the function declaration. (the this in the new arrow function of ES6 is different, and its direction depends on the location of the function declaration. )

Remember the four function call modes I mentioned earlier: 1. Independent function call mode, such as foo (arg). two。 Object method invocation mode, such as obj.foo (arg). 3. The constructor calls the pattern, such as new foo (arg). 4.call/apply invocation mode, such as foo.call (this) or foo.apply (this).

For stand-alone function call mode, in non-strict mode, the this in it points to the global object by default. In strict mode, this does not allow default binding to global objects, so it is bound to undefined.

For the object method invocation pattern, the this in the function points to the object itself that calls it:

For the constructor invocation pattern, the execution steps inside the constructor have been described earlier:

1. Implicitly create an empty this object

two。 Execute the code in the constructor (add properties to the current this object)

3. Implicitly returns the current this object

Therefore, when a function is called with new, its this points to an implicitly independently created this object inside the constructor, and all properties or methods added through this will eventually be added to the empty object and returned to the instance of the constructor.

For the call/apply call mode, the this in the function is bound to the first parameter you pass in, as shown in the figure:

Foo.apply () and foo.call () have the same function in changing the direction of the this, except that the second parameter begins to pass parameters in array format or scattered parameter format.

This is the end of this article on "what are the underlying knowledge points of JavaScript". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, please share it for more people to see.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report