javascript框架設計之種子模塊
種子模塊也叫核心模塊,是框架中最先執(zhí)行的部分。即便像jQuery那樣的單文件函數庫,它的內部也分很多模塊,必然有一些模塊執(zhí)行時在最前面立即執(zhí)行,有一些模塊只有用到才執(zhí)行。有的模塊可有可無,存在感比較弱,只有在特定的瀏覽器下才運行。
種子模塊就是其中的先鋒,它里邊的方法不一定要求個個功能齊全,設計優(yōu)良,但一定要極具擴展性,常用,穩(wěn)定。
擴展性是指通過他們能給將其它模塊包含進來;常用是指絕大多數的模塊都能用到它們,防止做重復工作。穩(wěn)定是指在版本迭代時不輕易被新方法替代。
參照許多框架和庫的實現,我們認為種子模塊包含如下功能:對象擴展,數組化,類型判定,簡單的綁定與卸載,無沖突處理,模塊的加載與domReady.本章學習的內容以mass Framework種子模塊為范本。
https://github.com/RubyLouvre/mass-Framework
1.命名空間
種子模塊作為一個框架最開始的部分,負責輔建全局的基礎設施外。jQuery就有一個很好的開頭,使用IIFE(立即調用函數表達式).
LIFE是現代javascript框架里最主要的基礎設施,它就像細胞一樣包裹自身,防止變量污染。就像一個立足點,這個就是命名空間,如prototype.js,mootools,它們讓你感受不到框架的存在,它的意義深刻到javascript、DOM、BOM等整個執(zhí)行環(huán)境的每個角落,對原生的對象原型就行擴展。由于道格拉斯(JSON作者)的極力反對,新的框架都在命名空間上構建了。
我們看怎么在javascript上模擬命名空間。javascript一切基于對象,但只有符合類型的對象才符合要求,比如function 、RegExp、Object,不過最常用的是object和function。我們往一個對象上添加一個屬性,而這個屬性又是一個對象,這個對象我們又可以為它添加一個對象,通過這種方法,我們就可以有條不紊的構建我們的框架。用戶想調用某個方法,就以xxx.yyy.zzz()的形式調用。
if( typeof(Ten) === "undefined" ){
Ten = {};
Ten.Function = { /*略*/ }
Ten.Array = { /*略*/ }
Ten.Class = { /*略*/ }
Ten.JSONP = new Ten.Class(/*略*/ )
Ten.XHR = new Ten.Class(/*略*/ )
}
縱觀各大類庫的實現,一開始基本都是定義一個全局變量作為命名空間,然后對它進行擴展,如Base2的Base,Ext的Ext,jQuery的jQuery,YUI的YUI,dojo的dojo,MochiKit的mochKit。從全局變量的污染程度來看,分為兩類:
prototype.js和mootools與Base2歸為一類,Prototype的哲學是對javascript的原生對象進行擴展。早些年,prototype差點稱為事實的標準。因此沒有考慮到與其它庫共存的問題?;綪rototype,也發(fā)展出諸如script.aculo.us,rico,Plotr,protoChart,Script2等非常優(yōu)秀的類庫以一大類收費插件。而且,有些淵源的插件幾乎都與Prototype有關,比如lightBox。mootools是prototype.js的升級版,更加OO,全面復制其API。Base2則是想修復IE的bug,讓IE擁有標準瀏覽器的API,因此也把所有原生的對象污染一遍。
第二類是jQuery,YUI,EXT這些框架,YUI和Ext就是對象嵌套對象的方式構建的。jQuery則另辟蹊徑,它是以選擇器為導向的,因此它的命名空間是一個函數,方便用戶將css表達器的字符串傳進來。然后通過選擇器進行查找,最后返回一個jQuery對象實例。
jQuery最開始也像Prototype使用$作為它的命名空間,因此,它實現了很多庫的共存機制,在$和jQuery中任意切換,jQuery的多庫共存原理很簡單,因此后來也成為許多小庫的標配。首先,把命名空間保存到一個臨時變量中(注意,這時候這個對象并不是自己框架的東西,可能是prototype.js或者其他的),然后再使用個noConflict放回去。
//jQuery1.2
var _jQuery = window.jQury , _$ = window.$; //把可能存在同名變量先保存起來
jQury.extend({
noConflict : function(deep) {
window.$ = _$; //這時再放回去
if (deep) //補充 if ( deep && window.jQuery === jQuery )
window.jQury = _jQuery;
return jQury;
}
})
參考:http://zhidao.baidu.com/question/1239712776390687219.html
但jQuery的noConflict只是對單文件的類庫框架有用,像Ext就不能復制了。因此把命名空間改名后,將Ext置為null,然后通過動態(tài)加載的方法引入新的javascript文件中,該文件會以Ext調用,會導致報錯。
2.對象擴展
我們需要一種機制,將新功能添加到我們的命名空間上來。這方法在javascript通常稱作extend或mixin。javascript對象在屬性描述符(Property Descriptor)沒有誕生之前,是可以隨意添加、更改、刪除其成員的,因此,擴展一個對象非常便捷。一個簡單的擴展方法實現是這樣的。
function extend (destination,source){
for (var property in source)
destination[property] = source[property];
return destination;
}
不過,舊版本IE在這里有個問題,它認為像Object的原型方法就是不應該被遍歷出來,因此for in循環(huán)是無法遍歷valueOf、toString的屬性名。這導致,模擬Object.keys方法是現實時也遇到了這個問題。
Object.keys = Object.keys || function(obj){
var a = [];
for(a[a.length] in obj);
return a;
}
在不同的框架,這個方法還有不同的實現,如Ext分別為apply與applyIf兩個方法,前者會覆蓋目標對象的同名屬性,而后者不會。dojo允許多個對象合并在一起。jQuery還支持深拷貝。下面是mass Farmework的mix方法。支持多對象合并與選擇是否覆寫。
function mix(target,source){ //如果最后參數是布爾,判定是否覆蓋同名屬性
var args = [].slice.call(arguments), i = 1, key,
ride = typeof args[args.length - 1] == "boolean" ? args.pop() : true;
if (args.length === 1){ //處理$.mix(hash)的情形
target = !this.window ? this : {};
i = 0;
}
while ((source = args[i++])) {
for (key in source){ //允許對象糅雜,用戶保證都是對象
if (ride || !(key in target)) {
target[key] = source[key];
}
}
}
return target;
}
3.數組化
瀏覽器下存在很多類數組對象,如function內的arguments,通過document.forms、form.elements,document.links、select.options、document.getElementsByName,document.getElementsByTagName、childNodes、children等方式獲取的節(jié)點的結合(HTMLCollection 、NodeList)或按照某些特殊的寫法自定義對象。
類數組對象是一個很好的存儲結構。不過功能太弱了,為了能使用純數組的那些便捷的方法,我們會在處理它們前都會做一下轉換。
通常來說,使用[].slice.call就能轉換了 ,不過功能不夠用,但在舊版本的HTMLCollection、NodeList不是Object的子類,采用如上的方法會導致IE執(zhí)行異常。我們看一下
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的實現,一開始就進行區(qū)分,直接[].slice.call,IE的話自己動手實現一個slice方法
$.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.類型的判定
javascript存在兩套類型系統(tǒng),一套是基本的數據類型,另一套是對象類型系統(tǒng)?;緮祿愋桶?中 。分別是undefined、string、null、boolean、function、object。基本數據類型是通過typeof來檢測的。對象類型系統(tǒng)是以基礎類型系統(tǒng)為基礎的,通過instanceof來檢測的。然而,javascript自帶的這兩套識別機制非常不靠譜,于是就催生了isXXX系列。就拿typeof來說,它只能粗略識別出string、number、boolearn、function、undefined、object這6種數據類型,無法識別null,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 //在實現了ecma262v4的瀏覽器返回"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
另外,以前人們總是以document.all來判斷是否為ie,這其實是很危險的,因為,用document.all來取得頁面中所有的元素是不錯的注意,這個方法FF,chrome打算使用很久了,不過人們都這樣判斷,就是在chrome下有這樣的鬧劇。
typeof document.all //undefined document.all //HTMLAllCollection [728] (728為元素總數)
在判定undefined、null、string、number、boolean、function這六個還算簡單,前面兩個可以分別與void(0)、null比較,后面4個的typeof也可以滿足90%的情形。這樣說是因為string、number、boolean可以包裝成偽對象。
typeof new Boolean(1); //=>"object"
typeof new Number(1); //=>"object"
typeof new String("aa"); //=> "object"
這些還不是最難的,難點在于RegExp與Array.判定RegExp的情況很少。Array則不一樣。有關isArray的實現不下二十種。都是因為鴨式辨型被打破了。直到prototype.js把Object.prototype.toString發(fā)掘出來。此方法是直接輸出內部的[[Class]],絕對精準。有了它,95%的陷阱被跳過了。
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作者,反對原型污染)
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
}
至于null 、 undefined 、NaN直接這樣寫
function isNaN(obj) {
return obj !== obj
}
function isNull(obj) {
return obj === null;
}
function isUndefined(obj){
return obj === void 0;
}
最后要判定的是對象是window,由于ECMA是不規(guī)范的Host對象,window對象屬于host.所以也就沒有被約定。就算Object.prototype.toString也對它沒辦法
[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
不過根據window.window和window.setInterval去判定更加不靠譜,用一個技巧就可以完美識別ie6 ie7 ie8的window對象,其它還用toString,這個神奇的hack就是,window與document互相比較,如果順序不一樣,結果也是不一樣的!
剩下的就是一些經典方法:
在prototype.js中,擁有isElement,isArray,isHash,isFunctoion,isString,isNumber,isDate,isUndefined。
mootools有一個typeOf判定基本類型,instanceOf判定自定義“類”
RightJS 有isFunction , isHash , isString , isNumber , isArray ,isElement, isNode.
Ext有比較全面的判斷,isEmpty,isArray,isDate,isObject,isSimpleObject,isPrimitive,isFunction,isNumber,isMumeric,isString,isBoolean,isElement,isTextNode,isDefined,isIterable,應有盡有。最后,還有typeOf判斷基本類型。
Underscore.js有isElement,isEmpty,isArray,isArgument,isObject,isFunction,isString,isNumber,isFinite,isNaN,isBoolean,isDate,isRegExp,isNull,isUndefined.
jQuery就不與其它框架一樣了,在jQuery1.4中只有isFunction,isArray,isPlainObject,isEmptyObject。isFunction,isArray用戶肯定用的較多,isPlainObject是用來判斷是否是純凈的js對象。既不是DOM,BOM對象,也不是自定義的“類”的實例對象,制造它的目的最初是為了深拷貝,避開像window那樣自己引用自己的對象。isEmptyObject是用于數據緩存的系統(tǒng),當此對象為空時,就可以刪除它。
//jQuery2.0純凈數組的判定思路
jQuery.isPlainObject = function(obj){
//首先排除基礎類型不為Object的類型,然后是DOM節(jié)點與window對象
if(jQuery.type(obj) !== "object" || object.nodeType || jQuery.isWindow(obj)){
return false;
}
//然后回溯它的最近的原型對象是否有isPrototypeOf.
//舊版本的IE一些原生的對象沒有暴露constructor、prototype。因此在這里過濾掉
try{
if (obj.constructor && !hasOwn.call(obj.constructor.prototype,"isPrototypeOf")){
return false;
}
} case(e) {
return false;
}
return true;
}
avalon.mobile中有一個更精簡的版本,只支持最新的瀏覽器,可以大膽的使用ecma262v5新API
avalon.isPlainObject = function(obj){
return obj && typeof obj === "object" && Object.getPrototypeOf(obj) === Object.prototype
}
isArrayLike也是一個常用的方法,但是判定一個類數組太難了,唯一的識別方法就是判斷有一個大于0或等于0的length屬性,此外,還有一些共識,如window與函數和元素節(jié)點,如(form元素),不算類數組,雖然它們都滿足前面的條件。因此,至今jQuery都沒有把它暴露出來。
//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){//檢測length是否為非負整數
try{
if ({}.prototypeIsEnumerable.call(obj,'length') === false){
return Array.isArray(obj) || /^\s?function/.test(obj.item || obj.callee)
}
return true;
} catch (e) { //IE的NodeList直接報錯
return true;
}
}
}
return false
}
在Prototype.js1.3版本中的研究成果(Object.prototype.toString.call)就應用于jQuery,在jQuery1.2中,判斷一個變量是否為函數非常復雜。
isFunction : function(fn){
return !!fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /^[\s[]?function/.test(fn + "");
}
jQuery1.43中引入isWindow來處理makeArray中對window的判定,引入isNaN用于確保樣式賦值的安全。同時引入type代替typeof關鍵字,用于獲取基本數據的基本類型。
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";
}
jQuery1.7中添加isNumeric代替isNaN。這是個不同于其它框架的isNumber,它可以是個字符串,只要外觀上像數字就可以了。但jQuery1.7還做了一個違背之前提到穩(wěn)定性的事情。冒然去掉jQuery.isNaN,因此,基于舊版jQuery的有大批插件失效。
//jQuery.1.43-1.64
jQuery.isNaN = function ( obj ) {
return obj == null || !rdigit.test( obj ) || isNaN( obj );
}
//jQuery1.7就是isNaN的去反版
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;
}
massFarmeWork的思路與jQuery一致,盡量減少isXXX系列的代碼,把is Window,isNaN,nodeName等方法做了整合。代碼較長,既可以獲取類型,也可以傳入第二參數進行類型比較。
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".replace($.rword,function( name ) {
class2type[ "[object" + name + "]" ] = name;
});
//class2type這個映射幾乎把常用的判定對象一網打盡
mass.type = function( obj , str ){
var result = class2type[ (obj == null || obj !== obj) ? obj : toString.call(obj) ] || obj.nodeName || "#";
if(result.charAt(0) === "#") { //兼容舊版瀏覽器的個別情況,如window.opera
//利用IE678 window === document為true,document === window為false
if( obj == obj.document && obj.document != obj ) {
result = "window"; //返回構造器名稱
} else if ( obj.nodeType === 9 ) {
result = "Document";
} else if ( obj.callee) {
result = "Arguments";
} else if ( isFinite(obj.length) && obj.item ) {
result = "NodeList" //處理節(jié)點集合
} else {
result = toString.call(obj).slice(8,-1);
}
}
if(str){
result str === result;
}
return result;
}
然后type方法就十分輕松了,用toSring.call(obj)得出值的左鍵,直接從映射中取得。IE678,我們才費一些周折處理window,document,argument,nodeList等對象。
百度的七巧板基于實用主義,判定也十分嚴謹。與EXT一樣,能想到的寫上,并且判定十分嚴謹。
目前版本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.主流框架的引入機制-domReady
domReady其實是一種名為"DOMContentLoaded"事件的別稱,不過由于框架的需要,它與真正的DOMContentLoaded有一點區(qū)別,在很多新手和舊的書中,很多人將其寫在window.onload回調中,防止dom樹還沒有建完就開始對節(jié)點操作。而對于框架來說,越早越介入dom就越好,如要進行特征偵測之類的。domready還可以滿足用戶提前綁定事件需求,因為有時頁面圖片過多等,window.onload事件遲遲不能觸發(fā),這時用戶操作都沒有效果,因此,主流的框架都引入了domReady機制,并且費了很大周折才兼容所有瀏覽器。具體的策略如下:
對于支持DOMContentLoaded事件使用DOMcontentLoaded事件
舊版本IE使用Diego perini發(fā)現的著名Hack
//by Diego Perini 2007.10.5
function IEContentLoaded (w, fn) {
var d = w.document, done = false,
// 只執(zhí)行一次用戶的回調函數init()
init = function () {
if (!done) {
done = true;
fn();
}
};
(function () {
try {
// DOM樹未創(chuàng)建完之前調用doScroll會拋出錯誤
d.documentElement.doScroll('left');
} catch (e) {
//延遲再試一次~
setTimeout(arguments.callee, 50);
return;
}
// 沒有錯誤就表示DOM樹創(chuàng)建完畢,然后立馬執(zhí)行用戶回調
init();
})();
//監(jiān)聽document的加載狀態(tài)
d.onreadystatechange = function() {
// 如果用戶是在domReady之后綁定的函數,就立馬執(zhí)行
if (d.readyState == 'complete') {
d.onreadystatechange = null;
init();
}
};
}
此外,IE還可以通過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即使是死鏈也能觸發(fā)事件
if (this.readyState == "complete")
init(); // 指定了defer的script在dom樹建完才觸發(fā)
};
不過還有個問題,如果我們的種子模塊是動態(tài)加載的,在它插入dom樹時,DOM樹是否已經建完呢?這該怎么觸發(fā)ready回調?jQuery的方案是,連onload也監(jiān)聽了 ,但如果連onload也沒趕上,就判定document.readyState等于complete。(完美)可惜ff3.6之前沒有這屬性,看mass的方案
var readyList = [];
mess.ready = function( fn ) {
if ( readyList ) {
fn.push( fn );
} else {
fn();
}
}
var readyFn ,ready = W3C ? "DOMContentLoaded" : "readyStatechange";
function fireReady() {
for (var i = 0 , fn; fn = readyList[i++]) {
fn();
}
readyList = null;
fireReady = $.noop; //惰性函數,防止IE9調用_checkDeps
}
function doScrollCheck() {
try { //IE下通過doScrollCheck檢測DOM樹是否建設完
html.doScroll("left");
fireReady();
} catch (e){
setTimeout(doScrollCheck);
}
}
//FF3.6前,沒有readyState屬性
if (!document.readyState){
var readyState = document.readyState = document.body ? "complete" : "loading";
if (document.readyState === "complete") {
fireReady(); //如果在domReay之外加載
} else {
$.bind(document,ready,readyFn = function(){
if(W3C || document.readyState === "complete") {
fireReady();
if(readyState){ //IE下不能該項document.readyState
document.readyState = "complete";
}
}
});
if (html.doScroll){
try {//如果跨域就報錯,證明有兩個窗口
if (self.eval === parent.eval) {
doScrollCheck();
}
} catch (e) {
doScrollCheck();
}
}
}
}
6.無沖突處理
無沖突處理也叫多庫共存,$是這個重要的函數名,以至于大家都愛拿它來做自己的命名空間,當jQuery開始發(fā)展時,Prototype是主流,jQuery發(fā)明了noConflict函數
var window = this,
undefined,
_jQuery = window.jQuery,
_$ = window.$,
//將window存入閉包中的同名變量,方便內部函數調用windows不用太麻煩查找它。
//_jQuery與_$用于以后重寫
jQuery = window.jQuery = window.$ = function(selector, context){
//用于返回一個jQuery對象
return new jQuery.fn.init(selector,context);
}
jQuery.extend({
noConflict : function(deep) {
//引入jQuery類庫后,閉包外邊的window.$與window.jQuery都儲存著一個函數
//它是用來生成jQuery對象或在domReady后執(zhí)行里面的函數
//回顧最上面的代碼,在還沒有把function賦值給它們時,_jQuery和_$已經被賦值了,因此,它們兩的值必然是undefined
//因此,這種放棄控制權的技術很簡單,就是用undefined把window.$里邊jQuery函數清除掉。
//這時,prototype或mootools的$就被拿走了
window.$ = _$; //相當于window.$ = undefined,如果你有一個叫jQuery的庫,也能大方的過渡出去。
//這時,需要給noConflict添加一個布爾值,true
if (deep)
//但我們必須使用一個東西接納jQuery與jQuey的入口函數
//閉包里邊的東西除非被window等宿主引用,否則是不可見的
//因此,我們把閉包里的jQuery return出去,外面用一個變量接納就可以
window.jQuery = _jQuery; //相當window.jQuery = undefined
return jQuery;
}
})
使用時,先引入別人的庫,然后引入jQuery,使用調用$.noConflict()進行改名,這樣就不影響別人的$運行了。
mass的操作方式是在script標簽上定義一個nick屬性,那么釋放出來的命名空間就是你的那個屬性值。里面實現了類似jQuery的機制。
<script nike="aaa" src="mass.js"></script>
<script>
aaa.log("xxxxxx")
</script>
以上所述就是本文的全部內容了,希望大家能夠喜歡。
- 十大熱門的JavaScript框架和庫
- 深入解析JavaScript框架Backbone.js中的事件機制
- JavaScript框架是什么?怎樣才能叫做框架?
- 超贊的動手創(chuàng)建JavaScript框架的詳細教程
- javascript框架設計之類工廠
- javascript框架設計之瀏覽器的嗅探和特征偵測
- javascript框架設計之框架分類及主要功能
- 2014 年最熱門的21款JavaScript框架推薦
- javascript框架設計讀書筆記之數組的擴展與修復
- javascript框架設計讀書筆記之字符串的擴展和修復
- javascript框架設計讀書筆記之模塊加載系統(tǒng)
- javascript框架設計讀書筆記之種子模塊
- JavaScript框架(iframe)操作總結
- 怎么選擇Javascript框架(Javascript Framework)
- 詳細介紹8款超實用JavaScript框架
- brook javascript框架介紹
- 16個最流行的JavaScript框架[推薦]
- 如何選擇適合你的JavaScript框架
相關文章
優(yōu)化javascript的執(zhí)行效率一些方法總結
本文為大家介紹下優(yōu)化javascript的執(zhí)行效率一些方法,個人感覺還不錯,感興趣的朋友可以了解下2013-12-12

