精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

如何編寫可維護(hù)的JavaScript代碼?

開發(fā) 前端
在正式開始這篇博客之前,我們需要問自己為什么代碼可維護(hù)性值得我們關(guān)注。相信只要你寫過相當(dāng)量的代碼后,都已經(jīng)發(fā)現(xiàn)了這點(diǎn):Fix Bug比寫代碼困難得多。

PS:本人非前端開發(fā)人員,此文為業(yè)余興趣之作

JavaScript這門編程語言發(fā)展至今已經(jīng)非常流行了,各種名詞也層出不窮,我們隨便列舉下就有一大堆,比如Node.js、jQuery、 JavaScript MVC、Backbone.js、AMD、CommonJS、RequireJS、CoffeScript、Flexigrid、Highchart、 Script Loader、Script Minifier、JSLint、JSON、Ajax......這么多的東西席卷我們的腦海,無疑讓人頭暈?zāi)垦!5举|(zhì)的東西總是不變的,而所謂本質(zhì)就是一些核心的基礎(chǔ)概念。這里的基礎(chǔ)不是指JavaScript的表達(dá)式、數(shù)據(jù)類型、函數(shù)API等基礎(chǔ)知識(shí),而是指支撐上面這么一大堆JavaScript 名詞背后東西的基礎(chǔ)。我知道這樣會(huì)讓我這篇文章很難寫下去,因?yàn)槟菍嘀黝},所以本文只打算管中窺豹:本文將先講一些概念,然后講一些實(shí)踐指導(dǎo)原則,***涉及一些工具的討論。

在正式開始這篇博客之前,我們需要問自己為什么代碼可維護(hù)性值得我們關(guān)注。相信只要你寫過相當(dāng)量的代碼后,都已經(jīng)發(fā)現(xiàn)了這點(diǎn):Fix Bug比寫代碼困難得多。花三個(gè)小時(shí)寫的代碼,而之后為了Fix其中的一個(gè)Bug花兩三天時(shí)間,這種情況并不少見。再加上Fix Bug的人很可能不是代碼原作者,這無疑更雪上加霜。所以代碼可維護(hù)性是一個(gè)非常值得探討的話題,提高代碼可維護(hù)性就一定程度上能節(jié)省Fix Bug的時(shí)間,節(jié)省Fix Bug的時(shí)間進(jìn)而就節(jié)省了人力成本。

No 1. 將代碼組織成模塊

基本任何一門編程語言都認(rèn)為模塊化能提升代碼可維護(hù)性。我們知道軟件工程的核心在于控制復(fù)雜度,而模塊化本質(zhì)上是分離關(guān)注點(diǎn),從而分解復(fù)雜度。

IIFE模塊模式

當(dāng)我們最開始學(xué)習(xí)編寫JavaSript代碼時(shí),基本都會(huì)寫下面這樣的代碼:

  1. var myVar = 10;  
  2. var myFunc = function() {  
  3.    // ...  
  4. }; 

這樣的代碼本身沒有什么問題,但是當(dāng)這樣的代碼越來越多時(shí),會(huì)給代碼維護(hù)帶來沉重的負(fù)擔(dān)。原因是這樣導(dǎo)致myVar和myFunc暴露給全局命名空間,從而污染了全局命名空間。以我個(gè)人經(jīng)驗(yàn)來看,一般當(dāng)某個(gè)頁面中的JavaScript代碼達(dá)到200行左右時(shí)就開始要考慮這個(gè)問題了,尤其是在企業(yè)項(xiàng)目中。那么我們該怎么辦呢?

最簡單的解決方法是采用IIFE(Immediate Invoked Function Expression,立即執(zhí)行函數(shù)表達(dá)式)來解決(注意這里是函數(shù)表達(dá)式,而不是函數(shù)聲明,函數(shù)聲明類似 var myFunc = function() { // ... }),如下:

  1. (function() {  
  2.    var myVar = 10;  
  3.    var myFunc = function() {  
  4.       // ...  
  5.    };  
  6. }) (); 

現(xiàn)在myVar和myFunc的作用域范圍就被鎖定在這個(gè)函數(shù)表達(dá)式內(nèi)部,而不會(huì)污染全局命名空間了。這有點(diǎn)類似”沙盒機(jī)制“(也是提供了一個(gè)安全的執(zhí)行上下文)。我們知道JavaScript中沒有塊級(jí)作用域,能產(chǎn)生作用域只能借助函數(shù),正如上面這個(gè)例子一樣。

但是現(xiàn)在myVar、myFunc只能在函數(shù)表達(dá)式內(nèi)部被使用,如果它需要向外提供一些借口或功能(像大部分JavaScript框架或JavaScript庫一樣),那么該怎么辦呢?我們會(huì)采用下面的做法:

  1. (function(window, $, undefined) {  
  2.    var myFunc = function() {  
  3.       // ...  
  4.    }  
  5.  
  6.    window.myFunc = myFuc;  
  7. }) (window, jQuery); 

我們來簡單分析下,代碼很簡單:首先將window對象和jQuery對象作為立即執(zhí)行函數(shù)表達(dá)式的參數(shù),$只是傳入的jQuery對象的別名;其次我們并未傳遞第三個(gè)參數(shù),但是函數(shù)卻有一個(gè)名為undefined的參數(shù),這是一個(gè)小技巧,正因?yàn)闆]有傳第三個(gè)參數(shù),所以這里第三個(gè)參數(shù)undefined的值始終是undefined,就保證內(nèi)部能放心使用undefined,而不用擔(dān)心其他地方修改undefined的值;***通過 window.myFunc導(dǎo)出要暴露給外部的函數(shù)。

比如我們看一個(gè)實(shí)際JavaScript類庫的例子,比如 Validate.js,我們可以看到它是這樣導(dǎo)出函數(shù)的:

  1. (function(window, document, undefined) {  
  2.    var FormValidator = function(formName, fields, callback) {  
  3.       // ...  
  4.    };  
  5.  
  6.    window.FormValidator = FormValidator;  
  7. }) (window, document); 

是不是與前面說的基本一樣?另一個(gè)例子是jQuery插件的編寫范式中的一種,如下:

  1. (function($) {       
  2.    $.fn.pluginName = function() {     
  3.      // plugin implementation code   
  4.    };     
  5. })(jQuery);     

既然jQuery插件都來了,那再來一個(gè)jQuery源碼的例子也無妨:

  1. (function( window, undefined ) {  
  2.    var jQuery = function( selector, context ) {   // The jQuery object is actually just the init constructor 'enhanced'  
  3.       return new jQuery.fn.init( selector, context, rootjQuery );  
  4.    },  
  5.  
  6.    // Expose jQuery to the global object  
  7.    window.jQuery = window.$ = jQuery;  
  8. })( window ); 

上面這樣寫使得我們調(diào)用jQuery函數(shù)既可以用$("body"),又可以用jQuery("body")。

命名空間(Namespace)

雖然使用IIEF模塊模式讓我們的代碼組織成一個(gè)個(gè)模塊,維護(hù)性提升了,但如果代碼規(guī)模進(jìn)一步增大,比如達(dá)到2000-10000級(jí)別,這時(shí)前面方法的局限性又體現(xiàn)出來了?

怎么說呢?觀察下前面的代碼,所有函數(shù)都是通過作為window對象屬性的方式導(dǎo)出的,這樣如果有很多個(gè)開發(fā)人員同時(shí)在開發(fā),那么就顯得不太優(yōu)雅了。尤其是有的模塊與模塊之間可能存在層級(jí)關(guān)系,這時(shí)候我們需要借助“命名空間”了,命名空間可以用來對函數(shù)進(jìn)行分組。

我們可以這樣寫:

  1. (function(myApp, $, undefined) {  
  2.    // ...  
  3. }) (window.myApp = window.myApp || {}, jQuery); 

或者這樣:

  1. var myApp = (function(myApp, $, undefined) {  
  2.    ...  
  3.    return myApp;  
  4. }) (window.myApp || {}, jQuery); 

現(xiàn)在我們不再往立即執(zhí)行函數(shù)表達(dá)式傳遞window對象,而是傳遞掛載在window對象上的命名空間對象。第二段代碼中的 || 是為了避免在多個(gè)地方使用myApp變量時(shí)重復(fù)創(chuàng)建對象。

Revealing Module Pattern

這種模塊模式的主要作用是區(qū)分出私有變量/函數(shù)和公共變量/函數(shù),達(dá)到將私有變量/函數(shù)隱藏在函數(shù)內(nèi)部,而將公有變量/函數(shù)暴露給外部的目的。

代碼示例如下:

  1. var myModule = (function(window, $, undefined) {  
  2.    var _myPrivateVar1 = "";  
  3.    var _myPrivateVar2 = "";  
  4.    var _myPrivateFunc = function() {  
  5.       return _myPrivateVar1 + _myPrivateVar2;  
  6.    };  
  7.  
  8.    return {  
  9.       getMyVar1: function() { return _myPrivateVar1; },  
  10.       setMyVar1: function(val) { _myPrivateVar1 = val; },  
  11.       someFunc: _myPrivateFunc   
  12.    };  
  13. }) (window, jQuery); 

myPrivateVar1、 myPrivateVar2是私有變量,myPrivateFunc是私有函數(shù)。而getMyVar1(public getter)、getMyVar1(public setter)、someFunc是公共函數(shù)。是不是有點(diǎn)類似普通的Java Bean?

或者我們可以寫成這種形式(換湯不換藥):

  1. var myModule = (function(window, $, undefined) {  
  2.    var my= {};  
  3.  
  4.    var _myPrivateVar1 = "";  
  5.    var _myPrivateVar2 = "";  
  6.    var _myPrivateFunc = function() {  
  7.       return _myPrivateVar1 + _myPrivateVar2;  
  8.    };  
  9.  
  10.    my.getMyVar1 = function() {  
  11.       return _myPrivateVar1;  
  12.    };  
  13.      
  14.    my.setMyVar1 = function(val) {  
  15.       _myPrivateVar1 = val;  
  16.    };  
  17.  
  18.    my.someFunc = _myPrivateFunc;  
  19.  
  20.    return my;  
  21. }) (window, jQuery); 

模塊擴(kuò)展(Module Augmentation)

有時(shí)候我們想為某個(gè)已有模塊添加額外功能,可以像下面這樣:

  1. var MODULE = (function (my) {  
  2.     my.anotherMethod = function () {  
  3.         // added method...  
  4.     };  
  5.  
  6.     return my;  
  7. }(MODULE  || {})); 

Tight Augmentation

上面的例子傳入的MODULE可能是undefined,也就是說它之前可以不存在。與之對應(yīng)Tight Augmentation模式要求傳入的MODULE一定存在并且已經(jīng)被加載進(jìn)來。

  1. var MODULE = (function (my) {  
  2.     var old_moduleMethod = my.moduleMethod;  
  3.  
  4.     my.moduleMethod = function () {  
  5.         // method override, has access to old through old_moduleMethod...  
  6.     };  
  7.  
  8.     return my;  
  9. }(MODULE)); 

代碼意圖很明顯:實(shí)現(xiàn)了重寫原模塊的moduleMethod函數(shù),并且可以在重寫的函數(shù)中調(diào)用od_moduleMethod。但這種寫法不夠靈活,因?yàn)樗俣艘粋€(gè)先決條件:MODULE模塊一定存在并且被加載進(jìn)來了,且它包含moduleMethod函數(shù)。

子模塊模式

這個(gè)模式非常簡單,比如我們?yōu)楝F(xiàn)有模塊MODULE創(chuàng)建一個(gè)子模塊如下:

  1. MODULE.sub = (function () {  
  2.     var my = {};  
  3.     // ...  
  4.  
  5.     return my;  
  6. }()); 

#p#

No 2. 利用OO

構(gòu)造函數(shù)模式(Constructor Pattern)

JavaScript沒有類的概念,所以我們不可以通過類來創(chuàng)建對象,但是可以通過函數(shù)來創(chuàng)建對象。比如下面這樣:

  1. var Person = function(firstName, lastName, age) {  
  2.    this.firstName = firstName;  
  3.    this.lastName = lastName;  
  4.    this.age = age;  
  5. };  
  6.  
  7. Person.prototype.country = "China";  
  8. Person.prototype.greet = function() {  
  9.    alert("Hello, I am " + this.firstName + " " + this.lastName);  
  10. }; 

這里firstName、lastName、age可以類比為Java類中的實(shí)例變量,每個(gè)對象有專屬于自己的一份。而country可以類比為Java類中的靜態(tài)變量,greet函數(shù)類比為Java類中的靜態(tài)方法,所有對象共享一份。我們通過下面的代碼驗(yàn)證下(在Chrome的控制臺(tái)輸):

  1. var Person = function(firstName, lastName, age) {  
  2.    this.firstName = firstName;  
  3.    this.lastName = lastName;  
  4.    this.age = age;  
  5. };  
  6.  
  7. Person.prototype.country = "China";  
  8. Person.prototype.greet = function() {  
  9.    alert("Hello, I am " + this.firstName + " " + this.lastName);  
  10. };  
  11.  
  12. var p1 = new Person("Hub""John", 30);  
  13. var p2 = new Person("Mock""William", 23);  
  14. console.log(p1.fistName == p2.firstName);   // false  
  15. console.log(p1.country == p2.country);   // true  
  16. console.log(p1.greet == p2.greet);   // true 

但是如果你繼續(xù)測下面的代碼,你得不到你可能預(yù)期的p2.country也變?yōu)閁K:

  1. p1.country = "UK";  
  2. console.log(p2.country);   // China 

這與作用域鏈有關(guān),后面我會(huì)詳細(xì)闡述。繼續(xù)回到這里。既然類得以通過函數(shù)模擬,那么我們?nèi)绾文M類的繼承呢?

比如我們現(xiàn)在需要一個(gè)司機(jī)類,讓它繼承Person,我們可以這樣:

  1. var Driver = function(firstName, lastName, age) {  
  2.    this.firstName = firstName;  
  3.    this.lastName = lastName;  
  4.    this.age = age;  
  5. };  
  6.  
  7. Driver.prototype = new Person();   // 1  
  8. Driver.prototype.drive = function() {  
  9.    alert("I'm driving. ");  
  10. };  
  11.  
  12. var myDriver = new Driver("Butter""John", 28);  
  13. myDriver.greet();  
  14. myDriver.drive(); 

代碼行1是實(shí)現(xiàn)繼承的關(guān)鍵,這之后Driver又定義了它擴(kuò)展的只屬于它自己的函數(shù)drive,這樣它既可以調(diào)用從Person繼承的greet函數(shù),又可以調(diào)用自己的drive函數(shù)了。

#p#

No3. 遵循一些實(shí)踐指導(dǎo)原則

下面是一些指導(dǎo)編寫高可維護(hù)性JavaScript代碼的實(shí)踐原則的不完整總結(jié)。

盡量避免全局變量

JavaScript使用函數(shù)來管理作用域。每個(gè)全局變量都會(huì)成為Global對象的屬性。你也許不熟悉Global對象,那我們先來說說Global對象。ECMAScript中的Global對象在某種意義上是作為一個(gè)***的“兜底兒”對象來定義的:即所有不屬于任何其他對象的屬性和方法最終都是它的屬性和方法。所有在全局作用域中定義的變量和函數(shù)都是Global對象的屬性。像escape()、encodeURIComponent()、 undefined都是Global對象的方法或?qū)傩浴?/p>

事實(shí)上有一個(gè)我們更熟悉的對象指向Global對象,那就是window對象。下面的代碼演示了定義全局對象和訪問全局對象:

  1. myglobal = "hello"// antipattern  
  2. console.log(myglobal); // "hello"  
  3. console.log(window.myglobal); // "hello"  
  4. console.log(window["myglobal"]); // "hello"  
  5. console.log(this.myglobal); // "hello" 

使用全局變量的缺點(diǎn)是:

全局變量被應(yīng)用中所有代碼共享,所以很容易導(dǎo)致不同頁面出現(xiàn)命名沖突(尤其是包含第三方代碼時(shí))

全局變量可能與宿主環(huán)境的變量沖突

  1. function sum(x, y) {  
  2.    // antipattern: implied global  
  3.    result = x + y;  
  4.    return result;  

result現(xiàn)在就是一個(gè)全局變量。要改正也很簡單,如下:

  1. function sum(x, y) {  
  2.    var result = x + y;  
  3.    return result;  

另外通過var聲明創(chuàng)建的全局變量與未通過var聲明隱式創(chuàng)建的全局變量有下面的不同之處:

通過var聲明創(chuàng)建的全局變量無法被delete

而隱式創(chuàng)建的全局變量可以被delete

delete操作符運(yùn)算后返回true或false,標(biāo)識(shí)是否刪除成功,如下:

  1. // define three globals  
  2. var global_var = 1;  
  3. global_novar = 2; // antipattern  
  4. (function () {  
  5.    global_fromfunc = 3; // antipattern  
  6. }());  
  7.  
  8. // attempt to delete  
  9. delete global_var; // false  
  10. delete global_novar; // true  
  11. delete global_fromfunc; // true  
  12.  
  13. // test the deletion  
  14. typeof global_var; // "number"  
  15. typeof global_novar; // "undefined"  
  16. typeof global_fromfunc; // "undefined" 

推薦使用Single Var Pattern來避免全局變量如下:

  1. function func() {  
  2.    var a = 1,  
  3.        b = 2,  
  4.        sum = a + b,  
  5.        myobject = {},  
  6.        i,  
  7.        j;  
  8.    // function body...  

上面只用了一個(gè)var關(guān)鍵詞就讓a、b、sum等變量全部成為局部變量了。并且為每個(gè)變量都設(shè)定了初始值,這可以避免將來可能出現(xiàn)的邏輯錯(cuò)誤,并提高可讀性(設(shè)定初始值意味著能很快看出變量保存的到底是一個(gè)數(shù)值還是字符串或者是一個(gè)對象)。

局部變量相對于全局變量的另一個(gè)優(yōu)勢在于性能,在函數(shù)內(nèi)部從函數(shù)本地作用域查找一個(gè)變量毫無疑問比去查找一個(gè)全局變量快。

避免變量提升(hoisting)陷阱

你很可能已經(jīng)看到過很多次下面這段代碼,這段代碼經(jīng)常用來考察變量提升的概念:

  1. myName = "global";  
  2. function func() {  
  3.    console.log(myName);   // undefined  
  4.    var myName = "local";  
  5.    console.log(myName);   // local  
  6. }  
  7.  
  8. func(); 

這段代碼輸出什么呢?JavaScript的變量提升會(huì)讓這段代碼的效果等價(jià)于下面的代碼:

  1. myName = "global";  
  2. function func() {  
  3.    var myName;  
  4.    console.log(myName);   // undefined  
  5.    myName = "local";  
  6.    console.log(myName);   // local  
  7. }  
  8.  
  9. func(); 

所以輸出為undefined、local就不難理解了。變量提升不是ECMAScript標(biāo)準(zhǔn),但是卻被普遍采用

對非數(shù)組對象用for-in,而對數(shù)組對象用普通for循環(huán)

雖然技術(shù)上for-in可以對任何對象的屬性進(jìn)行遍歷,但是不推薦對數(shù)組對象用for-in,理由如下:

如果數(shù)組對象包含擴(kuò)展函數(shù),可能導(dǎo)致邏輯錯(cuò)誤

for-in不能保證輸出順序

for-in遍歷數(shù)組對象性能較差,因?yàn)闀?huì)沿著原型鏈一直向上查找所指向的原型對象上的屬性

所以推薦對數(shù)組對象用普通的for循環(huán),而對非數(shù)組對象用for-in。但是對非數(shù)組對象使用for-in時(shí)常常需要利用hasOwnProperty()來濾除從原型鏈繼承的屬性(而一般不是你想要列出來的),比如下面這個(gè)例子:

  1. // the object  
  2. var man = {  
  3.    hands: 2,  
  4.    legs: 2,  
  5.    heads: 1  
  6. };  
  7.  
  8. // somewhere else in the code  
  9. // a method was added to all objects  
  10. if (typeof Object.prototype.clone === "undefined") {  
  11.    Object.prototype.clone = function () {};  
  12. }  
  13.  
  14. for(var i  in man) {   
  15.    console.log(i, ": ", man[i]);   

輸出如下:

  1. hands :  2  
  2. legs :  2  
  3. heads :  1  
  4. clone :  function () {} 

即多了clone,這個(gè)可能是另外一個(gè)開發(fā)者在Object的原型對象上定義的函數(shù),卻影響到了我們現(xiàn)在的代碼,所以規(guī)范的做法有兩點(diǎn)。***堅(jiān)決不允許在原生對象的原型對象上擴(kuò)展函數(shù)或者屬性 。 第二將代碼改寫為類似下面這種:

  1. for(var i  in man) {  
  2.    if(man.hasOwnProperty(i)) {  
  3.       console.log(i, ": ", man[i]);  
  4.    }  

進(jìn)一步我們可以改寫代碼如下:

  1. for(var i  in man) {  
  2.    if(man.hasOwnProperty(i)) {  
  3.       console.log(i, ": ", man[i]);  
  4.    }  

這樣有啥好處呢?***點(diǎn)防止man對象重寫了hasOwnProperty函數(shù)的情況;第二點(diǎn)性能上提升了,主要是原型鏈查找更快了。

進(jìn)一步緩存Object.prototype.hasOwnProperty函數(shù),代碼變成下面這樣:

  1. var i, hasOwn = Object.prototype.hasOwnProperty;  
  2. for (i in man) {  
  3.     if (hasOwn.call(man, i)) { // filter  
  4.         console.log(i, ":", man[i]);  
  5.     }  

避免隱式類型轉(zhuǎn)換

隱式類型轉(zhuǎn)換可能導(dǎo)致一些微妙的邏輯錯(cuò)誤。我們知道下面的代碼返回的是true:

  1. 0 == false  
  2. 0 == ""  

建議做法是始終使用恒等于和恒不等于,即===和!===。

而對于下面的代碼:

  1. null == false 
  2. undefined == false 

我們常常期望它返回true的,但卻返回的是false。

那么我們可以用下面的代碼來將其強(qiáng)制轉(zhuǎn)換為布爾類型后比較:

  1. !!null === false 
  2. !!undefined === false  

避免eval()

eval()接受任意字符串并將其作為JavaScript代碼進(jìn)行執(zhí)行,最初常用于執(zhí)行動(dòng)態(tài)生成的代碼,但是eval()是有害的,比如可能導(dǎo)致XSS漏洞,如果根據(jù)某個(gè)可變屬性名訪問屬性值,可以用[]取代eval(),如下:

  1. // antipattern  
  2. var property = "name";  
  3. alert(eval("obj." + property));  
  4.  
  5. // preferred  
  6. var property = "name";  
  7. alert(obj[property]); 

注意傳遞字符串給setTimeout()、setInterval()和Function()也類似eval(),也應(yīng)該避免。比如下面:

  1. // antipatterns  
  2. setTimeout("myFunc()", 1000);  
  3. setTimeout("myFunc(1, 2, 3)", 1000);  
  4.  
  5. // preferred  
  6. setTimeout(myFunc, 1000);  
  7. setTimeout(function () {  
  8.    myFunc(1, 2, 3);  
  9. }, 1000); 

如果你遇到非要使用eval()不可的場景,用new Function()替代,因?yàn)閑val()的字符串參數(shù)中即使通過var聲明變量,它也會(huì)成為一個(gè)全局變量,而new Function()則不會(huì),如下: 

  1. eval("var myName='jxq'"); 

則myName成了全局變量,而用newFunction()如下:

  1. var a = new Function("firstName, lastName""var myName = firstName+lastName"); 

實(shí)際上a現(xiàn)在是一個(gè)匿名函數(shù):

  1. function anonymous(firstName, lastName) {  
  2.     var myName = firstName+lastName  

則myName現(xiàn)在就不是全局變量了。當(dāng)然如果還堅(jiān)持用eval(),可以用一個(gè)立即執(zhí)行函數(shù)表達(dá)式將eval()包起來:

  1. (function() {  
  2.    eval("var myName='jxq';");  
  3. }) ();   // jxq  
  4.  
  5. console.log(typeof myName);   // undefined 

另外一個(gè)eval()和Function()的區(qū)別是前者會(huì)影響作用域鏈,而后者不會(huì),如下:

  1. (function() {   
  2.    var local = 1;   
  3.    eval("console.log(typeof local);");   
  4. })();   // number  
  5.  
  6. (function() {   
  7.    var local = 1;   
  8.    Function("console.log(typeof local);");   
  9. })();   // undefined 

使用parseInt()時(shí),指定第二個(gè)進(jìn)制參數(shù)

這個(gè)不用多提,相信大家也都知道了

使用腳本引擎,讓JavaScript解析數(shù)據(jù)生成HTML

傳說中的12306在查詢車票時(shí)返回的是下面這么一大串(我已無力吐槽,這個(gè)是我今天剛截的,實(shí)際大概100來行):

  1. <span id='id_240000G13502' class='base_txtdiv' onmouseover=javascript:onStopHover('240000G13502#VNP#AOH') onmouseout='onStopOut()'>G135</span>,<img src='/otsweb/images/tips/first.gif'>&nbsp;&nbsp;&nbsp;&nbsp;北京南&nbsp;&nbsp;&nbsp;&nbsp;  
  2. <br>  
  3. &nbsp;&nbsp;&nbsp;&nbsp;12:40,<img src='/otsweb/images/tips/last.gif'>&nbsp;&nbsp;上海虹橋&nbsp;&nbsp;  
  4. <br>  
  5. &nbsp;&nbsp;&nbsp;&nbsp;18:04,05:24,8,--,<font color='#008800'>有</font>,<font color='#008800'>有</font>,--,--,--,--,--,--,--,<a name='btn130_2' class='btn130_2' style='text-decoration:none;' onclick=javascript:getSelected('G135#05:24#12:40#240000G13502#VNP#AOH#18:04#北京南#上海虹橋#01#08#O*****0072M*****00629*****0008#8A72A4AD8B70A5E0FF02AC9290DDB39C6E0B6D***0F8A9A8FB305FB11#P2')>預(yù)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;訂</a>\n1,<span id='id_240000G13705' class='base_txtdiv' onmouseover=javascript:onStopHover('240000G13705#VNP#AOH') onmouseout='onStopOut()'>G137</span>,<img src='/otsweb/images/tips/first.gif'>&nbsp;&nbsp;&nbsp;&nbsp;北京南&nbsp;&nbsp;&nbsp;&nbsp;  
  6. <br>  
  7. &nbsp;&nbsp;&nbsp;&nbsp;12:45,<img src='/otsweb/images/tips/last.gif'>&nbsp;&nbsp;上海虹橋&nbsp;&nbsp; 

為什么不能只返回?cái)?shù)據(jù)(比如用JSON),然后利用JavaScript模板引擎解析數(shù)據(jù)呢?比如下面這樣(使用了jQuery tmpl模板引擎,詳細(xì)參考我的代碼 JavaScript模板引擎使用):

  1. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
  2. "http://www.w3.org/TR/html4/loose.dtd">  
  3. <html xmlns="http://www.w3.org/1999/xhtml">  
  4.     <head>  
  5.         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
  6.         <title>JavaScript tmpl Use Demo</title>  
  7.         <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>  
  8.         <script src="./tmpl.js" type="text/javascript"></script>  
  9.                 <!-- 下面是模板user_tmpl -->  
  10.         <script type="text/html" id="user_tmpl">  
  11.             <% for ( var i = 0; i < users.length; i++ ) { %>  
  12.             <li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>  
  13.             <% } %>  
  14.         </script>  
  15.         <script type="text/javascript">  
  16.                         // 用來填充模板的數(shù)據(jù)  
  17.             var users = [  
  18.                 { url: "http://baidu.com", name: "jxq"},  
  19.                 { url: "http://google.com", name: "william"},  
  20.             ];  
  21.               
  22.             $(function() {  
  23.                                // 調(diào)用模板引擎函數(shù)將數(shù)據(jù)填充到模板獲得最終內(nèi)容  
  24.                 $("#myUl").html(tmpl("user_tmpl", users));  
  25.             });  
  26.         </script>  
  27.     </head>  
  28.     <body>  
  29.         <div>  
  30.             <ul id="myUl">  
  31.             </ul>  
  32.         </div>  
  33.     </body>  
  34. </html> 

使用模板引擎可以將數(shù)據(jù)和HTML內(nèi)容完全分離,這樣有幾個(gè)好處:

修改HTML結(jié)構(gòu)時(shí)幾乎可以不修改返回的數(shù)據(jù)的結(jié)構(gòu)

只返回純粹的數(shù)據(jù),節(jié)省了網(wǎng)絡(luò)帶寬(網(wǎng)絡(luò)帶寬就是錢)

采用一致的命名規(guī)范

構(gòu)造函數(shù)首字母大寫。

而非構(gòu)造函數(shù)的首字母小寫,標(biāo)識(shí)它們不應(yīng)該通過new操作符被調(diào)用。

常量名稱應(yīng)該全大寫。

私有變量或似有函數(shù)名稱前帶上下劃線,如下:

  1. var person = {  
  2.     getName: function () {  
  3.         return this._getFirst() + ' ' + this._getLast();  
  4.     },  
  5.  
  6.     _getFirst: function () {  
  7.         // ...  
  8.     },  
  9.     _getLast: function () {  
  10.         // ...  
  11.     }  
  12. }; 

不吝嗇注釋,但也不要胡亂注釋

為一些相對艱澀些的代碼(比如算法實(shí)現(xiàn))添加注釋。

為函數(shù)的功能、參數(shù)和返回值添加注釋。

不要對一些常識(shí)性的代碼進(jìn)行注釋,也不要像下面這樣多此一舉地注釋:

  1. var myName = "jxq";   // 聲明字符串變量myName,其值為"jxq" 

#p#

No4. 合理高效地使用工具

這里的工具包括JavaScript框架、JavaScript類庫以及一些平時(shí)自己積累的Code Snippet。

使用JavaScript框架的好處是框架為我們提供了一種合理的組織代碼方式,比如Backbone.js、Knockout.js這種框架能讓我們更好地將代碼按MVC或者M(jìn)VP模式分離。

而使用JavaScript類庫可以避免重復(fù)造輪子(而且往往造出一些不那么好的輪子),也可以讓我們更專注于整體業(yè)務(wù)流程而不是某個(gè)函數(shù)的具體實(shí)現(xiàn)。一些通用的功能如日期處理、金額數(shù)值處理***用現(xiàn)有的成熟類庫。

***使用自己平時(shí)積累的Code Snippet可以提高我們的編碼效率,并且最重要的是可以提供多種參考解決方案。

下面是一些流行的工具列表。

jQueryCoreUISelect

它提供了完全自定制功能,支持選項(xiàng)組、回調(diào)函數(shù)等等。另外一個(gè)擴(kuò)展Select控件的插件是jQuery Chosen,可以參考我分享的代碼:美化Select下拉框

jQueryCoreUISelect

Sisyphus.js

它提供了表單離線存儲(chǔ)功能,能夠自動(dòng)保存用戶未提交的表單數(shù)據(jù)。而當(dāng)提交表單后會(huì)清除數(shù)據(jù)。

Sisyphus.js

TextExt

這個(gè)類庫允許我們將HTML文本轉(zhuǎn)換成輸入域。

TextExt

Validate.js

這是一個(gè)輕量級(jí)表單驗(yàn)證類庫,它預(yù)定義了一系列驗(yàn)證規(guī)則(通過正則表達(dá)式),并且支持定制驗(yàn)證回調(diào)函數(shù)和驗(yàn)證失敗消息,兼容所有主流瀏覽器(包括IE 6),更詳細(xì)的信息有興趣的話可以參考我的博客 Validate.js框架源碼完全解讀

JavaScript Library

jQuery File Upload

jQuery文件上傳插件,支持多文件上傳

JavaScript Library

Handsontables: Excel-Like Tables For The Web

提供Web Excel功能的jQuery插件

JavaScript Library

Pivot.js

通過Pivot我們可以很方便地展現(xiàn)大量數(shù)據(jù)。數(shù)據(jù)源可以是CSV或者JSON

Pivot.js

Date.js

一個(gè)很方便的日期處理類庫。

[[70485]]

使用很簡單,下面是兩個(gè)小實(shí)例:

  1. // What date is next thursday?  
  2. Date.today().next().thursday();  
  3.  
  4. // Add 3 days to Today  
  5. Date.today().add(3).days();  
  6.  
  7. ... 

RequireJS

RequireJS是一個(gè)JavaScript文件和模塊加載器。使用RequireJS可以顯著提高代碼的運(yùn)行效率。據(jù)說百度音樂盒利用RequireJS后加載速度提高了好幾秒(按需加載),號(hào)稱神器。

[[70486]]

Grunt.js

Grunt.js是一個(gè)基于任務(wù)的命令行工具,可以用于JavaScript項(xiàng)目構(gòu)建。它預(yù)包含幾十個(gè)內(nèi)置的任務(wù):文件合并、項(xiàng)目腳手架(基于一個(gè)預(yù)定義的模板)、JSLint驗(yàn)證、UglifyJS代碼壓縮、qUnit單元測試、啟動(dòng)服務(wù)器等。

Grunt.js: Task-Based Command Line Tool

參考資料:

http://www.codethinked.com/preparing-yourself-for-modern-javascript-development

http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html

http://net.tutsplus.com/tutorials/javascript-ajax/the-essentials-of-writing-high-quality-javascript/

http://coding.smashingmagazine.com/2012/09/23/useful-javascript-libraries-jquery-plugins-web-developers/

原文鏈接:http://my.oschina.net/feichexia/blog/122217

責(zé)任編輯:張偉 來源: oschina
相關(guān)推薦

2016-11-30 18:35:03

JavaScript

2023-01-27 14:53:03

2022-06-07 09:30:35

JavaScript變量名參數(shù)

2020-07-17 13:01:44

If-Else代碼編程

2011-03-04 10:11:09

JavascriptAPI

2020-09-27 09:41:04

代碼開發(fā)注釋

2025-06-10 10:05:00

GoSOLID代碼

2018-11-08 15:50:18

前端Javascript重用性

2022-07-18 11:06:36

Go 語言GORM 庫數(shù)據(jù)庫

2023-11-08 13:55:27

2012-12-17 13:51:22

Web前端JavaScriptJS

2021-08-08 08:23:45

SQL代碼編程

2014-04-21 10:14:52

PromisesJavaScript

2009-06-24 15:00:39

Javascript代

2020-06-15 11:04:38

JavaScript 代碼JavaScript

2018-04-08 10:41:31

2018-03-30 15:19:24

2012-07-11 10:51:37

編程

2021-09-22 11:05:19

JS代碼前端

2021-01-14 09:59:07

JS代碼編碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

国产精品99一区二区三区| 成人无遮挡免费网站视频在线观看| 999在线观看精品免费不卡网站| 亚洲大胆美女视频| av观看免费在线| 毛片免费不卡| 91在线视频观看| 成人黄色影片在线| 99热只有这里有精品| 不卡日本视频| 欧美r级在线观看| 免费激情视频在线观看| a黄色片在线观看| 91蜜桃网址入口| 91免费在线视频网站| 亚洲视频免费播放| 亚洲激情中文在线| 亚洲欧美精品伊人久久| 亚洲AV成人精品| 国产福利91精品一区二区| 亚洲va欧美va人人爽午夜 | 妖精视频一区二区三区| 777欧美精品| 欧美xxxxx在线视频| 超碰在线caoporen| 国产精品毛片高清在线完整版| 国产精品一区免费观看| 国产精品人人爽| 日韩黄色免费网站| 97精品视频在线| 极品盗摄国产盗摄合集| 精品视频99| 亚洲精品久久久久久久久久久久| 日本高清免费在线视频| 日本综合视频| 日韩欧美在线视频日韩欧美在线视频 | 久久久999视频| 午夜在线激情影院| 成人免费在线视频| 天堂√在线观看一区二区| 天天干天天干天天干| 国产精品一卡二卡| 成人妇女淫片aaaa视频| 天天操天天干天天摸| 9色国产精品| 久久噜噜噜精品国产亚洲综合| 丝袜美腿小色网| 国产精品久久观看| 日韩在线视频网站| 人妻互换一区二区激情偷拍| 国产亚洲一区| 亚洲日韩第一页| 色无极影院亚洲| 亚洲性视频大全| 国产视频在线观看一区二区| 国产一级二级在线观看| 国产精品白丝av嫩草影院| 日韩欧美国产系列| 少妇搡bbbb搡bbb搡打电话| 综合激情五月婷婷| 欧美videos大乳护士334| 性高潮久久久久久| 成人av地址| 日韩av一区二区在线观看| 小毛片在线观看| 欧美一级三级| 亚洲欧美激情精品一区二区| 亚洲自拍偷拍图| 欧美精品一二| 久久视频国产精品免费视频在线| 国产一二三四区| 激情久久婷婷| 琪琪第一精品导航| 在线视频精品免费| 久久se这里有精品| 91原创国产| 亚洲欧洲综合在线| 国产农村妇女精品| 亚洲午夜精品一区二区三区| 老司机午夜在线| 亚洲国产美女搞黄色| 久久精品国产sm调教网站演员| 色是在线视频| 欧美日韩精品系列| 亚洲熟女乱综合一区二区| 国偷自产av一区二区三区| 亚洲精品一区久久久久久| 手机av在线不卡| 午夜国产欧美理论在线播放| 97**国产露脸精品国产| 欧美特级黄色片| 国产美女一区二区三区| 久久精品久久精品国产大片| 91电影在线播放| 亚洲最大成人综合| 无码人妻h动漫| 国产美女亚洲精品7777| 亚洲国产精品人久久电影| 日本精品在线观看视频| 欧美三级第一页| 欧亚精品中文字幕| 国产免费一区二区三区最新不卡| 97精品国产露脸对白| 亚洲成人网上| 91超碰在线| 欧美精品自拍偷拍| 野外性满足hd| 欧美精品国产| 国产精品久久久精品| 亚洲va欧美va| 国产精品毛片久久久久久| 九一国产精品视频| 国产激情综合| 在线视频欧美性高潮| 久久黄色免费视频| 日韩激情在线观看| 国产精品国色综合久久| 欧美13一16娇小xxxx| 精品久久久久久中文字幕| 三上悠亚在线一区二区| 欧美一级色片| 久久人人爽人人| 国产农村妇女毛片精品久久| 国产欧美视频一区二区| 国产素人在线观看| 超碰成人免费| 美日韩精品免费观看视频| 波多野结衣电影在线播放| 成人av先锋影音| 99热这里只有精品7| 亚洲美女尤物影院| 亚洲精品在线免费播放| 永久看片925tv| 精品在线一区二区三区| 天堂社区 天堂综合网 天堂资源最新版| 高端美女服务在线视频播放| 精品免费日韩av| 日韩视频中文字幕在线观看| 蜜桃视频在线一区| 日本视频一区在线观看| 日本不卡1234视频| 亚洲福利精品在线| 久草视频在线免费看| 精品一区二区三区在线播放视频 | 日本一区二区三区在线观看视频| 洋洋av久久久久久久一区| 污视频在线观看免费网站| 四虎成人av| 国产欧亚日韩视频| 毛片av在线| 日韩亚洲欧美一区二区三区| 1024手机在线视频| 国产黄色91视频| 男人天堂a在线| 九色丨蝌蚪丨成人| 91国产美女视频| 头脑特工队2免费完整版在线观看| 性做久久久久久久久| 日本一卡二卡在线| 中文精品视频| 日本一区二区三区免费看| 欧美韩国亚洲| 中文字幕亚洲精品| 国产精品久久久久久久免费看| 亚洲色图欧美偷拍| 岛国大片在线免费观看| 激情文学一区| 免费看成人午夜电影| 88xx成人永久免费观看| 国产亚洲欧美日韩一区二区| 国产乡下妇女三片| 自拍偷拍欧美激情| 色综合久久久无码中文字幕波多| 亚洲激情婷婷| 日本一区视频在线播放| 人人精品久久| 久久久久久久久久久av| 天堂在线一二区| 欧美色精品天天在线观看视频| 亚洲激情图片网| 国产一区二区电影| 妺妺窝人体色777777| 一道本一区二区三区| 国产又爽又黄的激情精品视频| 在线观看男女av免费网址| 亚洲精品二三区| 在线亚洲欧美日韩| 亚洲电影在线免费观看| 69视频在线观看免费| 国产成人精品三级| 日韩欧美xxxx| 欧美日韩精品| 午夜精品区一区二区三| 亚洲午夜精品| 国产精品久久久久久中文字| 欧洲性视频在线播放| 国产一区二区三区视频免费| 性生活黄色大片| 欧美在线999| 国产精品6666| 成人免费一区二区三区视频 | 婷婷成人在线| 成人午夜激情网| 男女羞羞在线观看| 不卡伊人av在线播放| 国产一二在线观看| 精品噜噜噜噜久久久久久久久试看 | 精品黑人一区二区三区观看时间| 久久成人综合网| 人妻熟妇乱又伦精品视频| 天天做天天爱天天爽综合网| 鲁丝一区二区三区免费| 亚洲国产视频二区| 国产在线拍揄自揄视频不卡99| 日韩在线伦理| 久久久久久久香蕉网| 黄色网页在线看| 伊人伊成久久人综合网站| 三级视频在线看| 日韩欧美电影一区| 一区二区三区免费在线视频| 欧美午夜电影在线| 日韩av女优在线观看| 亚洲蜜臀av乱码久久精品蜜桃| 日本理论中文字幕| 久久蜜桃av一区二区天堂| 男人的天堂影院| 国产成都精品91一区二区三| 国产精品久久久久久9999| 日本不卡中文字幕| 能看的毛片网站| 免费日韩视频| 欧美在线观看成人| 99在线精品免费视频九九视| aa视频在线播放| 欧美日韩伊人| 国产日本在线播放| 黄色欧美日韩| 野外做受又硬又粗又大视频√| 午夜精品影院| 日韩一级片一区二区| 欧美不卡高清| 800av在线免费观看| 欧美午夜电影在线观看| 国产爆乳无码一区二区麻豆| 欧美午夜不卡| 日韩小视频在线播放| 国产日韩一区二区三区在线| 日韩少妇内射免费播放18禁裸乳| 国产视频一区在线观看一区免费| av动漫在线看| 久久先锋资源| 污视频网站观看| 国产一区免费电影| 在线观看网站黄| 成人免费黄色大片| 中文字幕5566| 国产拍揄自揄精品视频麻豆| 欧美xxxx精品| 亚洲男人天堂av网| 国产精品theporn动漫| 五月天激情综合| 日本免费精品视频| 欧美剧在线免费观看网站| 国产三级伦理片| 精品欧美一区二区三区精品久久| 人人妻人人澡人人爽精品日本| 亚洲精品国精品久久99热 | 久9re热视频这里只有精品| 国产一区再线| 精品国产成人| av动漫在线免费观看| 最新国产乱人伦偷精品免费网站| 黄色影院一级片| 青青草97国产精品免费观看无弹窗版| 亚洲精品手机在线观看| 国产精品一级片| 国产精品无码网站| 最新久久zyz资源站| 18精品爽视频在线观看| 色综合久久六月婷婷中文字幕| 一级片在线免费观看视频| 日韩欧美久久一区| 你懂的在线看| 欧美乱妇40p| 新片速递亚洲合集欧美合集| 91日本在线观看| 欧美美女在线直播| 色女人综合av| 亚洲精品孕妇| 天堂中文av在线| 91在线国内视频| av最新在线观看| 岛国av在线不卡| 国产色在线视频| 亚洲精品自产拍| 在线观看三级视频| 国产精品av免费在线观看| 日韩在线观看中文字幕| 欧美国产一二三区| 国产精品99免费看| 婷婷六月天在线| fc2成人免费人成在线观看播放 | av中文一区| 丰满少妇久久久| 国产一区二区在线看| a毛片毛片av永久免费| 亚洲精品视频在线观看网站| 免费又黄又爽又猛大片午夜| 精品国产不卡一区二区三区| 欧美黄色激情| 国产成人在线一区二区| 国产精品毛片视频| 国产麻豆电影在线观看| 三级亚洲高清视频| 玖玖爱在线精品视频| 亚洲乱码日产精品bd| 中文字幕av网站| 亚洲精品视频中文字幕| wwwww亚洲| 成人3d动漫一区二区三区91| 99久久精品国产亚洲精品| 999香蕉视频| 99re66热这里只有精品3直播 | 色国产综合视频| 日色在线视频| 992tv在线成人免费观看| 91在线一区| www.69av| 国产一区二区毛片| 视频国产一区二区| 欧美影院一区二区三区| 青青草av免费在线观看| 7777kkkk成人观看| 国产日韩三级| 久久精品无码中文字幕| 国产精品资源在线看| 国产黄色小视频网站| 欧美乱妇20p| 九色porny丨首页在线| 成人精品视频在线| 我不卡伦不卡影院| 国产精品久久久久久久av福利| 中文一区在线播放| 在线观看国产小视频| 色狠狠久久aa北条麻妃| 欧美日韩破处视频| 在线观看一区二区三区三州| 韩国av一区二区三区在线观看| 91制片厂在线| 欧美一级艳片视频免费观看| 亚洲图区一区| 国产精品免费观看高清| 亚洲日产国产精品| 亚洲成人日韩在线| 色8久久人人97超碰香蕉987| 国产成人天天5g影院在线观看| 国产精品久久久久久久app | 屁屁影院ccyy国产第一页| 粉嫩av一区二区三区在线播放| 国产无遮无挡120秒| 日韩电影网在线| 搜成人激情视频| 中文字幕色一区二区 | 人妻丰满熟妇aⅴ无码| 色综合婷婷久久| 日韩黄色影院| 99国产高清| 久久激情一区| 国产在线观看免费视频软件| 日韩一卡二卡三卡四卡| av剧情在线观看| 日本在线免费观看一区| 韩国三级在线一区| 日本一级淫片色费放| 亚洲人成自拍网站| 高清一区二区中文字幕| 可以看毛片的网址| 国产欧美日本一区视频| 亚洲性生活大片| 欧美激情影音先锋| 国内精品久久久久久久久电影网| 九九热免费在线观看| 午夜一区二区三区视频| 国产黄在线看| 99在线高清视频在线播放| 亚洲尤物精选| 日韩在线观看免| 日韩国产精品视频| 91丨精品丨国产| 噜噜噜久久亚洲精品国产品麻豆| 国产精品理伦片| 亚州av在线播放| 91网站在线免费观看| 国产精品亚洲产品| 卡通动漫亚洲综合| 亚洲欧美国产一区二区三区| 欧美片网站免费| 国产91色在线观看| 午夜精品福利一区二区三区av |