日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

前端要给力之:代码可以有多烂?

發布時間:2025/3/21 HTML 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 前端要给力之:代码可以有多烂? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1、爛代碼是怎么定義的?

!KissyUI是淘寶Kissy這個前端項目的一個群,龍藏同學在看完我在公司內網的“讀爛代碼系列”之后就在群里問呵:爛代碼是怎么定義的?

是呵,到底什么才算爛代碼呢?這讓我想到一件事,是另一個網友在gtalk上問我的一個問題:他需要a,b,c三個條件全真時為假,全假時也為假,請問如何判斷。

接下來KissyUI群里的同學給出了很多答案:


[javascript]?view plain?copy
  • //?1.?圓心??
  • if(?a&&b&&c?||?!a&&!b&&!c){??
  • ????return?false??
  • }??
  • //?2.?龍藏??
  • (a?^?b)?&?c??
  • //?3.?愚公(我給gtalk上的提問者)的答案??
  • (a?xor?b)?or?(a?xor?c)??
  • //?4.?提問者自己的想法??
  • (a?+?b?+?c)?%?3??
  • //?5.?云謙對答案4的改進版本??
  • (!!a+!!b+!!c)%n??
  • //?6.?拔赤??
  • a???(b?c:b)?:?(b?!b:!c)??
  • //?7.?吳英杰??
  • (a?!=?b?||?b?!=?c)??
  • 或??
  • (!a?!=?!b?||?!b?!=?!c)??
  • //?8.?姬光??
  • var?v?=?a&&b&&c;??
  • if(!v){??
  • ???return?false;??
  • }else?if(v){??
  • ???return?false;??
  • }else{??
  • ???return?true;??
  • }??

  • en... 確實,我沒有完全驗證上面的全面答案的有效性。因為如同龍藏后來強調的:“貌似我們是要討論什么是爛代碼?”的確,我們怎么才能把代碼寫爛呢?上面出現了種種奇異代碼,包括原來提問者的那個取巧的:

    [javascript]?view plain?copy
  • //?4.?提問者自己的想法??
  • (a?+?b?+?c)?%?3??


  • 因為這個問題出現在js里面,存在弱類型的問題,即a、b、c可能是整數,或字符串等等,因此(a+b+c)%3這個路子就行不通了,所以才有了

    [javascript]?view plain?copy
  • //?5.?云謙對答案4的改進版本??
  • (!!a+!!b+!!c)%n??
  •  

     
    2、問題的泛化與求解:普通級別

    如果把上面的問題改變一下:

    ?- 如果不是a、b、c三個條件,而是兩個以上條件呢?

    ?- 如果強調a、b、c本身不一定是布爾值呢?

    那么這個問題的基本抽象就是:

    [c-sharp]?view plain?copy
  • //?v0,對任意多個運算元求xor??
  • function?e_xor()?{?...?}??
  • ??
  • 對于這個e_xor()來說,最直接的代碼寫法是:??
  • //?v1,掃描所有參數,發現不同的即返回true,全部相同則返回false。??
  • function?e_xor()?{??
  • ??var?args=arguments,?argn=args.length;??
  • ??args[0]?=?!args[0];??
  • ??for?(var?i=1;?i<argn;?i++)?{??
  • ????if?(args[0]?!=?!args[i])?return?true;??
  • ??}??
  • ??return?false;??
  • }??
  • 接下來,我們考慮一個問題,既然arguments就是一個數組,那么可否使用數組方式呢?事實上,據說在某些js環境中,直接存取arguments[x]的效率是較差的。因此,上面的v1版本可以有一個改版:

    [javascript]?view plain?copy
  • //?v1.1,對v1的改版??
  • function?e_xor()?{??
  • ??var?args=[].slice.call(arguments,0),?argn=args.length;??
  • ??...??
  • }??


  • 這段小小的代碼涉及到splice/slice的使用問題。因為操作的是arguments,因此splice可能導致函數入口的“奇異”變化,在不同的引擎中的表現效果并不一致,而slice則又可能導致多出一倍的數據復制。在這里仍然選用slice()的原因是:這里畢竟只是函數參數,不會是“極大量的”數組,因此無需過度考慮存儲問題。

    ?

    3、問題的泛化與求解:專業級別

    接下來,我們既然在args中得到的是一個數組,那么再用for循環就實在不那么摩登了。正確的、流行風格的、不被前端鄙視做法是:

    [javascript]?view plain?copy
  • //?v2,使用js1.6+的數組方法的實現??
  • function?e_xor(a)?{??
  • ??return?([].slice.call(arguments,1)).some(function(b)?{?if?(!b?!=?!a)?return?true?});??
  • }??


  • 為了向一些不太了解js1.6+新特性的同學解釋v2這個版本,下面的代碼分解了上述這個實現:

    [javascript]?view plain?copy
  • //?v2.1,對v2的詳細分解??
  • function?e_xor(a)?{??
  • ??var?args?=?[].slice.call(arguments,1);??
  • ??var?callback?=?function(b)?{??
  • ????if?(!b?!=?!a)?return?true??
  • ??}??
  • ??return?args.some(callback);??
  • }??


  • some()這個方法會將數組args中的每一個元素作為參數b傳給callback函數。some()有一項特性正是與我們的原始需求一致的:

    ? - 當callback()返回true的時候,some()會中斷args的列舉然后返回true值;否則,

    ? - 當列舉完全部元素且callback()未返回true的情況下,some()返回false值。

    現在再讀v2版本的e_xor(),是不是就清晰了?

    ?

    當然,僅僅出于減少!a運算的必要,v2版本也可以有如下的一個改版:

    [javascript]?view plain?copy
  • //?v2.2,對v2的優化以減少!a運算次數??
  • function?e_xor(a)?{??
  • ??return?(a=!a,?[].slice.call(arguments,1)).some(function(b)?{?if?(!b?!=?a)?return?true?});??
  • }??


  • 在這行代碼里,使用了連續運算:

    [javascript]?view plain?copy
  • (a=!a,?[].slice.call(arguments,1))??
  • 而連續運算返回最后一個子表達式的值,即slice()后的數組。這樣的寫法,主要是要將代碼控制在“一個表達式”。

    ?

    ?

    4、問題的泛化與求解:Guy入門級別

    好了,現在我們開始v3版本的寫法了。為什么呢?因為v2版本仍然不夠酷,v2版本使用的是Array.some(),這個在js1.6中擴展的特既不是那么的“函數式”,還有些面向對象的痕跡。作為一個函數式語言的死忠,我認為,類似于“列舉一個數組”這樣的問題的最正常解法是:遞歸。

    為什么呢?因為erlang這樣的純函數式語言就不會搞出個Array.some()的思路來——當然也是有這樣的方法的,只是從“更純正”的角度上講,我們得自己寫一個。呵呵。這種“純正的遞歸”在js里面又怎么搞呢?大概的原型會是這樣子:

    [javascript]?view plain?copy
  • //?v3,采用純函數式的、遞歸方案的框架??
  • function?e_xor(a,?b)?{??...?}??


  • 在這個框架里,我們設e_xor()有無數個參數,但每次我們只處理a,b兩個,如果a,b相等,則我們將其中之任一,與后續的n-2個參數遞歸比較。為了實現“遞歸處理后續n-2個參數”,我們需要借用函數式語言中的一個重要概念:連續/延續(continuous)。這個東東月影曾經出專題來講過,在這里:

    http://bbs.51js.com/viewthread.php?tid=85325

    簡單地說,延續就是對函數參數進行連續的回調。這個東東呢,在較新的函數式語言范式中都是支持的。為了本文中的這個例子,我單獨地寫個版本來分析之。我稱之為tail()方法,意思是指定函數參數的尾部,它被設計為函數Function上的一個原型方法。

    [javascript]?view plain?copy
  • Function.prototype.tail?=?function()?{??
  • ??return?this.apply(this,?[].slice.call(arguments,0).concat([].slice.call(this.arguments,?this.length)));??
  • }??


  • 注意這個tail()方法的有趣之處:它用到了this.length。在javascript中的函數有兩個length值,一個是foo.length,它表明foo函數在聲明時的形式參數的個數;另一個是arguments.length,它表明在函數調用時,傳入的實際參數的個數。也就是說,對于函數foo()來說:

    [javascript]?view plain?copy
  • function?foo(a,?b)?{??
  • ??alert([arguments.length,?arguments.callee.length]);??
  • }??
  • foo(x);??
  • foo(x,y,z);??


  • 第一次調用將顯示[1,2],第二次則會顯示[3,2]。無論如何,聲明時的參數a,b總是兩個,所以foo.length == arguments.callee.length == 2。

    回到tail()方法。它的意思是說:

    [javascript]?view plain?copy
  • Function.prototype.tail?=?function()?{??
  • ??return?this.apply(?//?重新調用函數自身??
  • ????this,?//?以函數foo自身作為this?Object??
  • ????[].slice.call(arguments,0)?//?取調用tail時的全部參數,轉換為數組??
  • ????.concat(?//?數組連接??
  • ??????[].slice.call(this.arguments,?//?取本次函數foo調用時的參數,由于tail()總在foo()中調用,因此實際是取最近一次foo()的實際參數??
  • ????????this.length)??//?按照foo()聲明時的形式參數個數,截取foo()函數參數的尾部??
  • ????)??
  • ??);??
  • }??


  • 那么tail()在本例中如何使用呢?

    [javascript]?view plain?copy
  • //?v3.1,使用tail()的版本??
  • function?e_xor(a,?b)?{??
  • ??if?(arguments.length?==?arguments.callee.length)?return?!a?!=?!b;??
  • ??return?(!a?==?!b???arguments.callee.tail(b)?:?true);??
  • }??


  • 這里又用到了arguments.callee.length來判斷形式參數個數。也就是說,遞歸的結束條件是:只剩下a,b兩個參數,無需再掃描tail()部分。當然,return中三元表達式(?:)右半部分也會中止遞歸,這種情況下,是已經找到了一個不相同的條件。

    在這個例子中,我們將e_xor()寫成了一個尾遞歸的函數,這個尾遞歸是函數式的精髓了,只可惜在js里面不支持它的優化。WUWU~~ 回頭我查查資源,看看新的chrome v8是不是支持了。v8同學,尚V5否?:)

    ?

    5、問題的泛化與求解:Guy進階級別

    從上一個小節中,我們看到了Guy解決問題的思路。但是在這個級別上,第一步的抽象通常是最關鍵的。簡單地說,V3里認為:

    [javascript]?view plain?copy
  • //?v3,采用純函數式的、遞歸方案的框架??
  • function?e_xor(a,?b)?{??...?}??


  • 這個框架抽象本身可能是有問題。正確的理解不是“a,b求異或”,而是“a跟其它元素求異或”。由此,v4的框架抽象是:

    [javascript]?view plain?copy
  • //?v4,更優的函數式框架抽象,對接口的思考??
  • function?e_xor(a)?{??...?}??


  • 在v3中,由于每次要向后續部分傳入b值,因此我們需要在tail()中做數組拼接concat()。但是,當我們使用v4的框架時,b值本身就隱含在后續部分中,因此無需拼接。這樣一來,tail()就有了新的寫法——事實上,這更符合tail()的原意,如果真的存在拼接過程,那它更應由foo()來處理,而不是由tail()來處理。

    [javascript]?view plain?copy
  • //?更符合原始抽象含義的tail方法??
  • Function.prototype.tail?=?function()?{??
  • ??return?this.apply(this,?[].slice.call(this.arguments,?this.length));??
  • }??


  • 在v4這個版本中的代碼寫法,會變得更為簡單:

    [javascript]?view plain?copy
  • //?v4.1,相較于v3更為簡單的實現??
  • function?e_xor(a)?{??
  • ??if?(arguments.length?<?2)?return?false;??
  • ??return?(!a?==?!arguments[1]???arguments.callee.tail()?:?true);??
  • }??
  • //?v4.1.1,一個不使用三元表達式的簡潔版本??
  • function?e_xor(a)?{??
  • ??if?(arguments.length?<?2)?return?false;??
  • ??if?(!arguments[1]?!=?!a)?return?true;??
  • ??return?arguments.callee.tail();??
  • }??
  • ?

    6、問題的泛化與求解:Guy無階級別

    所謂無階級別,就是你知道他是Guy,但不知道可以Guy到什么程度。例如,我們可以在v4.1版本的e_xor()中發現一個模式,即:

    ? - 真正的處理邏輯只有第二行。

    由于其它都是框架部分,所以我們可以考慮一種編程范式,它是對tail的擴展,目的是對在tail調用e_xor——就好象對數組調用sort()方法一樣。tail的含義是取數據,而新擴展的含義是數組與邏輯都作為整體。例如:

    [javascript]?view plain?copy
  • //?在函數原型上擴展的tailed方法,用于作參數的尾部化處理??
  • Function.prototype.tailed?=?function()?{??
  • ??return?function(f)?{??//?將函數this通過參數f保留在閉包上??
  • ????return?function()?{??//?tailed()之后的、可調用的e_xor()函數??
  • ??????if?(arguments.length?<?f.length+1)?return?false;??
  • ??????if?(f.apply(this,?arguments))?return?true;??//?調用tailed()之前的函數f??
  • ??????return?arguments.callee.apply(this,?[].slice.call(arguments,?f.length));??
  • ????}??
  • ??}(this)??
  • }??
  • tailed()的用法很簡單:

    [javascript]?view plain?copy
  • e_xor?=?function(a){??
  • ??if?(!arguments[1]?!=?!a)?return?true;??
  • }.tailed();??
  • 簡單的來看,我們可以將xor函數作為tailed()的運算元,這樣一樣,我們可以公開一個名為tailed的公共庫,它的核心就是暴露一組類似于xor的函數,開發者可以使用下面的編程范式來實現運算。例如:

    [javascript]?view plain?copy
  • /*?tiny?tailed?library,?v0.0.0.1?alpha.?by?aimingoo.?*/??
  • Function.prototype.tailed?=?....;??
  • //?對參數a及其后的所有參數求異或??
  • function?xor(a)?{??
  • ??if?(!arguments[1]?!=?!a)?return?true;??
  • }??
  • //?...更多類似的庫函數??


  • 那么,這個所謂的tailed庫該如何用呢?很簡單,一行代碼:

    [javascript]?view plain?copy
  • //?求任意多個參數的xor值??
  • xor.tailed()(a,b,c,d,e,f,g);??
  • ?

    現在我們得到了一個半成熟的、名為tailed的開放庫。所謂半成熟,是因為我們的tailed()還有一個小小缺陷,下面這行代碼:

    [javascript]?view plain?copy
  • if?(arguments.length?<?f.length+1)?return?false;??
  • ?

    中間的f.length+1的這個“1”,是一個有條件的參數,它與xor處理數據的方式有關。簡單的說,正是因為要比較a與arguments[1],所這里要+1,如果某種算法要比較 多個運算元,則tailed()就不通用了。所以正確的、完善的tailed應該允許調用者指定終止條件。例如:

    [javascript]?view plain?copy
  • //?less_one()作為tailed庫函數中的全局常量,以及缺省的closed條件??
  • //?當less_one返回true時,表明遞歸應該終止??
  • function?less_one(args,?f)??{??
  • ??if?(args.length?<?f.length+1)?return?true;??
  • }??
  • //?在函數原型上擴展的tailed方法,用于作參數的尾部化處理??
  • Function.prototype.tailed?=?function(closed)?{??
  • ??return?function(f)?{??//?將函數this通過參數f保留在閉包上??
  • ????return?function()?{??//?tailed()之后的、可調用的e_xor()函數??
  • ??????if?((closed||less_one).apply(this,?[arguments,f]))?return?false;??
  • ??????if?(f.apply(this,?arguments))?return?true;??//?調用tailed()之前的函數f??
  • ??????return?arguments.callee.apply(this,?[].slice.call(arguments,?f.length));??
  • ????}??
  • ??}(this)??
  • }??

  • 使用的方法仍然是:

    [javascript]?view plain?copy
  • xor.tailed()(a,b,c,d,e,f,g);??
  • //?或者??
  • xor.tailed(less_one)(a,b,c,d,e,f,g);??
  • ?

    在不同的運算中,less_one()可以是其它的終止條件。

    ?

    現在,在這個方案——我的意思是tailed library這個庫夠Guy了嗎?不。所謂意淫無止盡,淫人們自有不同的淫法。比如,在上面的代碼中我們可以看到一個問題,就是tailed()中有很多層次的函數閉包,這意味著調用時效率與存儲空間都存在著無謂的消耗。那么,有什么辦法呢?比如說?哈哈,我們可以搞搞范型編程,弄個模板出來:

    [javascript]?view plain?copy
  • /*?tiny?tailed?library?with?templet?framework,?v0.0.0.1?beta.?by?aimingoo.?*/??
  • Function.prototype.templeted?=?function(args)?{??
  • ??var?buff?=?['[',?,'][0]'];??
  • ??buff[1]?=?this.toString().replace(/_([^_]*)_/g,?function($0,$1)?{?return?args[$1]||'_'});??
  • ??return?eval(buff.join(''));??
  • }??
  • function?tailed()?{??
  • ??var?f?=?_execute_;??
  • ??if?(_closed_(arguments,?f))?return?false;??
  • ??if?(f.apply(this,?arguments))?return?true;??
  • ??return?arguments.callee.apply(this,?[].slice.call(arguments,?f.length));??
  • }??
  • function?less_one(args,?f)??{??
  • ??if?(args.length?<?f.length+1)?return?true;??
  • }??
  • function?xor(a)?{??
  • ??if?(!arguments[1]?!=?!a)?return?true;??
  • }??
  • e_xor?=?tailed.templeted({??
  • ??closed:?less_one,??
  • ??execute:?xor??
  • })??


  • 當然,我們仍然可以做得更多。例如這個templet引擎相當的粗糙,使用eval()的方法也不如new Function來得理想等等。關于這個部分,可以再參考QoBean對元語言的處理方式,因為事實上,這后面的部分已經在逼近meta language編程了。

    ?

    7、Guy?

    我們在做什么?我們已經離真相越來越遠了。或者說,我故意地帶大家兜著一個又一個看似有趣,卻又漸漸遠離真相的圈子。

    我們不是要找一段“不那么爛的代碼”嗎?如果是這樣,那么對于a,b,c三個運算條件的判斷,最好的方法大概是:

    [javascript]?view plain?copy
  • (a!=b?||?a!=c)??
  • 或者,如果考慮到a,b,c的類型問題:

    [javascript]?view plain?copy
  • (!a!=!b?||?!a!=!c)??
  • 如果考慮對一組運算元進行判斷的情況,那么就把它當成數組,寫成:

    [javascript]?view plain?copy
  • function?e_xor(a)?{??
  • ??for?(var?na=!a,i=1;?i<arguments.length;?i++)?{??
  • ????if?(!arguments[i]?!=?na)?return?true??
  • ??}??
  • ??return?false;??
  • }??
  • 對于這段代碼,我們使用JS默認對arguments的存取規則,有優化就優化,沒有就算了,因為我們的應用環境并沒有提出“這里的arguments有成千上萬個”或“e_xor()調用極為頻繁”這樣的需求。如果沒有需求,我們在這方面所做的優化,就是白費功能——除了技術上的完美之外,對應用環境毫無意義。

    ?

    夠用了。我們的所學,在應用環境中已經足夠,不要讓技巧在你的代碼中泛濫。所謂技術,是控制代碼復雜性、讓代碼變得優美的一種能力,而不是讓技術本身變得強大或完美。

    ?

    所以,我此前在“讀爛代碼”系統中討論時,強調的其實是三個過程:

    ?- 先把業務的需求想清楚,

    ?- 設計好清晰明確的調用接口,

    ?- 用最簡單的、最短距離的代碼實現。

    ?

    其它神馬滴,都系浮云。

    ?

    =====

    注:本文從第2小節,至第6小節,僅供對架構、框架、庫等方面有興趣的同學學習研究,有志于在語言設計、架構抽象等,或基礎項目中使用相關技術的,歡迎探討,切勿濫用于一般應用項目。


    from:?http://blog.csdn.net/aimingoo/article/details/6036574

    總結

    以上是生活随笔為你收集整理的前端要给力之:代码可以有多烂?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。