JavaScript Component Trip (ii) coding implementation and algorithm _javascript techniques

Source: Internet
Author: User
Tags throw exception

First, we need to consider the layout of its source files, which is how the code splits into separate files. Why would you do that? Remember when I mentioned at the end of the last period that this component would use "External code"? To differentiate the use of code, decide to divide the code into at least two parts: an external code file and a Smart Queue file.
Distinguishing between uses is only one, and the other is that it is advantageous for code maintenance to spread to separate files. Just imagine that someday you decide to have the basic functionality of the existing queue management, adding new extensions, or wrapping it up as a component that implements a specific task, and wanting to keep the existing functionality (internal implementation) and invocation (external interface) unchanged, then writing the new code to a separate file is the best choice.

Well, the next issue will focus on the topic of file layout, and now it's time to start cutting to the chase. The first step, of course, is to create your own namespace for the component, and all the code for the component will be confined to the top-level namespace:

var smartqueue = window. Smartqueue | | {};
' 0.1 ';

When initialized, pull it over if you encounter a namespace conflict. Typically, this conflict is caused by repeated referencing of component code, so "pull over" will rewrite the object one time with the same implementation; at worst, if it happens that another object on the page is called Smartqueue, I'm sorry, I'll overwrite your implementation--if there's no further naming conflict, Basically two components can run peacefully. At the same time give it a version number.

Next, create three queues for Smartqueue by three priorities:

var Q = Smartqueue.queue = [[], [], []];

Each is an empty array because there is no task to add to it. Also by the way to build a "shortcut", to access the array to write directly q[n] on it.

Next, our protagonist task is a grand debut-how to new a task, defined here:

  if (ErrorArray dependencies: [];}; 

Inside the details do not say, there are necessary comments, the general our code can also do self-describing, the following code is the same. Here to tell the customer (user): You want to create a Smartqueue.task instance, you need to pass at least one parameter to the constructor (the last 3 can be omitted for default processing), otherwise throw exception to wait.

But that's not enough. Sometimes a customer wants to clone a new instance from an existing task, or fix a "health body" (a real Task object instance) from a "disabled body" (an object with a partial task attribute), which is a bit uncomfortable by the way it was constructed--the customer has to write:

", obj.dependencies);

I am lazy, I just want to preach FN and dependencies two attributes, do not want to do additional things. Okay, let's refactor the constructor:

  Var_setuptask =function(FN, level, name, dependencies) {If(typeoffn!== FUNCTION) { throw  New  Error ( ' Invalid argument type:fn. ');  This.fn = fn;  This.level = _validatelevel (level)? Level:level_normal; //Detect type of name  this.name =  typeof name = = = STRING && name? Name:  ' t ' + _id++; span>//dependencies could is retrieved as an ' Object ' and so use instanceof instead.  this.dependencies = dependencies  instanceof  Array? dependencies: [];};  var t = Smartqueue.task =  function (Task) { if (Arguments.length >  1) {_setuptask.apply ( t his, arguments);  else {_setuptask.call ( this, Task.fn, Task.level, Task.name, task.dependencies); The //init context/scope and data for the task.  this.context = task.context | | window;  this.data = Task.data | | {}; }; 

In this way, the original structure can continue to work, and the lazy man above can be passed to a "disabled body":

New Smartqueue.task ({dependencies:obj.dependencies});

When a constructor receives more than one argument, it is treated as if it were the previous scheme, otherwise the unique parameter is considered a Task object or a "disabled body." This passes the new instance to the Refactored method through the JavaScript apply / call method _setupTask , as the context of the method (also known as scope), apply / call The JavaScript passes the context between the methods The magic weapon, must attentively realize oh. At the same time, the user is allowed to define the task.fn context at execution time and pass the custom data to the FN in execution.

What is the classic three-segment JavaScript object?

    1. Defining the constructor of an object
    2. Defining properties and methods on a prototype
    3. New object, take it.

Therefore, you SmartQueue.Task define properties and methods for the object's prototype. Previous Analysis Task (Task) has several properties and methods, some of which we have already defined in, and the _setupTask following are the properties and methods provided by the prototype:

T.prototype = {Enabled:True,Register:function() {VarQueue = q[This. level];If(_findtask (Queue,This. Name)!==-1) {Throw New Error(' Specified name exists: '+This. name); } Queue.push ( this);  Changeto:  function (level) { if (!_validatelevel) { throw  new  Error ( ' Invalid argument:level '); Level = parseint (level,  10);  if ( this.level = = level) { return;} q[ This.level].remove ( this);  this.level = level;  this.register ();},  Execute:  function () { if ( this.enabled) {//pass context and data  This.fn.call ( this.context,  this.data);},  toString:  function () { var str =  T His.name;  if ( this.dependencies.length) {str =  ' depends on: [' +  this.dependencies.join ( ', ') + 
                                                 
                                                   '] '; 
                                                   return str;}  
                                                              

As you can see, the logic is very simple, perhaps you have swept the code in a minute, the corners of the mouth inadvertently revealed a hint of the. However, here is a simple and usually least valued toString method. In some high-level languages, the implementation of a custom object is toString recommended as a best practice guideline. Because toString it is easy to provide useful information in the debugger, you can easily write the object basic information to the log; In a unified programming model, implementation allows toString you to write less code.

Well, let's move on, we're going to implement Smartqueue's specific features. Last period analysis, Smartqueue had only one instance, so we decided to create the method directly below Smartqueue:

  function () {
    Q.foreach (0;});

Here we use the traversal method provided by JavaScript 1.6 for the Array object forEach . This is because we assume that the "external code" has been run before. Sets the property of the array object length to 0 cause it to be emptied and all items (array cells) are freed.

The final method fire , which is the most important method of the entire component, is responsible for sorting all task queues and executing them one after the other. Because the code is a little bit longer, here we only describe the algorithm and implementation of the sort usage , where the complete code is.

Var_dirty =True,A flag indicates weather the Queue need to be fired._sorted = [], index;Sort all queues. Refhttp://en.wikipedia.org/wiki/Topological_sorting Var_visit =function(Queue, Task) {If(Task._visited >= 1) {task._visited++;  return;} task._visited =  1; //Find out and visit all dependencies.  var dependencies = [], I; Task.dependencies.forEach ( function (dependency) {i = _findtask (queue, dependency);  if (i!=- 1) {Dependencies.push (queue[i]);}); Dependencies.foreach ( function (t) {_visit (queue, t);});  if (task._visited = =  1) {_sorted[index].push (Task);}, _start =  function (queue) {Queue.foreach (
                      
                       function (Task) {_visit (queue, Task);}); }, _sort = 
                        function (suppress) { for (index = level_low; index <= level_high; index++) { var queue = q[ Index]; _sorted[index] = []; _start (queue);  if (!suppress && queue.length > _sorted[index].length) { throw  new  Error ( ' Cycle found in queue: ' + queue '; } } };
                       

We will sort the tasks within the same priority by the dependencies specified by the task, ensuring that the dependent tasks run before the dependent tasks are set. This is a typical depth-first topology sequencing problem, and Wikipedia provides a depth-first sorting algorithm, which is described as follows:

Pictures from Wikipedia

    1. Access each node to be sorted
      1. If it has already been visited, return the
      2. Otherwise marked as visited
      3. Find each node where it connects (is dependent here)
      4. Skip to inner layer 1 recursively access these nodes
      5. When you're done, add the current node to the sorted list
    2. Continue to access the next

If a depends on B, B depends on C, C depends on a, then these 3 nodes form a cyclic dependency. It is pointed out that this algorithm can not detect the cyclic dependency. By marking whether a node has been accessed, you can resolve the recursive death cycle caused by cyclic dependency. Let's analyze the loop dependency scenario:

When starting with Node A, it is marked as accessed, and it has been accessed from node C back to Node A. But at this point C does not know whether A is on its own upstream chain, so it is not directly possible to determine the cyclic dependency, because a may be the other node that has been "processed" (running the inner-layer recursion). If we know if a node is visited for the first time, we can tell which is the case.

Modify the above algorithm to change "access" to "Access Count" ( task._visited++ ). When a node is accessed 1 times ( task._visited === 1 ), it is added to the sorted list, and if the number of nodes to be sorted is more than the ordered (), then the number of nodes to be sorted has queue.length > _sorted[index].length a circular dependency.

At this point, the encoding implementation of the queue management component has been completed. What the? How to use it? Very simple:

New Smartqueue.task (function () {alert (new Smartqueue.task (function () {alert (' myname '); T1.register ( ); T2.register (); Smartqueue.fire ();   

More features, such as task dependencies, await you to discover OH.

This issue of the code is a few local fragments, some helper method code is not posted. To view the complete code please visit here . We'll see how to manage the component files and build the components later.

Related Article

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.