亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

如何編寫高質量JS代碼

2019-11-20 13:35:01
字體:
來源:轉載
供稿:網友

想寫出高效的javascript類庫卻無從下手;

嘗試閱讀別人的類庫,卻理解得似懂給懂;

打算好好鉆研js高級函數,但權威書上的內容太零散,

即使記住“用法”,但到要“用”的時候卻沒有想“法”。

也許你和我一樣,好像有一顧無形的力量約束著我們的計劃,讓我們一再認為知識面的局限性,致使我們原地踏步,難以向前跨越。

這段時間,各種作業、課程設計、實驗報告,壓力倍增。難得擠出一點點時間,絕不睡懶覺,整理總結往日所看的書,只為了可以離寫自己的類庫近一點。

本文參考自《javascript語言精粹》和《Effective JavaScript》。例子都被調試過,理解過后,我想把一些“深奧”的道理說得淺顯一點點。

1.變量作用域

作用域對于程序員來說就像氧氣。它無處不在,甚至,你往往不會去想他。但當它被污染時(例如使用全局對象),你會感覺到窒息(例如應用響應變慢)。javascript核心作用域規則很簡單,被精心設計,且很強大。有效地使用javascript需要掌握變量作用域的一些基本概念,并了解一些可能導致難以捉摸的、令人討厭的問題的極端情況。

1.1盡量少用全局變量

javascript很容易在全局命名空間中創建變量。創建全局變量毫不費力,因為它不需要任何形式的聲明,而且能被整個程序的所有代碼自動地訪問。

對于我們這些初學者,遇到某些需求(例如,傳輸的數據被記錄下來、等待某時機某函數調用時使用;或者是某函數被經常使用)時,好不猶豫想到全局函數,甚至大一學到的C語言面向過程思想太根深蒂固,系統整整齊齊地都是滿滿函數。定義全局變量會污染共享的公共命名空間,并可能導致意外的命名沖突。全局變量也不利于模塊化,因為它會導致程序中獨立組件間的不必要耦合。嚴重地說,過多的全局(包括樣式表,直接定義div或者a的樣式),整合到多人開發過稱將會成為災難性錯誤。這就是為什么jQuery的所有代碼都被包裹在一個立即執行的匿名表達式――自調用匿名函數。當瀏覽器加載完jQuery文件后,自調用匿名函數立即開始執行,初始化jQuery的各個模塊,避免破壞和污染全局變量以至于影響到其他代碼。

復制代碼 代碼如下:

(function(window,undefined){
    var jQuery = ...
    //...
    window.jQuery = window.$ = jQuery;
})(window);

另外,你或許會認為,“先怎么怎么寫,日后再整理”比較方便,但優秀的程序員會不斷地留意程序的結構、持續地歸類相關的功能以及分離不相關的組件,并這些行為作為編程過稱中的一部分。

 由于全局命名空間是javascript程序中獨立的組件經行交互的唯一途徑,因此,利用全局命名控件的情況是不可避免的。組件或程序庫不得不定義一些全局變量。以便程序中的其他部分使用。否則最好使用局部變量。

復制代碼 代碼如下:

this.foo ;//undefined
foo = " global foo";
this.foo ;//"global foo"
var foo = "global foo";
this.foo = "changed";
foo ;//changed

javascript的全局命名空間也被暴露在程序全局作用域中可以訪問的全局對象,該對象作為this關鍵字的初始值。在web瀏覽器中,全局對象被綁定在全局window變量。這就意味你創建全局變量有兩種方法:在全局作用域內使用var聲明他,或者將其加入到全局對象中。使用var聲明的好處是能清晰地表達全局變量在程序范圍中的影響。

鑒于引用為綁定的全局變量會導致運行時錯誤,因此,保存作用域清晰和簡潔會使代碼的使用者更容易理解程序聲明了那些全局變量。

由于全局對象提供了全局環境的動態反應機制,所以可以使用它查詢一個運行環境,檢測在這個平臺下哪些特性可用。

eg.ES5引入了一個全局的JSON對象來讀寫JSON格式的數據。

復制代碼 代碼如下:

if(!this.JSON){
   this.JSON = {
         parse : ..,
         stringify : ...   
    } 
}

 如果你提供了JSON的實現,你當然可以簡單無條件地使用自己的實現。但是由宿主環境提供的內置實現幾乎更適合的,因為它們是用C語言寫進瀏覽器的。因為它們按照一定的標準對正確性和一致性進行了嚴格檢查,并且普遍來說比第三方實現提供更好的性能。

當初數據結構課程設計模擬串的基本操作,要求不能使用語言本身提供的方法。javascript對數組的基本操作實現得很好,如果只是出于一般的學習需要,模擬語言本身提供的方法的想法很好,但是如果真正投入開發,無需考慮第一時間選擇使用javascript內置方法。

1.2避免使用with

with語句提供任何“便利“,讓你的應用變得不可靠和低效率。我們需要對單一對象依次調用一系列方法。使用with語句可以很方便地避免對對象的重復引用:

復制代碼 代碼如下:

function status(info){
    var widget = new Widget();
    with(widget){
           setBackground("blue");
           setForeground("white");
           setText("Status : "+info);
           show();
    } 
}

使用with語句從模塊對象中”導入“(import)變量也是很有誘惑力的。

復制代碼 代碼如下:

function f(x,y){
   with(Math){
         return min(round(x),sqrt(y));//抽象引用
   }
}

事實上,javascript對待所有的變量都是相同的。javascript從最內層的作用域開始向外查找變量。with語言對待一個對象猶如該對象代表一個變量作用域,因此,在with代碼塊的內部,變量查找從搜索給定的變量名的屬性開始。如果在這個對象中沒有找到該屬性,則繼續在外部作用域中搜索。with塊中的每個外部變量的引用都隱式地假設在with對象(以及它的任何原型對象)中沒有同名的屬性。而在程序的其他地方創建或修改with對象或其原型對象不一定會遵循這樣的假設。javascript引擎當然不會讀取局部代碼來獲取你使用了那些局部變量。javascript作用域可被表示為高效的內部數據結構,變量查找會非??焖?。但是由于with代碼塊需要搜索對象的原型鏈來查找with代碼里的所有變量,因此,其運行速度遠遠低于一般的代碼塊。

替代with語言,簡單的做法,是將對象綁定在一個簡短的變量名上。

復制代碼 代碼如下:

function status(info){
    var w = new Widget();
   
     w.setBackground("blue");
     w.setForeground("white");
     w.setText("Status : "+info);
     w.show();
  
}

其他情況下,最好的方法是將局部變量顯式地綁定到相關的屬性上。

復制代碼 代碼如下:

function f(x,y){
    var    min    = Math.min,
           round  = Math.round,
           sqrt   = Math.sqrt; 
     return min(round(x),sqrt(y));
}

1.3熟練掌握閉包

理解閉包有單個概念:

a)javascript允許你引用在當前函數以外定義的變量。

復制代碼 代碼如下:

function makeSandwich(){
   var magicIngredient = "peanut butter";
   function make(filling){
        return magicIngredient + " and " + filling;
   }
   return make("jelly"); 
}
makeSandwich();// "peanut butter and jelly"

b)即使外部函數已經返回,當前函數仍然可以引用在外部函數所定義的變量

復制代碼 代碼如下:

function makeSandwich(){
   var magicIngredient = "peanut butter";
   function make(filling){
        return magicIngredient + " and " + filling;
   }
   return make; 
}
var f = sandwichMaker();
f("jelly");                      // "peanut butter and jelly"
f("bananas");               // "peanut butter and bananas"
f("mallows");               // "peanut butter and mallows"

javascriptd的函數值包含了比調用它們時所執行所需要的代碼還要多的信息。而且,javascript函數值還在內部存儲它們可能會引用的定義在其封閉作用域的變量。那些在其所涵蓋的作用域內跟蹤變量的函數被稱為閉包。

 make函數就是一個閉包,其代碼引用了兩個外部變量:magicIngredient和filling。每當make函數被調用時,其代碼都能引用這兩個變量,因為閉包存儲了這兩個變量。

函數可以引用在其作用域內的任何變量,包括參數和外部函數變量。我們可以利用這一點來編寫更加通用的sandwichMaker函數。

復制代碼 代碼如下:

function makeSandwich(magicIngredient){
   function make(filling){
        return magicIngredient + " and " + filling;
   }
   return make; 
}
var f = sandwichMaker(”ham“);
f("cheese");                      // "ham and cheese"
f("mustard");               // "ham and mustard"

閉包是javascript最優雅、最有表現力的特性之一,也是許多習慣用法的核心。

c)閉包可以更新外部變量的值。事實上,閉包存儲的是外部變量的引用,而不是它們的值的副本。因此,對于任何具有訪問這些外部變量的閉包,都可以進行更新。

復制代碼 代碼如下:

function box(){
    var val = undefined;
    return {
         set : function(newval) {val = newval;},
         get : function (){return val;},
         type : function(){return typeof val;}
    };
}
var b = box();
b.type(); //undefined
b.set(98.6);
b.get();//98.6
b.type();//number

該例子產生一個包含三個閉包的對象。這三個閉包是set,type和get屬性,它們都共享訪問val變量,set閉包更新val的值。隨后調用get和type查看更新的結果。

1.4理解變量聲明提升

javascript支持此法作用域(對變量foo的引用會被綁定到聲明foo變量最近的作用域中),但不支持塊級作用域(變量定義的作用域并不是離其最近的封閉語句或代碼塊)。

不明白這個特性將會導致一些微妙的bug:

復制代碼 代碼如下:

function isWinner(player,others){
    var highest = 0;
    for(var i = 0,n = others.length ;i<n;i++){
          var player = others[i];
          if(player.score > highest){
                   highest = player.score;
          }
    }
    return player.score > highest;
}

1.5 當心命名函數表達式笨拙的作用域

復制代碼 代碼如下:

function double(x){ return x*2; }
var f = function(x){ return x*2; }

同一段函數代碼也可以作為一個表達式,卻具有截然不同的含義。匿名函數和命名函數表達式的官方區別在于后者會綁定到與其函數名相同的變量上,該變量作為該函數的一個局部變量。這可以用來寫遞歸函數表達式。

復制代碼 代碼如下:

var f = function find(tree,key){
  //....
  return find(tree.left , key) ||
             find(tree.right,key);   
}

值得注意的是,變量find的作用域只在其自身函數中,不像函數聲明,命名函數表達式不能通過其內部的函數名在外部被引用。

復制代碼 代碼如下:

find(myTree,"foo");//error : find is not defined;
var constructor = function(){ return null; }
var f= function(){
    return constructor();
};
f();//{}(in ES3 environments)

該程序看起來會產生null,但其實會產生一個新的對象。

因為命名函數變量作用域內繼承了Object.prototype.constructor(即Oject的構造函數),就像with語句一樣,這個作用域會因Object.prototype的動態改變而受到影響。在系統中避免對象污染函數表達式作用域的辦法是避免任何時候在Object.prototype中添加屬性,以避免使用任何與標準Object.prototype屬性同名的局部變量。

    在流行的javascript引擎中另外一個缺點是對命名函數表達式的聲明進行提升。

復制代碼 代碼如下:

var f = function g(){return 17;}
g(); //17 (in nonconformat environment)

一些javascript環境甚至把f和g這兩個函數作為不同的對象,從而導致不必要的內存分配。

1.6 當心局部塊函數聲明笨拙的作用域

復制代碼 代碼如下:

function f() {return "global" ; }
function  test(x){
    function f(){return "local";}
    var result = [];
    if(x){
         result.push(f());
    }     
     result.push(f());
     result result;
}
test(true);   //["local","local"]
test(false);  //["local"]

復制代碼 代碼如下:

function f() {return "global" ; }
function  test(x){
    var result = [];
    if(x){
         function f(){return "local";}
         result.push(f());
    }     
     result.push(f());
     result result;
}
test(true);   //["local","local"]
test(false);  //["local"]

javascript沒有塊級作用域,所以內部函數f的作用域應該是整個test函數。一些javascript環境確實如此,但并不是所有javascript環境都這樣,javascript實現在嚴格模式下將這類函數報告為錯誤(具有局部塊函數聲明的處于嚴格模式下的程序將報告成一個語法錯誤),有助于檢測不可移植代碼,為未來的標準版本在給局部塊函數聲明給更明智和可以的語義。針對這種情況,可以考慮在test函數內聲明一局部變量指向全局函數f。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91在线播放国产| 欧美成人午夜影院| 狠狠色狠狠色综合日日小说| 国产免费一区二区三区在线能观看| 91精品国产综合久久久久久久久| 亚洲第一区在线观看| 久久久久久久久久久亚洲| 欧美国产日韩一区二区| 97视频在线免费观看| 精品亚洲夜色av98在线观看| 亚洲女同性videos| 在线精品视频视频中文字幕| 91久久精品在线| 精品久久久久久久久久国产| 国产不卡av在线免费观看| 亚洲人成亚洲人成在线观看| 欧美激情一级欧美精品| 亚洲毛片在线看| 国产高清在线不卡| 国产精品偷伦视频免费观看国产| 91精品国产九九九久久久亚洲| 亚洲精品久久视频| 亚洲视频在线视频| 国产成人avxxxxx在线看| 欧美日韩加勒比精品一区| 国产亚洲成精品久久| 成人激情视频在线观看| 中文字幕最新精品| 国产精品白丝av嫩草影院| 日韩中文字幕精品| 粉嫩av一区二区三区免费野| 成人精品视频99在线观看免费| 91精品国产高清自在线看超| 久久精品欧美视频| 亚洲国产成人久久综合| 久久久精品在线| 成人精品一区二区三区| 91精品国产成人| 国产精品福利在线观看| 大荫蒂欧美视频另类xxxx| 宅男66日本亚洲欧美视频| 久久99国产综合精品女同| 久久久天堂国产精品女人| 色综合久久精品亚洲国产| 日韩激情av在线播放| 亚洲日本欧美日韩高观看| 欧美激情一级欧美精品| 国产主播在线一区| 亚洲va欧美va国产综合久久| 5566成人精品视频免费| 欧美激情视频一区二区| 九九久久精品一区| 国产精品视频久| 亚洲国产精彩中文乱码av| 岛国精品视频在线播放| 2021久久精品国产99国产精品| 国产香蕉97碰碰久久人人| 91av在线国产| 欧美国产欧美亚洲国产日韩mv天天看完整| 亚洲精品美女久久久| 久久久久北条麻妃免费看| 日韩欧美国产一区二区| 国产一区二区免费| 国内揄拍国内精品| 亚洲男人天堂2019| 日韩美女视频免费在线观看| 欧美三级欧美成人高清www| 一本一道久久a久久精品逆3p| 欧美激情一区二区三区久久久| 亚洲欧美日韩国产精品| 亚洲电影免费观看高清| 最近2019中文字幕在线高清| 亚洲自拍在线观看| 国产精品国产福利国产秒拍| 国产欧美亚洲精品| 亚洲国产欧美一区| 精品欧美一区二区三区| 国产香蕉精品视频一区二区三区| 日韩中文字幕视频在线| 国产成人短视频| 亚洲精品白浆高清久久久久久| 中文字幕在线看视频国产欧美在线看完整| 日韩精品在线免费播放| 精品无人区太爽高潮在线播放| 亚洲一区二区三区在线免费观看| 久久精品国产亚洲精品2020| 亚洲欧美日本伦理| 久久久久久久色| 中文字幕在线成人| 久久久久久久久久久av| 亚洲二区中文字幕| 国产精自产拍久久久久久| 欧美午夜精品久久久久久久| 中文字幕av一区中文字幕天堂| 欧洲精品毛片网站| 欧美又大又粗又长| 97精品伊人久久久大香线蕉| 久久人人爽人人爽人人片亚洲| 久久精品在线播放| 久久久久久91香蕉国产| 国产在线视频2019最新视频| 亚洲一级免费视频| 国产成人精品网站| 久久久精品国产亚洲| 久久久久日韩精品久久久男男| 久久视频免费在线播放| 影音先锋欧美在线资源| 日韩小视频网址| 欧美成人剧情片在线观看| 亚洲人精选亚洲人成在线| 国产精品黄色av| 免费97视频在线精品国自产拍| 神马久久桃色视频| 亚洲国产婷婷香蕉久久久久久| 国产精品香蕉av| 不卡av在线网站| 欧美成人四级hd版| 亚洲天堂久久av| 久久国产精品久久久久久久久久| 夜夜嗨av色综合久久久综合网| 一区二区亚洲欧洲国产日韩| 久久天堂电影网| 欧美高跟鞋交xxxxxhd| 欧美激情亚洲精品| 亚洲最大福利视频网站| 久久国内精品一国内精品| 亚洲福利视频专区| 日产精品99久久久久久| 欧美日韩精品在线播放| 97热在线精品视频在线观看| 992tv成人免费影院| 国产精品久久久久高潮| 久久人人爽人人爽爽久久| 亚洲天堂2020| 亚洲在线视频观看| 日本成人免费在线| 亚洲精品狠狠操| 成人做爰www免费看视频网站| 欧美多人爱爱视频网站| 青草青草久热精品视频在线观看| 欧美床上激情在线观看| 国产精品劲爆视频| 91香蕉嫩草影院入口| 亚洲精品电影网| 91黄色8090| 欧美性猛交xxxx乱大交3| 久久影院免费观看| 色婷婷av一区二区三区在线观看| 日本久久91av| 亚洲精品aⅴ中文字幕乱码| 成人激情在线观看| 精品国产一区二区三区久久狼5月| 日韩欧美成人区| 国产99久久精品一区二区| 日韩欧美成人精品| 97视频com| 亚洲最大的网站| 国产一区玩具在线观看| 国产精品免费视频xxxx| 色妞欧美日韩在线| 亚洲欧美国产另类| 国产a∨精品一区二区三区不卡| 日韩av免费在线看| 国产精品高潮粉嫩av|