中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

提升代碼的可讀性系列--基礎(chǔ)篇

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

編程是一門藝術(shù)活,好的代碼應(yīng)該就像住的房子一樣,有整體的框架,有門,有窗戶,相互獨(dú)立又完美組合。你覺得門不夠結(jié)實(shí),就拆下來換個(gè)實(shí)心的;你覺得窗戶不夠明亮就換個(gè)全玻璃的,總之對房子的其他部位沒有任何影響。所以說每一個(gè)程序員都應(yīng)該有一顆設(shè)計(jì)師的心。本文主要從編碼、變量處理錯(cuò)誤、對象等基礎(chǔ)方面進(jìn)行簡單的探討,希望能對大家的工作有所幫助~~

1 編碼風(fēng)格

老生常談,我們先從最基礎(chǔ)的編碼說起吧!好的編碼規(guī)范不僅僅能夠提升代碼的可讀性與可維護(hù)性,提高團(tuán)隊(duì)的工作效率,也能夠避開一些低級的錯(cuò)誤,減少bug的隱患,提升程序員的自我修養(yǎng)。編碼雖小,但卻是萬丈高樓的基礎(chǔ),對于編寫清晰連貫的代碼來說,每一個(gè)字符都是非常重要的。以下部分編碼規(guī)范參考自凹凸實(shí)驗(yàn)室。

1.1 縮進(jìn)

通常使用四個(gè)空格進(jìn)行代碼縮進(jìn),有些也用tab來縮進(jìn),這主要根據(jù)團(tuán)隊(duì)的風(fēng)格跟個(gè)人喜好

1.2 空格

  • 左括號與類名之間一個(gè)空格
  • 冒號與屬性值之間一個(gè)空格
  • 操作符前后
  • 匿名函數(shù)表達(dá)式之后等

1.3 空行

這是一個(gè)容易被大家忽略的點(diǎn),但它所帶來的效果是毋庸置疑的!通常一段代碼的語義和另一段代碼不相關(guān),就應(yīng)該用空行隔開,避免一大段的代碼揉在一起,比如

  • 在方法之間;
  • 方法中的局部變量和第一條語句之間;
  • 注釋之前
  • 方法內(nèi)的邏輯片段之間

1.4 命名約定

有一位大師曾說過,計(jì)算機(jī)科學(xué)只存在兩個(gè)難題:緩存命名。由此可見命名不僅是一門科學(xué),也是一門技術(shù)。
通常情況下,變量與函數(shù)一般使用駝峰大小寫命名法,其中為了區(qū)分變量與函數(shù),變量命名前綴應(yīng)當(dāng)是名詞,函數(shù)前綴應(yīng)當(dāng)是動(dòng)詞,也就是說我們應(yīng)當(dāng)讓命名承載一定的含義,因此要避免使用沒有意義的命名。

1.4 注釋

通常我們在編寫完一段代碼的短時(shí)間內(nèi),會(huì)清楚這段代碼的工作原理。但是當(dāng)過一段時(shí)間再次回到代碼中,可能會(huì)花很長的時(shí)間才能讀懂。這種情況下,編寫注釋就變得尤為重要了。

2 變量

首先說一說全局變量存在哪些的問題吧!命名沖突測試難度大、深耦合等等。在創(chuàng)建變量的時(shí)候,我們應(yīng)該注意以下幾個(gè)方面

2.1 避免隱性的創(chuàng)建全局變量

什么是隱性的全局變量呢?官方的回答是:任何變量,如果未經(jīng)聲明,就為全局對象所有。啥意思呢?其實(shí)就是沒有加var聲明的,請看下面的例子

function obj() {  name = "aotu";  return name; } 

另外一種容易創(chuàng)建隱形全局變量的情況就是var聲明的鏈?zhǔn)劫x值,如下代碼所示

function person() {  var a = b = 1; } 


以上這段代碼的執(zhí)行結(jié)果是:a是局部變量,b是全局變量,主要原因是從右至左的操作符優(yōu)先級,它實(shí)際執(zhí)行的結(jié)果等同于

var a = ( b = 0 ); 


綜上所述,隱式全局變量并不是我們平時(shí)用var聲明的變量,而是全局對象的屬性,既然是屬性,那么它可以通過delete操作符刪除,但變量不可以,且在ES5 strict以上會(huì)拋出錯(cuò)誤。

2.2 在函數(shù)頂部聲明變量

在javascript中,聲明變量有一個(gè)“提升”的概念,即無論在函數(shù)哪里聲明,效果都等同于在函數(shù)頂部進(jìn)行聲明。所以我們統(tǒng)一把變量在函數(shù)頂部聲明,既有利于可讀性與可維護(hù)行,也不易出錯(cuò)。

2.3 使用單一var模式

var a = 1,  b = 1,  c = 1; 

這樣聲明的變量不僅可讀性好,而且可以防止變量在定義前就被使用的邏輯錯(cuò)誤,且編碼更少。

2.4 單全局變量方式

雖然全局變量的容易污染命名空間,但有些功能的需要,難以避免使用,關(guān)鍵是我們應(yīng)該做到避免全局變量超出我們的掌控,最佳的方法是依賴盡可能少的全局變量。我們可以使用單全局變量的方式來開啟我們的項(xiàng)目,這種方式在許多的javascript類庫中都有這樣使用。如jQuery,它定義了兩個(gè)全局變量$和jQuery。

3 UI松耦合

什么是松耦合?當(dāng)修改一個(gè)組件的邏輯,而對另一個(gè)組件沒有影響,就說這叫松耦合。通常一個(gè)大型的web應(yīng)用,都是由多人共同開發(fā)維護(hù),這時(shí)候松耦合顯得至關(guān)重要,假如你修改了某一處的代碼而影響了團(tuán)隊(duì)其他人的功能,這是非常不友好的。通常我們主要注意以下幾點(diǎn)

  • 將javascript從css中抽離,如避免使用css表達(dá)式
  • 將csst從javascrip中抽離,如避免使用javascript直接修改css,最佳的方法是操作css的className;
  • 將javascript從HTML中抽離,如避免將函數(shù)直接嵌入到html執(zhí)行,我們應(yīng)該盡量做到將所有的js代碼都放入外置文件中,確保
    html中不會(huì)有內(nèi)聯(lián)的js代碼。
  • 將html從javascript中抽離,如避免在js中拼接html結(jié)構(gòu),我們可以用模板引擎,也可以使用Vue、React等。

4 錯(cuò)誤處理

4.1 為什么要拋出錯(cuò)誤?

在javascript開發(fā)中,總是會(huì)悄無聲息的出現(xiàn)一些超出我們預(yù)期的,攜帶的信息稀少的,隱晦含糊的bug,讓我們措手不及,大大增加了我們調(diào)試錯(cuò)誤、定位錯(cuò)誤的難度,影響開發(fā)效率。假設(shè)錯(cuò)誤中包含這樣的信息:“由于某某情況,導(dǎo)致某某函數(shù)執(zhí)行錯(cuò)誤”,那么是不是馬上就可以開始調(diào)試而不用花大量的時(shí)候去定位錯(cuò)誤?

4.2 何時(shí)拋出錯(cuò)誤?

主要是辨識(shí)代碼中哪些部分在特定的情況下最后可能導(dǎo)致錯(cuò)誤,這里的錯(cuò)誤通常都是我們在思考的過程中的一些可預(yù)期的錯(cuò)誤。

4.3 怎樣拋出錯(cuò)誤?

4.3.1 使用try-catch

將可能引發(fā)錯(cuò)誤的代碼放在try塊中,處理錯(cuò)誤的代碼放在catch中,如


try {  someMethod(); } catch (ex) {  catchError(ex); } 


也可以增加一個(gè)finally塊,這里需注意的是finally塊中的代碼塊不管是否有錯(cuò)誤發(fā)生,最后都會(huì)被執(zhí)行。

4.3.2 throw

當(dāng)我們能清晰的捕捉到錯(cuò)誤的時(shí)候,最好的做法就是拋出這個(gè)錯(cuò)誤,避免在不經(jīng)意的時(shí)候又遇到它,讓大家尷尬。這里需注意的是當(dāng)遇到throw操作符時(shí),代碼會(huì)立即停止執(zhí)行

 
throw new Error("method(): descdescdesc"); 


也可以自定義一個(gè)錯(cuò)誤類型,總之就是盡可能用最短的字符描述清楚

throw {   name: "myErrorType",  message: "arguments must be a DOM element",  errorMethod: errorMethod } 


5 創(chuàng)建對象

5.1 對象字面量

所謂的對象字面量其實(shí)就是我們通常所說的鍵值對哈希表,這種方式不僅富有表現(xiàn)力,可讀性好,且字符更短,沒有作用域解析。它的語法規(guī)則如下

  • 對象包裝在大括號中
  • 逗號分隔屬性和方法
  • 用冒號分隔屬性名稱和屬性的值
    var obj = {  name: "aotu",  job: "farmer",  getName: function () {  return this.name;  } }  //調(diào)用方式 obj.getName(); 
實(shí)現(xiàn)私有屬性

以上例子的name、job屬性都是可直接訪問的。有些時(shí)候我們可能想實(shí)現(xiàn)一些私有的屬性,然后提供一個(gè)公有的接口來對外訪問。雖然javascript并沒有特殊的語法來表示私有、公共屬性和方法,但是可以通過匿名閉包來實(shí)現(xiàn),內(nèi)部的任意變量都不會(huì)暴露,來看以下代碼


var obj;   (function () {  //這樣就能實(shí)現(xiàn)私有成員  var name = "aotu",  job = "farmer";    obj = {  getName: function () {  return name;  }  } }()) 


更優(yōu)雅的寫法

var obj = (function () {  var name = "aotu",  job = "farmer";   return {  getName: function () {  return name;  }  } }()); 


這種寫法也是模塊模式的基礎(chǔ)框架,后續(xù)會(huì)有詳細(xì)介紹。

熟悉了這種模式之后它還有很多種玩法,比如可以像jQuery這樣鏈?zhǔn)秸{(diào)用:“$(‘#id’).siblings(‘ul’).find(“l(fā)i”).addClass();


var obj = {  num: 0,  add: function (arg) {  this.num += arg;  return this;  },  red: function (arg) {  this.num -= arg;  return this;  },  setTotal: function () {  console.log(this.num);  } };  //調(diào)用方式 obj.add(5).red(2).setTotal(); //3 


5.2 構(gòu)造函數(shù)

我們先來看看構(gòu)造函數(shù)的基礎(chǔ)框架

function Obj() {  //公有屬性  this.name = "aotu";  this.job = "farmer";    //公有方法  this.getName = function () {  console.log(this.name);  }  }  //調(diào)用方式 var obj = new Obj(); obj.getName(); 


在使用new方式實(shí)例化構(gòu)造函數(shù)通常會(huì)經(jīng)歷以下幾個(gè)步驟

  • 創(chuàng)建一個(gè)對象并且this變量引用了該對象,且繼承了該對象的原型
  • 屬性和方法被加入到this引用的對象中
  • 隱式的返回新對象
忘記使用NEW的情況

當(dāng)然我們有時(shí)候會(huì)忘記使用new操作符的實(shí)例化的情況,然而這并不會(huì)導(dǎo)致語法錯(cuò)誤,但構(gòu)造函數(shù)的this指向了全局對象,可能會(huì)發(fā)生邏輯錯(cuò)誤或者意外,來看下面執(zhí)行的結(jié)果


var obj = Obj(); obj.getName(); //Cannot read property 'getInfo' of undefined 


為了避免這種意外發(fā)生,我們也可以在構(gòu)造函數(shù)中檢查this是否為構(gòu)造函數(shù)的一個(gè)實(shí)例,強(qiáng)制使用new操作符,繼續(xù)看下面的例子


function Obj() {  if(!(this instanceof Obj)){  return new Obj();  }   this.name = "aotu";  this.age = 25;   this.getName = function () {  console.log(this.name);  }  } 

再看執(zhí)行的結(jié)果

var obj = Obj(); obj.getName(); //"aotu" 


靜態(tài)成員

在javascript中,并沒有特殊的語法來表示靜態(tài)成員,但我們可以為構(gòu)造函數(shù)添加屬性這種方式來實(shí)現(xiàn)這種語法,請看下面的例子

//構(gòu)造函數(shù) function Obj() {}  
//添加靜態(tài)方法 Obj.getAge = function () {  console.log(25);  }  
//注意這里的調(diào)用方式 Obj.getAge(); //25  //如果使用實(shí)例對象調(diào)用 obj.getAge();
 //Object #<Obj> has no method 'getAge' 


這里大家需要注意調(diào)用靜態(tài)方法的方式,若以實(shí)例對象調(diào)用一個(gè)靜態(tài)方法是無法正常運(yùn)行的,反之同理。

私有屬性與方法

在以上例子中構(gòu)造函數(shù)的屬性與方法都屬于公有方法,我們也可以給構(gòu)造函數(shù)添加私有方法與私有屬性

function Obj() {
    this.name = "auto";
    this.age = 25;
     
    //私有屬性
    var address = "sz",
        that = this;
      
    //私有方法
    function getAddress() {
        console.log(that.address);
    }
       
    this.getName = function () {
        console.log(this.name);
    } 
}

構(gòu)造函數(shù)存在的問題

構(gòu)造函數(shù)的主要問題就是當(dāng)多次實(shí)例化這個(gè)構(gòu)造函數(shù)的時(shí)候,每個(gè)方法都會(huì)重新創(chuàng)建一遍,這樣就等于在內(nèi)存中的拷貝。解決問題的第一種思路就是將函數(shù)中的方法通過函數(shù)定義轉(zhuǎn)移到函數(shù)外面,并將指針傳遞給構(gòu)造函數(shù),來看下面的例子

function Obj() {
    this.name = "aotu";
    this.age = 25;

    //將指針賦給getName
    this.getName = getName;
}
  
function getName () {
    console.log(this.name);
}   

var obj1 = new Obj();
var obj2 = new Obj()

雖然也解決了以上的問題,但并沒有達(dá)到封裝的效果。接下來我們引入原型prototype的概念。

5.3 原型模式

每一個(gè)構(gòu)造函數(shù)都有一個(gè)原型prototype,原型對象包含一個(gè)指向構(gòu)造函數(shù)的指針,這個(gè)指針指向一個(gè)可以由特定類型的所有實(shí)例共享的屬性和方法,所以使用原型對象可以讓所有對象實(shí)例共享它的屬性和方法,來看下面的例子

function Obj() {}

Obj.prototype.name = "aotu";
Obj.prototype.age = 25;
Obj.prototype.getName = function () {
    console.log(this.name);
}

//調(diào)用方式

var obj1 = new Obj();
obj1.getName() //"aotu"

var obj2 = new Obj();
obj2.getName() //"aotu"

alert(obj1.getName == obj2.getName); //true

由此可見obj1 和 obj2 訪問的是同一個(gè)getName函數(shù)

更好的寫法

我們可以將所有的原型都寫在一個(gè)對象字面量里,這樣整個(gè)代碼看起來更加簡潔清晰,繼續(xù)往下看

function Obj() {}  Obj.prototype = {  name: "aotu",  age: 25,  getName: function () {  return this.name;  } } 


使用字面量的方式需注意的問題

在使用這種字面量的方式的時(shí)候需注意以下兩點(diǎn)

1.將prototype設(shè)置為等于一個(gè)對象字面量形式創(chuàng)建的對象,它本質(zhì)上已經(jīng)完全重寫了默認(rèn)的prototype對象,最終結(jié)果雖然相同但是其constructor屬性不再指向該對象。

constructor是個(gè)什么鬼?在默認(rèn)情況下,所有原型對象都會(huì)自動(dòng)獲得一個(gè)constructor,它指向prototype屬性所在函數(shù)的指針,換句話說這個(gè)constructor就是指這個(gè)構(gòu)造函數(shù)。以上代碼執(zhí)行結(jié)果如下所示

 
var obj= new Obj(); alert(obj.cnstructor == Obj) //false; 


我們可以在重寫prototype的時(shí)候給constructor指定構(gòu)造函數(shù),接著往下看

function Obj(){}

Obj.prototype = {
    constructor: Obj,
    name: "aotu",
    age: 25,
    getName: function () {
        return this.name;
    }
}

var obj= new Obj();
alert(obj.cnstructor == Obj) //true;

2.當(dāng)我們重寫整個(gè)原型的時(shí)候如果先創(chuàng)建了實(shí)例,就會(huì)切斷構(gòu)造函數(shù)與原型之間的聯(lián)系,因?yàn)?strong>實(shí)例的指針僅僅指向原型,而不是構(gòu)造函數(shù),在實(shí)際的操作過程中,應(yīng)該盡量避免這種錯(cuò)誤

function Obj() { }

var obj = new Obj();

Obj.prototype = {
    constructor: Obj,
    name: "aotu",
    age: 25,
    getName: function () {
        return this.name;
    }
}

obj.getName();  //error

組合使用二者

在我們的具體應(yīng)用中,通常比較多的是組合使用構(gòu)造函數(shù)模式與原型模式。構(gòu)造函數(shù)用于定義實(shí)例屬性,原型用于定于共享的屬性和方法,這樣能夠最大限度的節(jié)省內(nèi)存。以下是一個(gè)基本的組合使用構(gòu)造函數(shù)與原型的例子

function Obj(){
    if(!(this instanceof Obj)){
        return new Obj();
    }

    this.name = "aotu";
    this.age = 25;
}

Obj.prototype = {
    constructor: Obj,
    getName: function () {
        return this.name;
    }
}

var obj = Obj();
obj.getName();

5.4 模塊模式

模塊模式是一種非常通用的模式,也是使用頻率比較高的模式,它具有以下幾個(gè)特點(diǎn)

  • 模塊化
  • 可復(fù)用
  • 松耦合
  • 區(qū)分了私有方法與公共方法

我們先看看模塊模式的基礎(chǔ)框架

var testModule = function () {
    //私有成員
    var testNode = document.getElementById("test");

    //也可在此定義私有方法
    function privateMethod() {
        console.log("this is Private method!");
    }

    return {
        //對外公開的方法
        setHtml: function (txt) {
            testNode.innerHTML = txt;
        }
    }
}

//調(diào)用方式
var testModule = new testModule();
testModule.setHtml("Hello");

這種方式看起來比較清晰、簡潔,但就是每次調(diào)用的時(shí)候都需要用new來實(shí)例化,我們知道每個(gè)實(shí)例在內(nèi)存里都是一份拷貝。如何解決這個(gè)問題呢?…我們可以采用一個(gè)匿名閉包來完美的解決這個(gè)問題。

(function () {  //將所有的變量和function放在這里聲明,其作用域也只能在這個(gè)匿名閉包里面,既達(dá)到了封裝的目的,也能防止命名沖突 }()) 


接下來我們將它應(yīng)用到具體的實(shí)例中,以下就是一個(gè)基本的Module模式

var testModule =(function () {
    var my = {},
        testNode = document.getElementById("test");
     
    my.setHtml = function(txt) {
        testNode.innerHTML = txt;
    }
    
    return my;
} ())

//調(diào)用方式
testModule.setHtml("Hello");

通常在一個(gè)大型的項(xiàng)目中,會(huì)有多人共同開發(fā)一個(gè)功能的情況,這個(gè)時(shí)候我們可以運(yùn)用這種模式將全局變量當(dāng)作參數(shù)傳遞,然后通過變量返回,從而達(dá)到多人協(xié)作的目的。

var testModule =(function (my) {
    var testNode = document.getElementById("test");
     
    my.setHtml = function(txt) {
        testNode.innerHTML = txt;
    }
    
    return my;
} (testModule || {}))

我們也可以通過這個(gè)模式將私有的對象或者屬性保護(hù)起來,然后設(shè)置一些公共接口對外訪問,繼續(xù)來看下面的代碼

var testModule =(function () {
    var testNode = document.getElementById("test"),
     
        setHtml = function(txt) {
            testNode.innerHTML = txt;
        };

        //設(shè)置公共調(diào)用方法
        return {
            setHtml: setHtml
        }
    } ())

以上幾種方式僅僅只是一些創(chuàng)建對象的基礎(chǔ),通過靈活運(yùn)用這些基礎(chǔ),可以變換出傳說中各種各樣的模式,如迭代器模式、工廠模式、裝飾者模式等,對于后續(xù)學(xué)習(xí)其他的技術(shù)也是極有幫助的,

React:

var MyTitle = React.createClass({
    getDefaultProps : function () {
        return {
            title : 'Hello World'
        };
    },

    render: function() {
        return <h1> {this.props.title} </h1>;
    }
});

Vue:

new Vue({
    el: '#app',
    data: {
        message: 'Hello Vue.js!'
    },
    methods: {
        reverseMessage: function () {
            this.message = this.message.split('').reverse().join('')
        }
    }
})

以上就是本期的所有內(nèi)容,如有錯(cuò)漏,懇請指正,大家共同進(jìn)步!在下一期中,會(huì)繼續(xù)跟大家探討更多好玩的東西,敬請期待~~~

6 參考資料

《編寫可維護(hù)的JavaScript》[美] Nicholas C. Zakas 著
《JavaScript設(shè)計(jì)模式》[美] Addy Osmani 著
《JavaScript高級程序設(shè)計(jì)(第3版)》
博文:深入理解JavaScript系列

原文:凹凸實(shí)驗(yàn)室(http://aotu.io/notes/2016/03/31/readable/)

標(biāo)簽: 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:成為Java高手的25個(gè)學(xué)習(xí)要點(diǎn)

下一篇:Android從按下開機(jī)鍵到啟動(dòng)發(fā)生了什么