_javascript techniques for the seed module of JavaScript frame design

Source: Internet
Author: User
Tags closure object object setinterval mootools

The seed module is also called the Core module, which is the first part of the framework to perform. Even if a single file function library like jquery, its interior is also divided into many modules, there must be some modules executed immediately before the first execution, some modules only to be implemented. Some modules are dispensable, the sense of existence is weak, only under the specific browser to run.

Seed module is one of the pioneers, it does not necessarily require a full functional, well-designed, but must be extremely extensible, commonly used, stable.

Extensibility refers to the ability to include other modules through them, and it is often used to mean that most modules can be used to prevent duplication of work. Stability is not easily replaced by new methods in version iterations.

Referring to the implementation of many frameworks and libraries, we think that the seed module contains the following functions: Object extension, array, type determination, simple binding and uninstall, no conflict handling, module loading and domready. The content of this chapter is modeled on the mass framework seed module.

Https://github.com/RubyLouvre/mass-Framework

1. Namespaces

The seed module, as the first part of a framework, is responsible for building the overall infrastructure. jquery has a good start, using Iife (call function expressions immediately).

Life is the most important infrastructure in the modern JavaScript framework, and it wraps itself like a cell to prevent variable contamination. Just like a foothold, this is the namespace, such as Prototype.js,mootools, they make you feel the existence of the framework, it is profound to JavaScript, DOM, BOM, and so on every corner of the entire execution environment, to the native object prototype on the line extension. Because of the strong opposition of Douglas (JSON author), the new framework is built on namespaces.

Let's see how to simulate namespaces on JavaScript. JavaScript everything is object-based, but only objects of the type conform to the requirements, such as function, RegExp, object, but the most commonly used object and function. We add an attribute to an object that is an object, and we can add an object to it, and in this way we can build our framework methodically. The user wants to invoke a method, which is invoked as a xxx.yyy.zzz ().

  if (typeof (Ten) = = "undefined") {
    Ten = {};
    Ten.function = {/* slightly
    /} Ten.array = {/* slightly
    /} Ten.class = {/* slightly
    /} TEN.JSONP = new Ten.class (/* slightly * *)
    Ten . XHR = new Ten.class (* * slightly)
  }

Throughout the implementation of the large class library, a basic definition of a global variable as a namespace, and then extended to it, such as Base2 base,ext ext,jquery Jquery,yui yui,dojo dojo,mochikit. From the point of view of the pollution degree of the global variable, it is divided into two kinds:

Prototype.js and MooTools and Base2 are grouped into one category, and the prototype philosophy is to extend the native object of JavaScript. Earlier in the year, prototype almost called the standard of fact. Therefore, the problem of coexistence with other libraries is not taken into account. Basic prototype, but also developed such as SCRIPT.ACULO.US,RICO,PLOTR,PROTOCHART,SCRIPT2 and other very good class library to a large class of charging plug-ins. Moreover, some of the sources of Plug-ins are almost prototype related, such as Lightbox. MooTools is an upgraded version of Prototype.js, more oo, and full replication of its APIs. Base2 is to fix ie bug, let IE have standard browser API, so also all native object pollution again.

The second category is Jquery,yui,ext these frameworks, YUI and ext is the object nested object of the way to build. jquery is an alternative, it's selector-oriented, so its namespace is a function that makes it easy for users to pass in a string of CSS expressions. It then looks through the selector, and finally returns a JQuery object instance.

jquery started out as a prototype using $ as its namespace, so it implements many of the library's coexistence mechanisms, arbitrarily switching between $ and jquery, and jquery's multiple-Library coexistence principle is simple, so it later becomes standard for many smaller libraries. First, save the namespace in a temporary variable (note that the object is not something of its own frame, possibly prototype.js or otherwise), and then use a noconflict to put it back.

  jQuery1.2
  var _jquery = Window.jqury, _$ = window.$;//Save the possible variable with the same name
  jqury.extend ({
    noconflict: function (deep) {
      window.$ = _$;//Then put it back
      if (deep)//Add if (deep && window.jquery = = jQuery)
        window . Jqury = _jquery;
      Return Jqury
    }
  })

Reference: http://zhidao.baidu.com/question/1239712776390687219.html

But jquery's noconflict is only useful for a single file's class library framework, like Ext can't replicate. So after renaming the namespace, ext is null, and then the new JavaScript file is introduced by dynamically loading the file, which is invoked in Ext, resulting in an error.

2. Object extension

We need a mechanism to add new features to our namespaces. This method is commonly referred to as extend or mixin in JavaScript. JavaScript objects are free to add, change, and delete members of the property descriptor before they are born, so it is convenient to extend an object. A simple extension method implementation is the case.

  function Extend (Destination,source) {for
    (var property in source)
      destination[property] = Source[property];
    return destination;
  }

However, the older version of IE has a problem here, and it thinks that the prototype method like object is not supposed to be traversed, so for-in loops are property names that cannot traverse valueof and ToString. This leads to the problem of simulating the Object.keys method when it is realistic.

  Object.keys = Object.keys | | function (obj) {
    var a = [];
    For (A[a.length] in obj);
    return A;
  }

In different frameworks, this method also has a different implementation, such as Ext for apply and Applyif two methods, the former will overwrite the target object's same name, and the latter will not. Dojo allows multiple objects to be merged together. jquery also supports deep copies. Here is the mix method for mass farmework. Supports multiple object merging and selecting whether to overwrite.

  function Mix (target,source) {//If the last argument is Boolean, determine whether to overwrite the attribute with the same name
    var args = [].slice.call (arguments), i = 1, key,
      ride = Typeo F Args[args.length-1] = = "Boolean"? Args.pop (): true;
    if (args.length = = 1) {//handling $.mix (hash) case
      target =!this.window? this: {};
      i = 0;
    }  
    while (Source = args[i++]) {
      for (key in source) {//Allow object Leelawadee Miscellaneous, user guarantees are object
        if (Ride | |!) ( Key in Target)) {
          Target[key] = Source[key];
    }} return target;
  }

3. Array

There are many class array objects in the browser, such as arguments within the function, through Document.forms, Form.elements,document.links, Select.options, Document.getelementsbyname,document.getelementsbytagname, ChildNodes, children and other ways to obtain the combination of nodes (Htmlcollection, nodelist) or customize the object according to some special wording.

The class array object is a good storage structure. However, the functionality is so weak that in order to be able to use the convenient methods of a pure array, we will do the conversion before we process them.

Generally, the use of [].slice.call can be converted, but the function is not sufficient, but in the old version of Htmlcollection, NodeList is not a subclass of object, using the method as above will cause IE to perform an exception. Let's take a look.

Jquery:makearray

  var makearray = function (array) {
    var ret = [];
    if (array!= null) {
      var i = array.length;
      if (i = = NULL | | typeof array = = "string" | | jquery.isfunction (array) | | array.setinterval)
        ret[0] = array;
      else while
        (i)
          ret (-i) = Array[i];
    }
    return ret;
  }

Mass of the implementation of the beginning of the distinction, directly [].slice.call,ie the words of their own hands to implement a slice method

  $.slice = window.dispatchevent? function (nodes,start,end) {return
    [].slice.call (Nodes,start,end);
  }: Function (nodes,start,end) {
    var ret = [],
       n = nodes.length;
    if (end = = void 0 | | typeof end = = "Number" && isfinite (end)) {
      start = parseint (start,0) | | 0;
      End = end = = void 0? N:parseint (end,10);
      if (Start < 0) {
        start = n;
      }
      if (end > N) {end
        =n
      };
      if (end < 0) {End
        = n
      };
      for (var i = start, I < end; ++i) {
        Ret[i-start] = nodes[i];
      }
    return ret;
  }

4. Type of judgment

JavaScript exists in two sets of type systems, one for basic data types and the other for object type systems. The base data type includes 6. are undefined, string, null, Boolean, function, object, respectively. The basic data types are detected by typeof. The object type system is based on the base type system and is detected by instanceof. However, JavaScript's own two sets of identification mechanisms are very unreliable, so the birth of the Isxxx series. Take typeof, it can only roughly identify string, number, Boolearn, function, undefined, object 6 kinds of data types, unrecognized null,regexpargument and other types of objects.

  typeof null//=> "Object"
  typeof document.childnodes//=> Safari: "function"
  typeof Document.createlement (' embed ')//=> ff3-10 "function"
  typeof document.createlement (' object ')//=> ff3-10 " function "
  typeof document.createlement (' object ')//=> ff3-10" function "
  typeof/\d/i// In the browser that implements ECMA262V4 returns the "function"
  typeof window.alert//ie678 "Object"
  var iframe = document.createlement (" iframe ")
  Document.body.appendChild (iframe)
  Xarray = window.frames[window.frames.length-1]. Array;
  var arr = new Xarray (1,2,3)//=>[1,2,3]
  arr instanceof Array;//False
  isNaN ("AAA")//=> True

In addition, people used to document.all to determine whether IE, which is actually very dangerous, because, with document.all to get all the elements of the page is a good idea, this method Ff,chrome intend to use for a long time, but people are so judged, It's a farce under Chrome.

  typeof document.all//undefined
  document.all//htmlallcollection [728] (728 for total elements)

In determining Undefined, NULL, string, number, Boolean, function these six are also simple, the first two can be compared with void (0), null comparison, the following 4 typeof can also meet 90% of the case. This is because string, number, and Boolean can be wrapped into pseudo objects.

  typeof new Boolean (1); => "Object"
  typeof new Number (1),//=> "Object"
  typeof new String ("AA");//=> "Object"

These are not the most difficult, the difficulty lies in the RegExp and array. There are few cases in which regexp are judged. Array is not the same. There are no fewer than 20 implementations of the IsArray. It's all because the duck-type syndrome is broken. Until prototype.js to dig out the Object.prototype.toString. This method is directly output internal [[Class]], absolutely accurate. With it, 95% of the traps were skipped.

  function IsArray (arr) {return arr instanceof Array; function IsArray (arr) {return!!
  Arr && Arr.constructor = = Array; function IsArray (arr) {//prototype.js 1.6 return arr!= null && typeof arr = = "Object" && ' Spli
  Ce ' in arr && ' join ' in arr; function IsArray (arr) {//Douglas Crockford (JSON author, against prototype pollution) return typeof Arr.sort = "function"} function is
    Array (array) {//KRISZYP var result = false;  try{New Array.constructor (Math.pow (2,32))} catch (e) {result =/array/.test (e.message)} return
  Result

  };
      function IsArray (o) {//kangax try{Array.prototype.toString.call (o);
    return true;
  catch (e) {} return false; function IsArray (o) {//kangax if (o && typeof o = = ' object ' && typeof o.length = ' number ' &AMP;&A mp
      Isfinite (O.length)) {var _origlength = o.length;
      O[o.length] = ' __test__ '; var _newlength= O.length;
      O.length = _origlength;
    return _newlength = = _origlength + 1;

 return False}

As for null, undefined, Nan is written directly

  function isNaN (obj) {return
    obj!== obj
  }

  function IsNull (obj) {return
    obj = = null;
  }

  function isundefined (obj) {return
    obj = = void 0;
  }

The last thing to decide is that the object is window, because the ECMA is a nonstandard host object and the Window object belongs to the host. So there is no agreement. Even Object.prototype.toString can't do anything about it.

  [Object Object] IE6 
  [Object] IE7 
  [Object Object] IE8 [Object
  window] IE9
  [Object window] ff3.6
  [Object Window] opera10
  [object Domwindow] Safari 4.04
  [Object Global] chrome5.0

However, according to Window.window and Window.setinterval to determine more unreliable, with a skill can be perfect to identify the IE6 IE7 IE8 window objects, other also with ToString, this magical hack is, window and document compare each other, if the order is not the same, the result is not the same!

The rest is a few classic methods:

In Prototype.js, you have iselement,isarray,ishash,isfunctoion,isstring,isnumber,isdate,isundefined.

MooTools has a typeof decision base type, instanceof determines the custom "class"

Rightjs are isfunction, Ishash, isstring, Isnumber, IsArray, Iselement, Isnode.

Ext has a more comprehensive judgment, isempty,isarray,isdate,isobject,issimpleobject,isprimitive,isfunction,isnumber,ismumeric,isstring, Isboolean,iselement,istextnode,isdefined,isiterable, everything. Finally, there are typeof to judge the basic type.

Underscore.js has Iselement,isempty,isarray,isargument,isobject,isfunction,isstring,isnumber,isfinite,isnan, isboolean,isdate,isregexp,isnull,isundefined.

jquery is not the same as other frameworks, only isfunction,isarray,isplainobject,isemptyobject in jQuery1.4. Isfunction,isarray users must use more, Isplainobject is used to determine whether it is pure JS object. It is not a Dom,bom object, nor is it a custom instance object of a "class," which was originally created for deep copying, bypassing objects like window's own reference. Isemptyobject is a system for data caching that can be deleted when the object is empty.

  The judgment idea of jQuery2.0 pure array
  jquery.isplainobject = function (obj) {
    //first excludes types that have the underlying type not object, then the DOM node and the Window object
    if (Jquery.type (obj)!== "Object" | | object.nodetype | | jquery.iswindow (obj)) {return
      false;
    }

    Then backtrack to see if its nearest prototype object has isprototypeof.
    Older versions of IE some native objects were not exposed to constructor, prototype. So here we filter out
    try{
      if (obj.constructor &&!hasown.call (Obj.constructor.prototype, "isprototypeof")) { return
        false;
      }
    } Case (e) {return
      false;
    }
    return true;
  }

Avalon.mobile has a more streamlined version that only supports the latest browsers and can boldly use the ECMA262V5 new API

  Avalon.isplainobject = function (obj) {return
    obj && typeof obj = = "Object" && object.getprototypeof (obj) = = Object.prototype
  }

Isarraylike is also a common method, but it is too difficult to determine an array of classes, and the only way to identify it is to have a length attribute greater than 0 or equal to 0, and there are some common understandings, such as window and function and element nodes, such as (form element), not counting class arrays, Although they all meet the preceding conditions. As a result, jquery has not yet exposed it.

//jquery2.0 function Isarraylike (obj) {var length = obj.length, type = Jquery.typ
    E (obj);
    if (Jquery.iswindow (obj)) {return false; } if (Obj.nodetype = = 1 && length) {return true} return type = = "Array" | |  Type!== "function" && (length = = 0 | | typeof length = = "Number" && length > 0 && (length-1)
  in obj);
      }//Avalonjs function Isarraylike (obj) {if (obj && typeof obj = = "Object") {var n = obj.length if (+n = = n &&! (n% 1) && N >= 0) {//detect if length is a non-negative integer try{if ({}.prototypeise Numerable.call (obj, ' length ') = = False) {return Array.isarray (obj) | |/^\s?function/.test (Obj.item | | obj.callee
       )} return true;
       catch (e) {//ie nodelist directly error return true; }} return false} 

The results of the research in the prototype.js1.3 version (Object.prototype.toString.call) are applied to jquery, and in jQuery1.2 it is very complicated to judge whether a variable is a function.

Copy Code code as follows:

Isfunction:function (FN) {
Return!! fn && typeof fn!= "string" &&!fn.nodename && fn.constructor!= Array &&/^[\s[]?function /.test (fn + "");
}

In jQuery1.43, IsWindow is introduced to deal with the decision of window in Makearray, and the isNaN is introduced to ensure the security of style assignment. It also introduces a type instead of the TypeOf keyword, which is used to get the basic type of basic data.

  Class2type = {};
  Jquery.each ("Boolean number String Function Array Date regexpobject". Split (""), Function (i, name) {
    class2type["[ob Ject "+ name +"] "= Name.tolowercase ();
  });

  Jquery.type = function (obj) {return
    obj = null? String (obj): Class2type[tostring.call (obj)] | | "Object";
  }

Add IsNumeric instead of isNaN in jQuery1.7. This is a different isnumber than the other frame, it can be a string, as long as the appearance of a number on it. But jQuery1.7 also made a violation of the previous mentioned stability of things. The Jquery.isnan is removed, so a large number of plug-ins are invalidated based on the old version of jquery.

  jquery.1.43-1.64
  Jquery.isnan = function (obj) {return
    obj = = NULL | |!rdigit.test (obj) | | isNaN (obj); 
   }

  //jquery1.7 is isnan to reverse version
  Jquery.isnumeric = function (obj) {return
    obj!= null && rdigit.test (ob j) &&!isnan (obj);

  jquery.1.71-1.72
  jquery.isnumeric = function (obj) {return
    !isnan (parsefloat (obj)) && isfinite ( obj);
  }

  jQuery2.1
  jquery.ismumeric = function (obj) {return
    obj-parsefloat (obj) >= 0;
  }

Massfarmework's ideas are consistent with jquery, minimizing the code for the Isxxx series, and integrating the is Window,isnan,nodename methods. The code is longer, you can get the type, or you can pass in the second parameter for type comparison.

  var class2type = {"[Objecthtmldocument]": "Document", "[Objecthtmlcollection]": "NodeList", "[Objectstat Icnodelist] ":" NodeList "," [Objectixmldomnodelist] ":" NodeList "," [Objectdomwindow] ":" Window "," [Object gl
  Obal] ": Window", "null": "null", "undefined": "Undefined"}, toString = class2type.tostring; "Boolean,number,string,function,array,date,regexp,window,document,arguments,nodelist". Replace ($.rword,function (name)
  {class2type["[object" + name + "]"] = name;

  }); Class2type This mapping almost catch the commonly used decision objects Mass.type = function (obj, str) {var result = class2type[(obj = null | | obj!== o BJ)? Obj:toString.call (obj)] | | Obj.nodename | |
    "#"; if (Result.charat (0) = = "#") {//compatible with the old browser's individual case, such as Window.opera//using IE678 window = = Document for true,document = = = Window F  Alse if (obj = = obj.document && obj.document!= obj) {result = "window";//Return constructor name} else if (Obj.nodetype = = 9) {Result = "Document";
      else if (obj.callee) {result = ' Arguments '; else if (isfinite (obj.length) && obj.item) {result = ' nodelist '//Process node collection} else {Resul
      t = Tostring.call (obj). Slice (8,-1);
    } if (str) {result str = = result;
  return result;

 }

The type method is then very relaxed, using Tosring.call (obj) to derive the left key of the value and get it directly from the map. IE678, we only cost some trouble to deal with Window,document,argument,nodelist and other objects.

Baidu's Tangram is based on pragmatism and is judged very rigorously. Like ext, can think of write on, and judge very rigorous.

Current version 2.0.2.5 Http://tangram.baidu.com/api ()

  Baidu.isdate = function (unknow) {return
  baidu.type (unknow) = = "Date" && unknow.tostring ()!= ' Invalid date ' &&!isnan (unknow);
  };
  Baidu.isnumber = function (unknow) {return
  baidu.type (unknow) = = "Number" && isfinite (unknow);
  

5. Introduction mechanism of the mainstream framework-domready

Domready is actually a nickname for the "domcontentloaded" event, but because of the framework, it is a little different from the real domcontentloaded, in many new and old books, Many people write it in the window.onload callback to prevent the DOM tree from starting to operate on the node. For frameworks, the sooner the DOM gets involved, the better it will be, such as feature detection. Domready can also meet the user in advance binding event requirements, because sometimes the page picture too much, window.onload event delay can not trigger, then the user operation has no effect, therefore, the mainstream framework has introduced the Domready mechanism, and a lot of trouble to be compatible with all browsers. The specific strategies are as follows:

Use domcontentloaded event for support domcontentloaded events
Older versions of IE use Diego Perini to discover the famous hack

  by Diego Perini 2007.10.5
   function iecontentloaded (W, fn) {
   var d = w.document, done = False,
   //Only once user's callback Functions init ()
   init = function () {
    if (!done) {Done
      = true;
      fn ();
    }
   ;
  (function () {
    try {
      //DOM tree is not finished before calling DoScroll throws an error
      D.documentelement.doscroll (' left ');
    } catch (E {
      //delay to try again ~
      settimeout (Arguments.callee);
      return;
    }
    No error means that the DOM tree is created and then immediately executes the user callback
    init ();
         } ();
  Monitor document loading state
  D.onreadystatechange = function () {
    //If the user is a function that is bound after Domready, immediately executes the
    if ( D.readystate = = ' complete ') {
      d.onreadystatechange = null;
      Init ();}}
       ;
  

In addition, IE can also be judged by script defer hack

   document.write ("<script id=__ie_onload defer src=//0 mce_src=http://0></scr" + "ipt>"); 
    Script = document.getElementById ("__ie_onload"); 
    Script.onreadystatechange = function () {//ie Even a dead chain can trigger an event if 
          (this.readystate = = "complete") 
    init (); Specifies that the script for the defer is not triggered until the DOM tree is completed
  ; 

But there's a problem, if our seed module is dynamically loaded, is the DOM tree finished when it inserts into the DOM tree? How does this trigger the ready callback? jquery's solution is, even the onload also listens, but if even the onload did not catch up, Judge Document.readystate equals complete. (perfect) Unfortunately ff3.6 did not have this attribute before, see mass scheme

  var readylist = [];
    Mess.ready = function (fn) {if (readylist) {Fn.push (FN);
    else {fn (); } var readyfn, ready = the consortium?
  "domcontentloaded": "ReadyStateChange";
    function FireReady () {for (var i = 0, fn; fn = readylist[i++]) {fn ();
    } readylist = null; FireReady = $.noop; An inert function that prevents IE9 from calling _checkdeps} function Doscrollcheck () {try {//ie) to detect whether the DOM tree is finished after Doscrollcheck ("L
      EFT ");
    FireReady ();
    catch (E) {settimeout (Doscrollcheck); There is no ReadyState property if (!document.readystate) {var readyState = Document.readystate = Document.body before//ff3.6.
    Complete ":" Loading "; if (document.readystate = = "complete") {FireReady ();//If loading} else {$.bind outside Domreay (document,ready,rea
          DYFN = function () {if (The Consortium | | document.readystate = = "complete") {FireReady (); if (readyState) {//ie cannot be document.readystate document.readystate = "Complete";
      }
        }
      }); 
          if (Html.doscroll) {try {///If the error is self.eval across domains, it is shown that there are two windows if (= = = Parent.eval) {Doscrollcheck ();
        The catch (e) {Doscrollcheck ();

 }
      }
    }

  }

6. Conflict-Free processing

No conflict processing is also called docusate coexistence, $ is this important function name, so that everyone likes to take it to do their own namespaces, when jquery began to develop, prototype is the mainstream, jquery invented the Noconflict function

 var window = This, undefined, _jquery = window.jquery, _$ = window.$,//Change window to the same name in the closure
  volume, convenient internal function call Windows don't bother to find it. _jquery and _$ for later rewriting jquery = Window.jquery = window.$ = function (selector, context) {//For returning a jquery object return new
  JQuery.fn.init (Selector,context); } jquery.extend ({noconflict:function (deep) {//After the introduction of the JQuery class library, the window.$ and window.jquery outside the closure are stored with a function//it is To generate the jquery object or execute the functions inside after Domready//review the topmost code, _jquery and _$ have been assigned to them when the function has not been assigned to them, so the value of both of them must be undefined//Therefore, this abandonment control
      The technology of power is very simple, is to use undefined to remove the jquery function inside the window.$. At this time, prototype or MooTools's $ was taken away window.$ = _$;
      The equivalent of window.$ = undefined, if you have a library called jquery, can also be generous transition out. At this point, you need to add a Boolean value to the Noconflict, true if (deep)//But we have to use a thing to accept jquery with the Jquey entry function//closure inside the thing unless it is referenced by a host such as window, otherwise it is Invisible//Therefore, we will return the jQuery in the closure, outside with a variable acceptance can window.jquery = _jquery;
    Quite window.jquery = undefined return jQuery; }
  })

When used, first introduce someone else's library, and then introduce jquery, using the call $.noconflict () to rename, so that it does not affect the other person's $ run.

Mass's way of doing this is to define a Nick attribute on the script label, and the freed namespace is your attribute value. It implements a jquery-like mechanism.

<script nike= "AAA" src= "mass.js" ></script>
<script>
  aaa.log ("xxxxxx")
</script >

The above mentioned is the entire content of this article, I hope you can enjoy.

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.