Document directory
- Introduce your own package
- Special script object
- How does the module mechanism work?
Package Loading
First of all, it should be declared that JS does not have a module mechanism, which will inevitably cause a lot of trouble to create a substantive project. After all, our goal is, we still need to create a systematic, scientific, modern, and standardized logic code boundary. Otherwise, if it is a backward management mechanism, it is an obvious short board. Nodejs itself does not create a "new" module management method, but directly inherits the commonjs specification from volunteer organizations as the module Management Specification. Therefore, developers who develop nodejs should abide by this specification. Nodejs itself has several modules compiled into the process.
Usage
To use these defined modules, the common practice is to call require (the package name string) and assign it to a local variable. The sys. Puts ("foo bar") method we often use is defined in the "sys namespace". sys is a built-in system package of nodejs. Even so, if we want to use the SYS. Puts method, we must also declare reference to the SYS module at the top of the JS source code:
VaR sys = require ('sys '); <br/> SYS. Puts ("Hello World !"); // You can also use require ('sys '). Print ("Hello World !"); <Br/> var MD = require (_ dirname + '/EXT/markdown'); // _ dirname is a global variable, indicating the Directory
After referencing, you can access any public member (object, method, or attribute) within sys in the current context statement ). Of course, sys provides sys. puts ()/* conslose output with line breaks */, sys. log ();/* conslose output without line breaks */, sys. debug ();/* similar to logging a debugger */, sys. inspect ();/* list object information * // These methods (also mentioned in the previous study and debugging ). The console output object conslose is inseparable from the Global Object process. stdout. Process. stdout is nodejs
Built-in object, which does not need to be obtained by loading packages. It is an underlying object exposed by V8. Go to the core source code node. js and find that calling the code statement process. Binding ('stdio ') cannot be used directly. nodejs is encapsulated in multiple layers of Js. The following two internal usage methods allow you to more realistically access the console output:
Process. stdout. write (sys. inspect (object) + '/N'); <br/> process. binding ('stdio '). writeerror (x + "/N ");
What corresponds to process. stdout (starting from 611st) is process. stdoin, which defines the I/O Stream as soon as it is output, and In line 634 of the Code.
Introduce your own package
The require method standardized by commonjs is quite simple to use (in the end, it makes people feel that there are many shadows of Java import statements/C # using statements). Which package should be introduced whenever needed, you can also reference other packages in a package. It is not difficult to load a custom package. Assume that a custom package is used to calculate the circumference and area, as shown in circle. JS:
VaR Pi = 3.14; exports. area = function (r) {return pI * r;}; exports. circumference = function (r) {return 2 * pI * r ;};
How to reference this package? Place circle. js in the same directory as node.exe, you can use require ('./circle') to introduce this package in your code. Execute the nodejs program as follows:
VaR Circle = require ('./circle'); <br/> console. Log ('the area of a circle of radius 4 is '+ circle. Area (4 ));
Note that the prefix "./" cannot be omitted. Otherwise, it is considered as a built-in nodejs package. The following is an example of a multi-layer directory.
VaR Circle = require ('./EDK/base/core ');
If a directory contains a large number of JS files, what can I do? Can I add all files in the directory in batches? Yes, nodejs considers this and supports it. Therefore, nodejs maintains the path of the source file to be searched using a global array require. paths, allowing users to add new directories. Because this is a standard Javascript array, you can use the method to manipulate the JS array.
Require. paths. unshift ('/usr/local/node'); <br/> console. log (require. paths); // usr/local/node,/users/mjr /. node_libraries
Dynamically run JavaScript code
Through the previous introduction, we know that you can use require () to load any JavaScript source file of the disk to the runtime of nodejs runtime. Even the following method is allowed:
// Streamline loading <br/> If (minicoreonly) {<br/> require ('. /EDK/Server/core'); <br/>}else if (loadfull) {<br/> // fully loaded <br/> require ('. /EDK/Server/full '); <br/>}
Despite this situation, both in theory and practice are supported. However, it seems strange, too much, isn't it a big deal? Haha, since that looks bad, we should use more formal practices. We need to write standard code!
If you want to dynamically execute JavaScript code and load it as needed, nodejs allows you to choose several ways. The complie () on the prcess process object is one of them. Process. Compile (Code, filename) is very similar to the eval () function that we are familiar with Js in dynamic execution, similar to dynamic execution of JS Code. Although Eval's "Bad Name" Scheme in Dynamic Language has good components and is not good, nodejs still considers it anyway and provides more powerful encapsulation than eval. Return to process. Compile () and eval
In comparison, the two are also significantly different. The difference is that eval () runs in the current scope and is related to the current scope. You can modify all the members involved in the current scope, with this permission, the eval () Code and the current Code are both "Transparent", but -- process. when compile () is executed, it is isolated from the current scope and is not transparent. In other words, in process. compile () runs code that is invisible to the current scope (cannot see the local scope ). The following example shows the differences between them:
// Compare eval () and process. compile () Example <br/> var localvar = 123, compiled, evaled; compiled = process. compile ('localvar = 1; ', 'myfile. js'); <br/> console. log ('localvar: '+ localvar +', compiled: '+ compiled); <br/> evaled = eval ('localvar = 1;'); <br/> console. log ('localvar: '+ localvar +', evaled: '+ evaled); // localvar: 123, compiled: 1 // localvar: 1, evaled: 1
Because process. compile () does not access the local scope space, so localvar has not changed. If eval () is executed, it can access the scope space of its context, so lcoalvar has changed. If the Running code in process. Compile is incorrect, process. Compile exits the current node and ends the independent process without affecting local code execution.
How can we understand this local scope? To understand the concept of "local scope", we 'd better introduce such a "remote" reference object as opposed to "local". In other words, it is equivalent to adding an extra layer of shell function () {...} to the new Code "outer layer (){...}, to generate a "remote" scope, the scope chain will not be changed. Finally, this function () {...} returns the result returned by process. compile.
But it must be emphasized that it is still in the same global space. Therefore, the above test code is actually a script. runinthiscontext ('localvar = 1; ', 'myfile. JS '); if the global variable localvar is set to affect the local var localvar, the value of VaR localvar is changed to 1 and overwritten.
Another parameter, filename, is an optional parameter used to output information about exceptions or errors.
As mentioned earlier, process. * many methods are derived from node's extension to V8. Simply put, only the C ++ method is called. The C ++ source code shows that V8's native method is called. Extract the source code of process. complie () C ++ as follows (in lib/node. CC ):
Handle <value> compile (const arguments & ARGs) {handlescope scope; If (ARGs. length () <2) {return throwexception (exception: typeerror (string: New ("needs two arguments. ");} Local <string> source = ARGs [0]-> tostring (); Local <string> filename = ARGs [1]-> tostring (); trycatch try_catch; Local <V8: SCRIPT> script = V8: Script: Compile (source, filename); If (try_catch.hascaught ()) {<br/> // hack because I can't get a proper stacktrace on syntaxerror reportexception (try_catch, true); exit (1 );} local <value> result = script-> Run (); <br/> If (try_catch.hascaught () {<br/> reportexception (try_catch, false); exit (1 ); <br/>}< br/> return scope. close (result); <br/>}
Special script object
The specific word "script" is called a "class" in nodejs. What is the role of this script class? The purpose is to run JavaScript code. We can use binding ('evals ') to bind the script in the evals module. First, use VaR script = process. Binding ('evals'). script; to call out its class reference.
The first is the script. runinthiscontext (Code, [filename]) method. The parameter code is the actual JavaScript code to be executed, and filename is an optional parameter used to output information about exceptions or errors. According to the Code provided in the document. the results of compile () are the same, and the local scope is still not accessed. However, the global space is still open. The reason for calling runinthiscontext is basically in line with the original intent.
The following is an example of their comparison:
VaR localvar = 123, usingscript, evaled, script = process. binding ('evals '). script; <br/> usingscript = script. runinthiscontext ('localvar = 1; ', 'myfile. js'); <br/> onsole. log ('localvar: '+ localvar +', usingscript: '+ usingscript); <br/> evaled = eval ('localvar = 1;'); <br/> console. log ('localvar: '+ localvar +', evaled: '+ evaled); // localvar: 123, usingscript: 1 // localvar: 1, evaled: 1
But compared with process. complie () or script. runinthiscontext (), script. runinnewcontext () is much more rigorous. The introduction of code and running Code cannot be affected through the global space. Because script. runinnewcontext () generates a completely new JS runtime space, which is completely isolated from local code. The degree of isolation can be described in a sandbox. Sandbox is a security topic. No, use script. runinthiscontext () in nodejs ()
The security between codes is the highest. Human limitations are necessary.
But is there no mutual access between the two types of code? It's not so absolute, but it's just a little bit round and a compromise. This makes it difficult for programmers or attempted code to easily cross such barriers. Script. runinthiscontext (Code, sandboxobj) can use its second parameter sandboxobj, which is an object type parameter. For dynamic code, this new context is the Global Object of dynamic code. We can use sandboxobj to interact data between two runtime spaces.
Usage:
VaR sys = require ('sys '), <br/> script = process. binding ('evals '). script, scriptobj, I, <br/> sandbox = {animal: 'cat', Count: 2}; <br/> scriptobj = new script ('count + = 1; name = "kitty" ', 'myfile. js'); <br/> for (I = 0; I <10; I ++ = 1) {<br/> scriptobj. runinnewcontext (sandbox); <br/>}< br/> console. log (sys. inspect (sandbox); // {animal: 'cat', Count: 12, name: 'Kitty '}
By the way, something like sandbox can be found in Adobe Ajax air, and it's similar to a person who uses a certain object to communicate with each other!
This section will end with the script class itself. The instantiated script can also obtain the same method called runinthiscontext () and runinnewcontext (), but because it is an instance method (note that it is a lower-case script. runinthiscontext (), according to the prompts in the document ......), The script must be instantiated before it can be used, so its location is also different. The difference is that JS Code is not executed immediately. JS is called only when runinthiscontext () and runinnewcontext () are called, that is, JS is loaded separately.
The two steps for JS running are the same as the differences between other contexts. For example, if the exit is different, it is just about whether the latency is different. Let's take a look at the previous reuse thinking, in addition, nodejs uses the field between the instance method and the static method to identify whether the execution is delayed. You can see if it is.
How does the module mechanism work?
Before the end of the day's study, it is necessary to talk about how the nodeje package is a "package ". The module function code starts from line 1 of node. js. In fact, nodejs will set the "top-level" context space for all the code in require. Open the node. js source code and observe that the top-level context space (contextload = true) can be initialized as long as the environment variable node_module_contexts = 1 ). As a result, the VaR member in the definition package is a private member, and the Public Member is exposed by binding to the exports or this object. For example, in circle
Exports defines two methods: Area () and circumference (), allowing external access to these two methods. This is generally the case when the source code is added in the static mode. The above script dynamic loading method should also be available. No matter which method, in the runtime execution stage, all *. JS uses scripts. runinthiscontext () or process. compile (Code, filename) enters the V8 Parser (starting from line 1), which is roughly the same in principle.
One problem that affects performance is whether multiple declared package references will generate repeated loading? -- It should not. Because nodejs has a caching mechanism, duplicate packages do not need to be reloaded. Use the cache. Using the so-called cache in Javascript is a simple, simple, and consistent concept, which is equivalent to a hash structure, where key/value matches the matched target. It seems that VaR internalmodulecache ={} and VAR extensioncache ={} in nodejs represent the internal module cache and external Template
Cache. These two objects are generally not recycled by GC.
The overall module mechanism is complicated, but it does take up a lot of space for node. js. If you are interested in the require () principle, you can look at the src/node. js source code module section (the most important part is the _ compile () section of the module class, line 1 ).