那些年,我們一起寫過的JavaScript


它的出生,并不高貴。

1995年,Web尚處于嬰兒期,絕大數(shù)計算機(jī)用戶還不能告訴你網(wǎng)站到底為何物,而且在當(dāng)時開發(fā)一個網(wǎng)站也是很困難的一件事。微軟當(dāng)時也是剛剛意識到互聯(lián)網(wǎng)的重要性,Google在當(dāng)時也只是在Little Rascals中杜撰的一詞。

1996年早期,JavaScript創(chuàng)建不久,被提交到ECMA進(jìn)行標(biāo)準(zhǔn)化。它涵蓋了JavaScript的核心語法和DOM 0級的一個子集,并且大多數(shù)瀏覽器是以某種形式實現(xiàn)這個規(guī)范。我們很少聽到有人說ECMAScript,主要是因為它的名字太長了,不方便記憶,但是它和JavaScript是一回事。

其實JavaScript成功的原因很簡單,你不需要擔(dān)心數(shù)據(jù)類型,只要用var就可以聲明一個變量了,往往優(yōu)勢的背后總是隱藏著劣勢,在過去,沒有人把它當(dāng)作面向?qū)ο蟮模悴挥每紤]層級之間的關(guān)系,可以很快的上手,門檻很低,這就導(dǎo)致了問題的出現(xiàn)。

不幸的是,JavaScript的童年是沒有掌聲和鮮花的,活的很委屈。大量的高危安全嚴(yán)重?fù)p害了它早期的名望。更重要的是大多程序員對它的態(tài)度都是消極,認(rèn)為JavaScript缺乏開發(fā)工具,離開了瀏覽器就不能開發(fā)了,在他們眼中就是一個"腳本玩具",JavaScript在編程世界里面就想一只“丑小鴨”。

“對,人們對JavaScript的批評一點也沒有錯,它開始認(rèn)識到自己的缺點,并且它決心要不斷的變好。”

故事結(jié)束,其實JavaScript的遭遇真的比我們有些人還要慘。

一段老式的瀏覽器嗅探程例

function Redirect(){
    var WhatBrowser;
    var WhatVersion;
    //瀏覽器的版本
    WhatVersion=navigator.appVersion.toUpperCase();
    //瀏覽器的類型
    WhatBrowser=navigator.appName.toUpperCase();
    
    if(WhatBrowser.indexOf("MICROSOFT")>=0){
        if(WhatVersion.indexOf("3")>=0){
            console.log("MICROSOFT3");
        }else{
            console.log("MICROSOFT");
        }
    }
    if(WhatBrowser.indexOf("NETSCAPE")>=0){
        if (WhatVersion.indexOf("2")) {
            console.log("NETSCAPE2")
        }else{
            console.log("NETSCAPE");
        }
    }
}

這種嗅探瀏覽器版本的代碼流行了很長一段時間。也就意味著,對于不同對的瀏覽器,你需要采取不同的策略,我們需要做兩個版本,一個是為IE設(shè)計的,另一個是為NETSCAPE設(shè)計的,這顯然是兼容的好方法。但在很長的時間它確實是唯一的方法。


JavaScript可以很慢,對,沒錯,直至今日,你也可以很容易寫出性能很差的代碼。

 <!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>性能很差的JavaScript舉例</title>
   <script type="text/javascript">
       function badTest(){
           var startTime = new Date().valueOf();
           var s ="";
           for(var i=0;i<10000000;i++){
               s+="This is a test string";
           }
           return new Date().valueOf() - startTime;
               
       }
       
       function goodTest(){
           var startTime = new Date().valueOf();
           var stringBuffer = new Array();
           for(var i=0;i<10000000;i++){
               stringBuffer.push("This is a test string");
           }
           var s = stringBuffer.join("");
           return new Date().valueOf() - startTime;
       }
       
       function betterTest(){
           var startTime = new Date().valueOf();
           var stringBuffer = new Array();
           for(var i=0;i<10000000;i++){
               stringBuffer[stringBuffer.length]="This is a test string";
           }
           var s = stringBuffer.join("");
           return new Date().valueOf() - startTime;
       }
       
       function doTests(){
           var htm="";
           htm+="Time badTest took:"+badTest()+"<br>";
           htm+="Time goodTest took:"+goodTest()+"<br>";
           htm+="Time betterTest took:"+betterTest()+"<br>";
           document.getElementById("result").innerHTML=htm;
       }
       
   </script>
</head>
<body>
   <a href="javascript:void(0);" onClick="doTests();">Click here to test</a>
   <div id="result"></div>
<body>
</html>

在IE中的結(jié)果是:


IE.png

從數(shù)字上大家應(yīng)該有個很直觀的感受,也就是使用不同的方法去實現(xiàn)同樣的一種功能,所花的時間上面是有明顯的差別的。這里我使用的瀏覽器已經(jīng)是比較新的版本了,想想以前不同代碼之間所花時間的差距,自己腦補(bǔ)。


萬惡的根源:開發(fā)者!

與語言本身需要進(jìn)化一樣,開發(fā)者同樣需要不斷進(jìn)化。他們需要知道什么好用什么不好用,并且開發(fā)人員需要自己努力去尋找比較好的方法。如果你正在使用Java,那么很有確切地知道使用字符串拼接是件壞事。而字符串緩沖區(qū)是你的好朋友。但是在JavaScript中,沒有字符緩沖區(qū)。

不夠高效的編碼示例


function Person1(firstName,lastName){
    this.firstName = firstName;
    this.lastName=lastName;
    this.toString=function(){
        return this.firstName+" "+this.lastName;
    }
}

function Person2(attrs){
    this.firstName=attrs["firstName"];
    this.lastName=attrs["lastName"];
    this.toString=function(){
        return this.firstName+" "+this.lastName;
    }
}

function showPerson(){
    var p1 = new Person1("Frank","Zammetti");
    var p2 = new Person2({"firstName":"Frank","lastName":"Zammetti"});
    document.getElementById("divPerson").innerHTML =p1+"<br>" +p2;

}
這里我們有倆個不同的函數(shù)來表示人:person1和person2,Person1的構(gòu)造函數(shù)接受兩個參數(shù),firstName和lastName。Person2接受一個單獨(dú)的數(shù)組參數(shù)attrs,是屬性的數(shù)組。showPerson函數(shù)創(chuàng)建了兩個同樣的人。一個使用Person1,另一個使用Person2,當(dāng)我們想添加別的屬性來描述一個人會發(fā)生什么呢?對于Person1 ,我們?yōu)闃?gòu)造函數(shù)增加更多的參數(shù)。對于Person2,只需要添加適當(dāng)?shù)淖侄卧O(shè)置代碼,這里的差別就在于程序的可擴(kuò)展性問題。其實person1不僅可擴(kuò)展性差,可讀性也差;通過調(diào)用var p1 = new Person1("Frank","Zammetti");你知道frank,Zammetti這個字符串什么含義嗎?而person2在調(diào)用構(gòu)造函數(shù)你便可以知道frank是這個人的名,zammetti是這個人的型。

JavaScript缺乏塊級作用域的示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JavaScript沒有塊級作用域</title>
    <script type="text/javascript">
        function test(){
            var i=1;
            if(1){
                var i=2;
                if(1){
                    var i=3;
                    //網(wǎng)頁中彈窗
                    alert(i);
                }
                alert(i);
            }alert(i);
            
        }
    </script>
</head>
<body onLoad="test();">
    //正常如果有塊級作用域的編程語言,結(jié)果應(yīng)該321,而對于JavaScript,結(jié)果是333
</body>
</html>

上面所有的一切,確實只是沒有經(jīng)驗的開發(fā)人員會干的事情,因為他們還不知道如何做得更好,這并不是JavaScript的錯。這個鍋,它不背。


不斷的進(jìn)化:接近可用性

在第一批相對沒有經(jīng)驗的開發(fā)人員使用JavaScript之后,新一輪開始隱現(xiàn),人們認(rèn)識到一些普遍存在的錯誤并開始修正。
最重要的是,有經(jīng)驗的開發(fā)人員已經(jīng)看到JavaScript的能力并開始用自己聰明才智來改造它,正是因為這樣的人,才有了今天的JavaScript。

編寫代碼的好習(xí)慣,增加代碼的可讀性

  • 寫代碼需要有縮進(jìn)
  • 參數(shù)名稱要具有描述性
  • 引號要使用一致,不要一會雙引號,一會單引號
  • 結(jié)尾最好加上分號
  • 統(tǒng)一使用花括號,可能代碼只有一行
  • 函數(shù)的名字需要賦予實際意義
  • 在函數(shù)前后需要加上注釋
  • 注意轉(zhuǎn)義字符和拼寫

另外一個重要的概念是外部的JavaScript文件,也就是說,不要把結(jié)構(gòu),樣式,邏輯分開,有自己單獨(dú)的文件,不要全部嵌套在一起,分開便于后期的修改和擴(kuò)展,也便于代碼的重復(fù)使用。


專業(yè)的JavaScript

在過去的幾年里,主流的瀏覽器已經(jīng)達(dá)到共識,相互之間基本上都能兼容JavaScript,所以現(xiàn)在很少寫分支代碼。你現(xiàn)在可能會發(fā)現(xiàn)的問題其實與JavaScript無關(guān),那都是Dom惹的禍。

假設(shè)我們想在文檔上掛一個KeyDown的鉤子。我們可以這做document.onkeydown=KeyDown;但在Firefox中,你必須這么做:document.captureEvent(Event.KEYDOWN);兩個瀏覽器基本的keydown()函數(shù)的原型都是:function keyDown(e){};

看一個更為復(fù)雜的例子:

  document.onkeydown=KeyDown;
  if(document.layers){
        document.captureEvent(Event.KEYDOWN);
  }
  function keyDown(e){
            var ev = (e)?e:(window.event)?window.event:null;
            if(ev){
                      return (ev.charCode)?ev.charCode:
                        ((ev.keyCode)?ev.keyCode:((ev.which)?ev.which:null));
                    }
            return -1;
  } 

這段代碼兼容任何瀏覽器。第二行的第一個if只在非IE瀏覽器中為true,那些瀏覽器的document對象會有l(wèi)ayers屬性。這個時候,調(diào)用captureEvent();然后再keyDown()的內(nèi)部,第一行會把ev設(shè)置為傳入的參數(shù)e。如果document.layers屬性不存在,則使用window.event。但是如果尤銘設(shè)置window.event,那么則ev設(shè)置為null。然后如果設(shè)置了ev,則找出鍵值:分別看看里面對象是charCode還是keyCode,如果ev是null,則返回-1;


面向?qū)ο蟮腏avaScript

大多數(shù)程序員的生活從發(fā)現(xiàn)原型(prototype)那天開始就改變了。一旦他們發(fā)現(xiàn)所有JavaScript對象都可以通過原型來擴(kuò)展,并且這個功能允許他們創(chuàng)建自定義的類,那么一切都變了。

var answer = 0;
function addNum(num1,num2){
    answer =num2+ num1;
}
function subNum(num1,num2){
    answer =num1 - num2;
}
function multiNum(num1,num2){
    answer=num1 *num2;
}
function divideNum(num1,num2){
    if(num2){
        answer=num1/num2;
    }else{
        answer=0;
    }
        
}

這段代碼,從語法和功能上來說,沒有任何問題,無非就是進(jìn)行一些簡單的算術(shù)運(yùn)算。但是它組織的夠好嗎?我看不怎么樣。在全局作用域里answer變量代碼味太濃,每個函數(shù)都是如此,都是孤立的函數(shù),都是全局作用域里面。我們要避免全局污染。在任何編程中,使用全局變量會被認(rèn)為是一個壞習(xí)慣,因為不在全局變量范圍內(nèi),我們就可以隨意的去修改它。缺乏結(jié)構(gòu)意味著函數(shù)與函數(shù)之間沒有結(jié)構(gòu)關(guān)系,并且沒有邏輯的分組幫助人們從更高級別去理解它。
快來使用面向?qū)ο蟮乃枷氚桑?/p>

面向?qū)ο螅訉I(yè)

function NumberFunctions(){
    var answer = 0;
}
    
NumberFunctions.prototype.addNumbers = function (num1,num2){
    this.answer =num2+ num1;
}
NumberFunctions.prototype.subtractNumbers = function (num1,num2){
    this.answer =num1 - num2;
}
NumberFunctions.prototype.multiplyNumbers = function (num1,num2){
    this.answer=num1 *num2;
}
NumberFunctions.prototype.divideNumbers = function (num1,num2){
    if(num2){
        this.answer=num1/num2;
    }else{
        this.answer=0;
    }
        
}
NumberFunctions.prototype.toString =function(){
    return this.answer;
}

測試代碼

// 使用構(gòu)造函數(shù)與創(chuàng)建對象
var nf = new NumberFunctions();
//調(diào)用這個對象的方法
nf.addNumbers(2,1);
//通過控制來查看結(jié)果
console.log(nf):
//同理
nf.multiplyNumbers(4,5);
console.log(nf);

結(jié)果:


answer.png

這樣寫的優(yōu)點

  • 沒有對全局變量的污染
  • 所有函數(shù)實際上都是NumberFunctions類的成員,因此構(gòu)造了一個清晰的關(guān)系
  • 基本的面向?qū)ο螅簲?shù)據(jù)和操作的函數(shù)都封裝的很好

這里再提一個概念“柔性衰減”(graceful degradation):它是針對某個版本的瀏覽器設(shè)計的網(wǎng)頁應(yīng)該對舊的瀏覽器采取柔性衰減的策略:即使是不夠好的,也至少能用。同樣的概念適用在JavaScript中。

--------------------End--------------------
我就是番茄,愛生活,愛學(xué)習(xí),愛寵物的家伙!
如果覺得寫的還行,記得打賞哦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,247評論 6 543
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,520評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,362評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,805評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,541評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,896評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,887評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,062評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,608評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,356評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,555評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,077評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,769評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,175評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,489評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,289評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,516評論 2 379

推薦閱讀更多精彩內(nèi)容