Javascript Framework Design-seed module _ javascript skills

Source: Internet
Author: User
Tags mootools
This article introduces the content of the second chapter of the seed module of the javascript Framework Design of situ zhengmei. It is a small post-reading experience. You can refer to it. The seed module is also called the core module, which is the first execution part in the framework. Even a single file function library like jQuery has many internal modules. Some modules must be executed immediately at the beginning of execution, and some modules must be executed only when used. Some modules are dispensable and have a weak sense of presence. They only run in a specific browser.

The seed module is one of the pioneers. Its methods do not necessarily require complete functions and excellent design, but must be highly scalable, commonly used, and stable.

Scalability means that they can include other modules. Common means that most modules can use them to prevent repetitive work. Stability means that it is not easily replaced by new methods during version iterations.

With reference to the implementation of many frameworks and libraries, we believe that the seed module includes the following functions: object extension, array, type determination, simple binding and uninstallation, and no conflict processing, module loading and domReady. this chapter takes the mass Framework seed module as a template.

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

1. namespace

As the initial part of a framework, the seed module is responsible for assisting with the establishment of the local infrastructure. JQuery has a good start, using IIFE (call the function expression immediately ).

LIFE is the most important infrastructure in modern javascript frameworks. It wraps itself like cells to prevent variable contamination. Like a foothold, This is a namespace, such as prototype. js and mootools, they make you feel the existence of the framework. They are meaningful to every corner of the javascript, DOM, BOM, and other entire execution environment, and can be expanded to the native object prototype. Thanks to Douglas (author of JSON)'s strong opposition, the new frameworks are all built on namespaces.

We can see how to simulate a namespace on javascript. Javascript is based on objects, but only objects of the type can meet the requirements, such as function, RegExp, and Object. However, the most common types are objects and functions. We add an attribute to an object, which is an object. We can add an object to this object. In this way, we can establish our framework in an orderly manner. The user wants to call a method in the form of xxx. yyy. zzz.

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

Looking at the implementation of various major libraries, a global variable is basically defined as a namespace at the beginning, and then extended, such as Base2 Base, Ext, jQuery's jQuery, YUI's YUI, dojo's dojo, and mochKit of MochiKit. There are two types of global variables:

Prototype. js, mootools, and Base2 are classified as one type. Prototype is a philosophical extension of javascript native objects. Earlier in the year, prototype was almost called a de facto standard. Therefore, the coexistence with other databases is not considered. The basic Prototype has also developed very good class libraries such as script. aculo. us, rico, Plotr, protoChart, and Script2. In addition, almost all plug-ins of some origins are related to Prototype, such as lightBox. Mootools is an upgraded version of prototype. js. It is more OO and fully copies its APIs. Base2 wants to fix the bug of IE so that IE has a standard browser API, so it also contaminated all native objects.

The second type is jQuery, YUI, and EXT frameworks. YUI and Ext are built using nested object methods. JQuery is a different method. It is selector-oriented. Therefore, its namespace is a function that allows you to easily transmit the string of the css interpreter. Then you can use the selector to find the object and return a jQuery object instance.

JQuery initially uses $ as its namespace like Prototype. Therefore, it implements the coexistence mechanism of many libraries. In the process of switching between $ and jQuery, the coexistence principle of multiple libraries in jQuery is very simple, therefore, it has become a standard for many small libraries. First, save the namespace to a temporary variable (note that this object is not in its own framework at this time, it may be prototype. js or other), and then use noConflict to put it back.

// JQuery1.2 var _ jQuery = window. jQury, _ $ = window. $; // Save the variable with the same name as 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

However, jQuery's noConflict is only useful for the single-file class library framework, and cannot be copied like Ext. Therefore, after renaming the namespace, set Ext to null, and then introduce it to a new javascript file through the dynamic loading method. This file will be called with Ext, leading to an error.

2. object extension

We need a mechanism to add new features to our namespace. This method is generally called extend or mixin in javascript. Before Property Descriptor is born, javascript objects can be freely added, changed, and deleted. Therefore, it is very convenient to expand an object. A simple extension method is implemented in this way.

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

However, the old version of IE has a problem here. It thinks that the prototype method like the Object should not be traversed, so the for in loop cannot traverse the attribute names of valueOf and toString. As a result, this problem is also encountered when simulating the Object. keys method.

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

In different frameworks, this method has different implementations. For example, Ext is the apply and applyIf methods. The former will overwrite the attributes of the same name of the target object, while the latter will not. Dojo allows multiple objects to be merged. JQuery also supports deep copy. The following is the mix method of mass Farmework. Supports merging and selecting whether to overwrite multiple objects.

Function mix (target, source) {// if the last parameter is Boolean, determine whether to overwrite the Same Name attribute var args = []. slice. call (arguments), I = 1, key, ride = typeof args [args. length-1] = "boolean "? Args. pop (): true; if (args. length = 1) {// process $. mix (hash) target =! This. window? This: {}; I = 0 ;}while (source = args [I ++]) {for (key in source) {// allows mixing of objects, the object if (ride |! (Key in target) {target [key] = source [key] ;}} return target ;}

3. array-based

There are many class array objects in the browser, such as arguments in the function. forms, form. elements, document. links, select. options, document. getElementsByName, document. the combination of nodes (HTMLCollection, NodeList) obtained by getElementsByTagName, childNodes, and children, or custom objects in some special ways.

The class array object is a good storage structure. However, the function is too weak. In order to use the convenient methods of pure arrays, We will convert them before processing them.

Generally, []. slice. call can be converted, but the function is not enough. However, the old versions of HTMLCollection and NodeList are not subclasses of objects. Using the above method will cause IE execution exceptions. 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;  }

The implementation of mass is differentiated from each other at the beginning, directly []. slice. call. If IE is used, implement a slice method by yourself.

  $.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 Determination

Javascript has two types of systems. One is the basic data type and the other is the object type system. The basic data types include 6. They are undefined, string, null, boolean, function, and object. The basic data type is detected by typeof. The object type system is based on the basic type system and detected through instanceof. However, the two identification mechanisms provided by javascript are very unreliable, so they gave birth to the isXXX series. Taking typeof as an example, it can only roughly identify the string, number, boolearn, function, undefined, and object data types. It cannot identify the types of subdivided objects such as null and RegExpArgument.

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 // return "function" typeof window in the browser that implements ecma262v4. 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 ") //

In addition, people used to document. it is dangerous to determine whether it is ie, because the document. it is a good note to get all the elements on the page. This method FF and chrome are intended to be used for a long time. However, people have made such judgments that there is such a farce under chrome.

Typeof document. all // undefined document. all // HTMLAllCollection [728] (728 indicates the total number of elements)

It is easy to determine undefined, null, string, number, boolean, and function. The first two can be compared with void (0) and null respectively, the four typeof values can also meet the requirements of 90%. This is because string, number, and boolean can be packaged as pseudo objects.

  typeof new Boolean(1); //=>"object"  typeof new Number(1); //=>"object"  typeof new String("aa"); //=> "object"

These are not the most difficult problems. The difficulty is that RegExp and Array. are rarely used to determine RegExp. Array is different. There are no more than 20 isArray implementations. It is because the duck pattern is broken. Until prototype. js discovers Object. prototype. toString. This method directly outputs the internal [Class], which is absolutely accurate. With it, 95% of traps are 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" & 'splice 'in arr & 'join' in arr;} function isArray (arr) {// Douglas Crockford (JSON author, against prototype pollution) return typeof arr. sort = "function"} function isArray (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' & isFinite (o. length) {var _ origLength = o. length; o [o. length] = '_ test _'; var _ newLength = o. length; o. length = _ origLength; return _ newLength = _ origLength + 1;} return false}

Write null, undefined, and NaN directly.

  function isNaN(obj) {    return obj !== obj  }  function isNull(obj) {    return obj === null;  }  function isUndefined(obj){    return obj === void 0;  }

Finally, we need to determine that the object is window. Because ECMA is an nonstandard Host object, the window object belongs to host. Therefore, it is not agreed. Even if Object. prototype. toString is not supported

  [object Object] IE6   [object 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 is more unreliable. You can use a technique to perfectly identify the window object of ie6 ie7 ie8. Others also use toString. The magic hack is that window and document are compared with each other, if the order is different, the results are also different!

The rest are some classic methods:

Prototype. js contains isElement, isArray, isHash, isFunctoion, isString, isNumber, isDate, and isUndefined.

Mootools has a typeOf to determine the basic type, and instanceOf to determine the custom "class"

RightJS has isFunction, isHash, isString, isNumber, isArray, isElement, and isNode.

Ext has comprehensive judgment, including isEmpty, isArray, isDate, isObject, isSimpleObject, isPrimitive, isFunction, isNumber, isMumeric, isString, isBoolean, isElement, isTextNode, isDefined, and isIterable. Finally, there is typeOf to determine the basic type.

Underscore. js includes isElement, isEmpty, isArray, isArgument, isObject, isFunction, isString, isNumber, isFinite, isNaN, isBoolean, isDate, isRegExp, isNull, isUndefined.

JQuery is not the same as other frameworks. In jQuery1.4, only isFunction, isArray, isPlainObject, and isEmptyObject are supported. IsFunction and isArray are usually used by users. isPlainObject is used to determine whether it is a pure js object. It is neither a DOM, BOM object, nor a custom "class" instance object. The purpose of making it is initially to make a deep copy and avoid referencing its own object like a window. IsEmptyObject is a data cache system. When this object is empty, it can be deleted.

// Determine the idea of jQuery2.0 pure array jQuery. isPlainObject = function (obj) {// first, exclude the basic type not the Object type, and then the DOM node and window Object if (jQuery. type (obj )! = "Object" | object. nodeType | jQuery. isWindow (obj) {return false;} // then trace back to whether the latest prototype object has isPrototypeOf. // some native objects of earlier IE versions do not expose constructor and prototype. So here we filter out try {if (obj. constructor &&! HasOwn. call (obj. constructor. prototype, "isPrototypeOf") {return false ;}} case (e) {return false ;}return true ;}

There is a more simplified version of aveon. mobile that only supports the latest browsers. You can boldly use the new API ecma262v5.

  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. The only way to identify isArrayLike is to determine a length attribute greater than 0 or equal to 0. In addition, there are some consensus that, such as window and functions and element nodes, such as (form element), are not class arrays, although they all meet the preceding conditions. Therefore, jQuery has not exposed it so far.

// JQuery2.0 function isArrayLike (obj) {var length = obj. length, type = jQuery. type (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) {// check whether length is a non-negative integer try {if ({}. prototypeIsEnumerable. call (obj, 'length') === false) {return Array. isArray (obj) |/^ \ s? Function/. test (obj. item | obj. callee)} return true;} return true when NodeList of catch (e) {// IE;} return false}

The research results (Object. Prototype. toString. call) in prototype. js1.3 are applied to jQuery. In jQuery1.2, It is very complicated to judge whether a variable is a function.

The Code is as follows:


IsFunction: function (fn ){
Return !! Fn & typeof fn! = "String "&&! Fn. nodeName & fn. constructor! = Array & amp;/^ [\ s []? Function/. test (fn + "");
}


IsWindow is introduced in jQuery1.43 to process the window judgment in makeArray, and isNaN is introduced to ensure the security of style assignment. At the same time, type is introduced to replace the typeof keyword to obtain the basic type of basic data.

  class2type = {};  jQuery.each("Boolean Number String Function Array Date RegExpObject".split(" "),function( i , name ){    class2type[ "[object " + name + "]" ] = name.toLowerCase();  });  jQuery.type = function(obj){    return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";  }

In jQuery1.7, add isNumeric to replace isNaN. This is an isNumber different from other frameworks. It can be a string, as long as it looks like a number. However, jQuery1.7 does something against the stability mentioned earlier. Suddenly remove jQuery. isNaN. Therefore, a large number of plug-ins based on the old version of jQuery are invalid.

// JQuery.1.43-1.64 jQuery. isNaN = function (obj) {return obj = null |! Rdigit. test (obj) | isNaN (obj);} // jQuery1.7 is the de-reversed jQuery. isNumeric = function (obj) {return obj! = Null & rdigit. test (obj )&&! 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 ;}

The idea of massFarmeWork is consistent with that of jQuery. We try to reduce the isXXX series of code and integrate the is Window, isNaN, nodeName and other methods. The code is long. You can obtain the type or pass in the second parameter for type comparison.

Var class2type = {"[objectHTMLDocument]": "Document", "[objectHTMLCollection]": "NodeList", "[objectStaticNodeList]": "NodeList", "[objectIXMLDOMNodeList]": "NodeList", "[objectDOMWindow]": "window", "[object global]": "window", "Null": "Null", "undefined ": "undefined"}, toString = class2type. toString; "Boolean, Number, String, Function, Array, Date, RegExp, Window, Document, Arguments, NodeList ". r Eplace ($. rword, function (name) {class2type ["[object" + name + "]"] = name;}); // class2type ing almost overwrites frequently-used judgment objects by mass. type = function (obj, str) {var result = class2type [(obj = null | obj! = Obj )? Obj: toString. call (obj)] | obj. nodeName | "#"; if (result. charAt (0) = "#") {// compatible with the previous version of the browser, such as window. opera // use IE678 window === document to be true, document === window to be false if (obj = obj.doc ument & obj.doc ument! = Obj) {result = "window"; // returns the constructor name} else if (obj. nodeType = 9) {result = "Document";} else if (obj. callee) {result = "Arguments";} else if (isFinite (obj. length) & obj. item) {result = "NodeList" // processing node set} else {result = toString. call (obj ). slice (8,-1) ;}} if (str) {result str === result;} return result ;}

Then the type method is very easy. You can use toSring. call (obj) to get the left button of the value and directly obtain it from the ing. IE678, we only need to handle objects such as window, document, argument, and nodeList on a weekly basis.

Baidu's clever board is based on pragmatism and Its Determination is rigorous. Like EXT, you can think of writing, and the judgment is very rigorous.

Current 2.0.2.5 http://tangram.baidu.com/api#baidu.type ()

  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 mainstream frameworks-domReady

DomReady is an alias for the "DOMContentLoaded" event. However, due to the framework's needs, it is a little different from the real DOMContentLoaded. In many new books and old books, many people write it in windows. the onload callback prevents operations on nodes before the dom tree is created. For the framework, the sooner the dom is involved, the better, for example, feature detection. Domready can also meet the user's need to bind events in advance, because sometimes there are too many page images, such as window. the onload event cannot be triggered, so user operations are ineffective. Therefore, mainstream frameworks have introduced the domReady mechanism, and it takes a lot of effort to be compatible with all browsers. The specific policy is as follows:

Use DOMContentLoaded events for DOMcontentLoaded events
The famous Hack found in earlier version IE using Diego perini

// By Diego Perini 2007.10.5 function IEContentLoaded (w, fn) {var d = Doc ument, done = false, // only execute the user's callback function init () once () init = function () {if (! Done) {done = true; fn () ;}; (function () {try {// call doScroll before DOM tree creation will throw the error d.doc umentElement. doScroll ('left');} catch (e) {// try again later ~ SetTimeout (arguments. callee, 50); return;} // No error indicates that the DOM tree has been created, and the user callback init () ;}) () is immediately executed ();})(); // listen to the loading status of document d. onreadystatechange = function () {// if the user is a function bound after domReady, immediately execute if (d. readyState = 'complete') {d. onreadystatechange = null; init ();}};}

In addition, IE can also be determined through script defer hack.

Document. write ("

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.