Implementation of Qomolangma (I): Implementation of the kernel loading module system. js

Source: Internet
Author: User

========================================================== ==========================================================
Qomolangma openproject V1.0

Category: rich Web Client
Key words: JS Oop, JS framwork, rich Web Client, Ria, Web component,
Dom, dthml, CSS, JavaScript, JScript

Project Initiation: aimingoo (aim@263.net)
Project Team: aimingoo, Leon (pfzhou@gmail.com)
Contributor: Jingyu (zjy@cnpack.org)
========================================================== ==========================================================

1. Overview of the system. js Module
~~~~~~~~~~~~~~~~~~

System. JS is the first loading module of qomo. This module provides three functions:
-Basic $ debug () Functions
-Basic $ import () Functions
-Kernel subsystem Loading

System. JS is Firefox compatible.

Ii. Composition and loading of the kernel Subsystem
~~~~~~~~~~~~~~~~~~

In qomo, the so-called kernel refers to the system function layer composed of modules directly loaded in system. js. System. js
Implemented the $ import () function and loaded the following modules through it:
----------
$ Import ('names/namedsystem. js'); // namespace management
$ Import ('rtl/jsenhance. js'); // enhancements based on standard JS

$ Import ('jsunit/debug. js'); // enhanced debugging output
// More... // debug analysis functions (profiler/unit test, etc)

$ Import ('rtl/error. js'); // handle errors and exceptions
$ Import ('rtl/object. js'); // implements object-oriented language features and the base class tobject
$ Import ('rtl/ajax. js'); // tiny Ajax sub system
// More... // other kernel-level features (such as SOA and Interface)
----------

Qomo kernel can be reduced: If developers do not like to use namespaces, they can not load namedsystem. js;
Or, you do not need to use the enhanced OOP feature at all, so you can not load object. js. -- In other words, qomo's
The structure allows developers to reorganize their own kernel subsystems and develop their own languages and frameworks on this basis.

Qomo implements this feature by using "function overloading" and "function retelling" technologies. In system. JS
$ Debug uses the function overload technology, while some of the implementation features in $ import () use the function restatement"
.

To put it simply, "function overloading" means to re-write the current function in the delayed code, while "function retelling" means to delay.
In the code, re-describe one or more features of the function (and implement the Code ). -- Basically, if you have good
Good code Habits can be achieved using "more perfect object-oriented design" + "Oop polymorphism"
Technology. However, system. JS is implemented at a very underlying and kernel level. I don't want it to become large and inefficient,
So I used a technique to replace the design.

Iii. $ debug () Analysis
~~~~~~~~~~~~~~~~~~

In the JScript system, a debug object is provided, which is used to input information in the debugger console. If
If you use C and other advanced programming languages, you should know the outputdebugstring () function. And this debug
The role of an object is the same as that of an object. This object provides two methods: Debug. Write () and Debug. writeln ().

However, qomo tries to implement a more valuable debugging output subsystem. For example, sending an object to an output console, or
Used to record efficiency analysis information. Therefore, qomo provides the $ debug () function.

However, $ debug in the Basic module system. js does not provide these (complete) features. In system. JS
$ Debug () only outputs string information to document. This is the same as debug. writeln (), but it only outputs
The target of the parameter is different: the debugface is directed to the console, and the debug(faces are directed to the specified Doc ument object.

The $ debug () Implementation in system. js can be very simple. For example:
----------
$ DEBUG = Document. writeln;
----------

However, such code may fail in Firefox. Firefox protects some object methods so that they cannot be assigned
Values cannot be used to modify these actions by assigning values to JavaScript objects or variables. Therefore, in system. JS
The final code used is as follows:
----------
$ DEBUG = function (){
Document. writeln (array. Prototype. Join. Call (arguments ,''))
};
----------

In this way, the arguments parameter passed in by $ debug is treated as an array and the join () method in the array object prototype is used.
Concatenate a string and use document. writeln () to output it.

We mentioned earlier that $ debug () uses the function overload technology. This is because $ debug () is actually only
Then load namedsystem. js and jsenhance. js. -- If an error occurs during their "loading"
You can use $ debug () to output information by mistake or exception (or for debugging purposes. However, next:
-Step 1: at the end of jsenhance. JS, a line of code sets $ debug to a null function (nullfunction );
-Step 2: In the Debug. js header, you can rewrite the $ debug () function with a piece of code to implement your own output console.

This ensures that the $ debug () function can be used in any code to output information. And the output will be
In this way:
-If it is loading the kernel, an error message is output to the document. Otherwise,
-If the Debug. js module is loaded, an output console is provided to display information or objects (data). Otherwise,
-$ Debug () information is exposed to screen defects, or a third-party module loaded by the user can output debugging information or error information.

Developers can use $ debug () freely and securely without worrying about how it is implemented or how it is output. For example
If you do not want web viewers to see it, you only need to remove Debug. js (or a third-party module) without removing the source code.
Function call in.

IV. Implementation of $ import ()
~~~~~~~~~~~~~~~~~~

To make kernel-level modules such as system. js do not affect the definition of global variables
The $ import () function is also included. The following techniques are used to declare a function:
----------
$ Import = function () {// <--- anonymous function 1
VaR DATA =...
Function Foo (){...}

Return function () {// <--- anonymous function 2
...
};
}();
----------

In this way, both Foo () and data are declared in an "anonymous function 1", so they will not
The naming of Bureau variables has an impact (duplicate names ). $ Import () is actually the result returned after "anonymous function 1" is executed.
Result: anonymous function 2. -- In JavaScript, "functions" (objects) can be returned as execution results of other functions.

Importantly, because "anonymous function 2" is in the same "context" as data and foo (), it can
Local access to these variables and methods. Other external and Global Code cannot see the Implementation Details of $ import.

With this technique, $ import () hides many internal functions and information. The implementation is as follows:
----------
$ Import = function (){
// For Firefox only
VaR _ sys_tag =
VaR _ pai_tag =
VaR _ charset =

// Make the remotely obtained script
VaR tocurrentcharset =...

// Check the current webpage character set to determine the encoding used by the. JS File
VaR _ UU =...

// Get the HTTP connection object (httprequest) in different Firefox and IE versions)
VaR gethttpconnect =...

// A unique http connect used in $ import ()
// (Script execution is sequential, so there is no need to use Asynchronous HTTP connections)
VaR _ HTTP =...

// Whether to use XMLHTTP to obtain the script code. If it is false, use the <SCRIPT> label to load
VaR _ xml =...

// Connect to the Code through _ HTTP and convert the encoded Function
Function httpget...

// Obtain the URL of the currently running script
// (For example, the system. js URL implements the $ import () feature using the relative file path)
Function activejs...

// Important system information that may be used or "restated" in the future
VaR _ sys =...

// An activejs stack is used to implement the "call $ import ()" feature in $ import () code.
VaR _ stack =...

// Remotely read, load the script for the specified SRC, and execute
VaR _ load_and_execute =...

// Functions returned externally
Function _ import (SRC ){
/* Qomo core system ..*/
_ Load_and_execute (SRC );
}

// Other code (see "_ sys object value" in the following article ")
//...

Return _ import;
}
----------

The following describes the main functions one by one:

1. webpage Character Set, Unicode and Decoding
~~~~~~
In Ajax systems, codec is an important issue. Because Microsoft's XMLHTTP Control,
The XMLHTTPRequest object in Firefox recognizes the content obtained remotely as Unicode encoding by default. XMLHTTP
By default, the control recognizes the encoding method of uniocde by using the leading characters in the remote content. Therefore, unless otherwise specified,
XMLHTTP can correctly parse the remote content of the following encoding method:
----------
VaR _ UU = _ charset in {
'Utf-8': NULL,
'Unicode ': NULL,
'Utf-16': NULL,
'Unicodefffe ': NULL,
'Utf-32': NULL,
'Utf-32be': NULL
};
----------
If XMLHTTP cannot parse encoding by leading characters, it uses the UTF-8 encoding format by default for remote content.

However, this is only about the format of "remote content" (for example, the encoding storage format used by. js files ). In most cases, we will
The following tags are used to describe the encoding of the current webpage:
----------
<Meta http-equiv = "Content-Type" content = "text/html; charset = gb2312">
----------
Without the HTML Tag description, ie uses the default settings to decode and display the webpage. This situation
Under, document. charset will be set to "_ autodetect_all", or you can choose "View-> encoding" in the IE menu.
Character Set. When charset = "_ autodetect_all", you can access document. defaultcharset
Obtain the character set selected for decoding.

We can see a problem: the character set settings on which "Current webpage" decoding and "remote content" decoding depend are inconsistent.

As a matter of fact, the trouble is not only that. The string and other content understood in the JScript engine will also use Unicode characters.
Character Set. In this regard, no matter what the. js file encoding format is or what the webpage encoding format is, it will not be changed.
That is to say, in a charsett = gb2312. js file that uses gb2312 encoding, you use escape ()
Or Unescape () will be encoded and decoded in a unicode environment. What's more, even if you forcibly specify
The decoding method of a string. When it is finally displayed on a webpage, it will not be as expected. For example:
----------
// The gb2312 bytecode of the string "this is a test"
VaR S1 = '% D5 % E2 % Ca % C7 % D2 % BB % B8 % F6 % B2 % E2 % Ca % D4 ';

// Decoding
VaR S2 = Unescape (S1 );

// Display
Document. writeln (S2 );
----------
This code is not displayed normally on the webpage of the UTF-8 or gb2312 character set.

In qomo, the decoding of the $ import () function is based on a hypothesis: "The 'remote content' of the. js file and the 'current web'
Must use the same character set ". -- It must be noted that this is an ideal situation in a closed environment. If
If you try to use $ import () to read RSS content, you may have to "process UTF-8 encoding on the gb2313 webpage"
Code RSS data. Therefore, you should be clear that kernel-level $ import () is mainly used to process qomo systems.
While other "remote content" should be carried out by more complex Ajax systems.

Therefore, qomo believes that the remote code is the same as the current webpage code. Therefore, when the webpage character set is Unicode,
Remote content does not need to be decoded; otherwise, Unicode understood by XMLHTTP should be converted to the current character set. This
Conversion depends on the current web page string settings, that is, the value of the _ charset variable inside $ import.

Qomo implements the decoding function tocurrentcharset () in $ import (). The decoding function only implements
Character Set processing. If other (non-Unicode) decoding is required, you must modify
Some code.

When qomo's decoding function initially implements gb2312 bytecode processing, it draws on a widely used bytes2bstr ()
Function to implement the improved version of vbs_joinbytes (). Reduces string connections in the vbs_joinbytes () function
And length recognition times, greatly improving the efficiency. However, when this function is finally implemented
Tips on using the Unescape ()/escape () function to encode strings published in the classic forum. Because
The code is greatly optimized, and the new tocurrentcharset () is about 30% better than the Code provided by bjhaoyun.

In these decoding schemes, tocurrentcharset () and bjhaoyun's recode () adopt the same scheme, but the overall
Performance is improved by 30%. In general, tocurrentcharset () is three times faster than vbs_joinbytes ();
When the English content is dominant, it can be nearly 10 times faster. However, there are many Chinese characters (such as full Chinese text ).
In this case, the performance of vbs_joinbytes () is superior, or even 50% faster than that of tocurrentcharset.

Because $ import () mainly processes the English code. js script, tocurrentcharset () is used ()
As a built-in decoding function. You can refer to the test webpage t_decodeunicode.html for several decoding methods.

(Currently,) qomo does not provide a decoding function for content loaded using the XMLHTTPRequest object in Firefox.

2. Differences between XMLHTTP loading and <SCRIPT> tag Loading
~~~~~~
If the XMLHTTP object cannot be created or the encoding cannot be processed properly. Qomo provides a backup scheme, that is
Use the <SCRIPT> label to load the module and other remote content.

However, the two cannot be completely replaced, so there are some differences that must be added.

First, the content loaded by XMLHTTP is stored in the responsebody attribute of the XMLHTTP object (such as _ HTTP in qomo,
This is a byte-based safearray array, and JScript can only process the variant-based type.
. Therefore, qomo calls VBScript CSTR () to convert it into a string, and then
Tocurrentcharset () processing.

-- If the <SCRIPT> label is used for loading, this entire decoding process is not required. Because <SCRIPT>
You can specify the charset attribute or directly use the script file of the character set "Same as the current webpage.

<SCRIPT> is better than XMLHTTP. But in fact, another advantage of XMLHTTP makes <SCRIPT>
The sky is everywhere.

In asynchronous mode, the content loaded by XMLHTTP can be executed immediately. So in this example:
----------
<SCRIPT>
$ Import ('1. js ');
$ Import ('2. js ');

Foo_in_js1 ();
</SCRIPT>
----------
The first two lines of 1. js and 2. js are loaded and executed immediately. Therefore, in the 1. js file foo_in_js1 (), you can obtain
Run. In the following example:
----------
<SCRIPT>
Document. writeln ('<SCRIPT src = "1.js"> <', '/SCRIPT> ');
Document. writeln ('<SCRIPT src = "2.js"> <', '/SCRIPT> ');

Foo_in_js1 ();
</SCRIPT>
----------
The content written by document. writeln () to the webpage appears after the </SCRIPT> label. Therefore, 1. js and
2. js will be loaded, parsed, and executed only after all the current script blocks are executed. -- This also means
The function foo_in_js1 () cannot be called successfully.

Obviously, in a large framework system, we will use the following code to describe the current module (or
RMB) Dependency:
----------
$ Import ('/OS/Win32/filesystem /*');
$ Import ('/OS/Win32/UI /*');

// Some code...
----------
In this case, the filesystem and UI modules should have been loaded and executed before "some code" is executed.
As we can see, <SCRIPT> does not support this feature.

Therefore, replacing $ import () with <SCRIPT> In the qomo kernel is only a benefit, and it cannot complete $ import ()
. -- But in some simplified, small, and customized qomo systems, it is still available
. Pay attention to the differences between XMLHTTP and <SCRIPT> and the negative effects of these differences.

3. Different performance of execScript () and eval ()
~~~~~~
Jscriptcontains the window.exe cscript () method, but it is not in the Javascript specification. Therefore, Firefox
No execScript is implemented. The other method is similar to the Global. eval () method.

In the JScript of IE, Eval () executes a string and returns the result. During execution, the function is called.
Context. Therefore, if function a calls eval (STR), the script code in string STR can be used,
Modifies and affects local variables in function. Window.exe cscript () directly uses the Global Context Environment,
Therefore, the string STR in execScript (STR) can affect global variables. -- Includes declaring global variables,
Function and object constructor.

Therefore, after using XMLHTTP to remotely obtain the content of the. js file, we can use execScript ()
To execute it. This execution is consistent with the execution effect in the <SCRIPT> label.

In terms of JavaScript conventions, global. eval () does not have the ability to execute in a global context. In
In an accidental code test, I found that eval () in Firefox has a strange feature:
-If window. eval () is used for execution in the function, the global context environment is used;
-If eval () is used for execution, the context of the current function is used.

I don't know whether this is the language feature that Firefox provides for Ajax, or a javascript implementation bug.
However, I have tested several versions to demonstrate this effect. Therefore, in $ import (), I used window. eval to replace
Window.exe cscript () to implement the qomo kernel of Firefox.

For more information about this feature, see the test webpage t_eval.html.

4. Relationship between the load path and activejs
~~~~~~
When loading a. js file in the qomo system, a flexible path locating policy is adopted:
-If SRC starts with "XXXX: //", use "full path" to locate it;
-If SRC starts with "/", use the absolute path starting with the root path of the website where the current document is located;
-Otherwise, use relative path.

However, the following problems are troublesome. If the webpage URL is "http: // site/SUB/a.html ",
The qomo system is deployed on the "http: // site/qomo/" website. The following two methods can be used: a.html
Load System. JS:
----------
<SCRIPT src = "../framework/system. js"> </SCRIPT> <! -- One -->
<SCRIPT src = "/qomo/framework/system. js"> </SCRIPT> <! -- 2 -->
----------

Because XMLHTTP uses the current webpage for relative positioning, it is the same as the <SCRIPT> object in this respect.
Obviously, the following code should be used to import namesystem. js in system. JS:
----------
// $ Import () is implemented using XMLHTTP
$ Import ('../framework/names/namesystem. js'); // method 1
$ Import ('/qomo/framework/names/namesystem. js'); // method 2
----------

The current web page must be used for relative positioning in various. js files, which leads to inflexible script compiling in the system.
Pay attention to the location of other. js files in the code, which is not convenient during directory transfer. -- You can also use
But in this way, qomo's deployment in the application system is very poor.

Therefore, qomo's understanding of the loading path is based on the "current. JS File. In this way, in system. JS
You can import namesystem. js as follows:
----------
// System. JS is located on/qomo/framework/path
$ Import ('names/namesystem. js ');
----------
In namesystem. JS, if you want to import a. js file that is also located in the names directory, you only need:
----------
$ Import ('A. js ');
----------

Therefore, qomo implements a _ Stack Array variable in $ import. It is used as a post-in-first-out queue,
To ensure that curscript always has the URL of the. js file currently being executed. In this way, $ import () can be used.
Calculate the relative path of newly imported. js in the. Js being executed ".

The trouble has not been solved. Because qomo's understanding of the system is "Detachable", it will allow the following:
Code:
----------
<SCRIPT src = "../qomo/framework/system. js"> </SCRIPT>
<SCRIPT src = "../qomo/controls/components. js"> </SCRIPT>
<SCRIPT src = "../qomo/db. js"> </SCRIPT>

<SCRIPT>
$ Import ('../qomo/common/tools. js ');
</SCRIPT>
----------
In such a system, the "current path" defined in system. JS, components. JS, and DB. js"
They are all different. Therefore, we must have a way to know which. JS File $ import () is currently being executed,
And get its SRC.

The activejs () function is used to obtain the URL of the. js file imported by <SCRIPT>, and then you can
Use the relative path to load other. js files.

There is a chance to get "current. js path" in IE ". In use, I found the readystate of the <SCRIPT> object.
Attribute can help us achieve this requirement. To put it simply, when the. js file is executed, script. readystate
The value will be "interactive ". If we list all the <SCRIPT> tags, we can find this
Script object to obtain the SRC value.

Activejs () uses this feature for implementation. However, the DOM script object in Firefox does not have readystate.
Therefore, some Firefox Code uses the method to identify the file name "system. js. --
However, I cannot provide qomo with the same features as IE in Firefox. Between them
The differences are as follows:
-(Unless in system. JS) $ import () cannot use the relative path feature in Firefox
-If you modify the file name of system. JS, Firefox cannot use the relative path in system. js.

Finally, use $ import () in a common <SCRIPT> script block.
Paths serve as the basis for calculating relative paths. In this case, activejs () returns an empty string. -- Script. SRC
It is also an empty string.

4. Why is it not a namespace?
~~~~~~
Qomo supports but does not force namespace usage. This is why qomo implements $ import () in such a complicated way. Because
In a system that supports "namespace registration", you can do this:
----------
// 1. In system. js
Names. registernamespace ('/framework', 'currentpath ');

// 2. In my_script.js
$ Import ('/framework/debug. js ');
----------

In this way, because of the existence of the namespace, the system can quickly reflect the location and relationship of the module. However
In this form of manual registration through registernamespace (), users must use the namespace forcibly.
-- Although there is nothing wrong with this, it is indeed the best way to use it on Firefox. However, I am still
Qomo implements the self-registration namespace management subsystem, which makes it possible
It is automatically obtained and calculated based on the path relationship when $ import () is called.

The qomo system can run normally even if the namespace module is not loaded. This is a feature of qomo. Although this
Features cannot be implemented in Firefox. However, I still propose to developers who like fast and lightweight kernels.
A possible choice is provided.

However, we will discuss the specific implementation of namesystem. js later. This time, we only discuss system. js.

5. Value of _ sys object
~~~~~~
In the qomo kernel, some code can be used externally. For example, tocurrentcharset () for decoding ()
And the asynchronous XMLHTTP object used to import. js files.

However, $ import () encapsulates these details. In qomo's understanding of the Code, there is "no need, don't
To consume as few global variable names as possible.

How can valuable resources be used? Qomo declares the object _ sys in the $ import () function ,:
----------
VaR _ sys = {
// Read the content to understand the running status of $ import ().
'Scripts ':{},
'Curscript ':'',

// Ajax kernal
'Httpget': httpget,
'Httpconn': gethttpconnect,

// Decode for XMLHTTP. responsebody
'Bodydecodecode': tocurrentcharset,

// Obtain the SRC of the script currently being executed
'Activejs': activejs

//...
}
----------

Then, some methods are implemented for $ import to access this object:
----------
_ Import. Get = function (n ){
Return eval ('_ sys [N]');
}

_ Import. Set = function (n, v ){
Return eval ('_ sys [N] = V ');
}

_ Import. onsysinitialized = function (){...}
----------

In this way, you can write the following code in qomo to use the _ sys object:
----------
VaR httpget = $ import. Get ('httpget ');
VaR STR = httpget ('HTTP: // www.sina.com.cn /');

Alert (STR );
----------

The provision of $ import. Set () is related to the "Function Review" technology we mentioned earlier.
Because $ import. Set () modifies the "internal function entry" of the _ sys object, you can
Call the Set () method to "re-describe" The implementation methods of these function entries. These can be repeated
The content of depends on what the _ sys object exposes. In qomo, these can be repeated:
----------
VaR _ sys = {
'Transitionurl': function (URL ){...}
'Srcbase': function (){...}

//...
}
----------

Some people should have noticed that $ import () does not implement the "Import package or namespace" function.
These function entries are disclosed here so that they can be "restated ":
Repeat the implementation of transitionurl () and srcbase (), you can
Namespaces and packages are supported.

However, these "repeatable" features are still directly related to the kernel. Therefore, $ import () is made public.
An event onsysinitialized () is activated at the very beginning of system. js. --
The response code of this event is used to clear these methods for $ import (), such as set () and set.

Through $ import. Get ()/set (), we can set $ import () in other. js imported by system. js ()
You can also use the features and code that have been implemented by $ import. Load and
After the execution is complete, the reserved functions for the system kernel will be triggered by onsysinitialized ().
Cleared.

The system. js module implements a tension-and inclusive loading framework for the following qomo
The system provides a sufficient foundation.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.