前端項目-代碼開發規範(我的整理版)

前端代碼規範

基本的格式化

1.1 縮進層級

  • 使用空格符進行縮進;javascript

  • 四個空格符表示一個縮進層級;php

function demo() {
    console.log('hello world');
}

1.2 語句結尾

  • 使用分號結尾;
// 正確的代碼
let name = 'Nicholas';
function sayName() {
    alert(name);
}
  • 錯誤實例:因ASI的分號插入規則複雜難記,會形成與原代碼不一樣的執行結果;
// 原始代碼
function getData() {
    return
    {
        title: '',
        author: ''
    }
}
// 分析器會理解成
function getData () {
    return;
    {
        title: '',
        author: ''
    };
}

1.3 行的長度

  • 指定一行代碼長度限定在80個字符;

1.4 換行

  • 當一行長度超過最大字符數限制(80個字符),需手動將一行代碼拆成兩行,且下一行增長兩個層級的縮進;
// 正確作法: 在運算符後換行,第二行追加兩個縮進層級
callAFunction(document, element, window, 'some string value', true, 123,
        navigator);

// 很差的作法:第二行只有一個縮進
callAFunction(document, element, window, 'some string value', true, 123,
    navigator);

// 很差的作法:在運算符以前換行了
callAFunction(document, element, window, 'some string value', true, 123
        , navigator);
  • 注意:逗號也算一個運算符,應看成爲前一行當結尾;css

  • 變量賦值時,第二行的位置應當與賦值運算符的位置保持對齊;html

// 正確的作法
let result = something + anotherThing + yetAnotherThing + somethingElse +
             anotherSomethingElse;
// 確保代碼的可讀性,並能一眼看清折行的上下文

1.5 空行

  • 在方法之間加入空行前端

  • 在方法中的局部變量和第一條語句之間加入空行java

  • 在多行或單行註釋以前加入空行node

  • 在方法內的邏輯片斷之間插入空行,提升可讀性react

  • 在方法之間加入空行es6

// 好的寫法
function anther () {

    for (let i = 0; i < wl.length; i++) {

        // 內部邏輯
        p = wl[i];

        if (s.hasOwnProperty(p)) {

            if (merge && type === 'object') {
                y.min(r[p], s[p]);
            } else {
                r[p] = s[p];
            }
        }
    }
}

/*
*多行註釋
*/
function bather() {}

1.6 命名

  • 駝峯式大小寫:小寫字母開始,後續每一個單詞首字母都大寫;
// 好的寫法
let thisIsMyName;
let anotherletiable;
let aVeryLongariableName;

1.6.1 變量和函數

  • 變量名需老是遵照駝峯式大小寫命名法,且命名前綴爲【名詞】,以名詞作前綴可讓變量與函數區分開來;函數名前綴爲【動詞】
// 變量: 好的寫法
let count = 10;
let myName = 'Nicholas';
let found = true;

// 很差的寫法:變量看起來像函數
let getCount = 10;
let isFound = true;

// 函數: 好的寫法
function getName () {
    return myName;
}

// 很差的寫法: 函數看起來像變量
function theName() {
    return myName;
}
  • 對於函數和方法命名,第一個單詞應該式動詞;常見的一些使用動詞的約定
動詞 含義
can 函數返回一個布爾值
has 函數返回一個布爾值
is 函數返回一個布爾值
get 函數返回一個非布爾值
set 函數用來保存一個值

1.6.2 常量

  • 常量使用大寫字母和下劃線來命名,下劃線用以分割單詞
let MAX_COUNT = 10;
let URL = 'http://www.nczonline.net/';

1.6.3 構造函數

  • 構造函數以'駝峯式大小寫'命名,首字母爲大寫,名詞
// 好的作法
function Person(name) {
    this.name = name
}

Person.prototype.sayName = function() {
    alert(this.name);
};

let me = new Person('Nicholas')

1.7 直接量

1.7.1 字符串

  • 靜態字符串一概使用單引號或反引號,不使用雙引號。動態字符串使用反引號。並從頭至尾保持一種風格
// 很差的寫法
const a = "foobar";
const b = 'foo' + a + 'bar';

// 可接受的寫法
const c = `foobar`;

// 好的寫法
const a = 'foobar';
const b = `foo${a}bar`;
const c = 'foobar';

1.7.2 數字

  • 在js中不可省略小數部分或者整數部分
// 整數
let count = 10;

// 小數
let price = 10.0;
let price = 10.00;

// 不推薦的小數寫法:沒有小數部分
let price = 10.;

//不推薦的小數寫法: 沒有整數部分
let price = .1;

//不推薦的寫法: 八進制寫法已經被棄用了
let num = 010;

// 十六進制寫法
let num = 0xA2

// 科學計數法
let num = 1e23;

1.7.3 null

  • null是個特殊值,切勿與undefind搞混;
  • null用來初始化一個變量,這個變量可能賦值爲一個對象;
  • null用來和一個已經初始化的變量比較,這個變量有多是也可能不是一個對象;
  • 當函數的參數指望是對象時,用做參數傳入;
  • 當函數的返回值指望是對象時,用做返回值傳出;
  • 不要使用null來檢測是否傳入了某個參數;
  • 不要用null來檢測一個未初始化的變量
// 好的用法
let person = null;

// 好的用法
function getPerson() {
    if (condition) {
        return new Person('Nicholas')
    } else {
        return null;
    }
}

// 好的用法
let person = getPerson()
if (person !== null) {
    doSomething();
}

// 很差的寫法: 用來和未初始化的變量比較
let person;
if (person !== null) {
    doSomething();
}

// 很差的寫法: 檢測是否傳入了參數
function doSomething(arg1, agr2, arg3, arg4) {
    if (arg4 !== null) {
        doSomethingElse();
    }
}
  • 理解null最好的方式時將它看成對象的佔位符;

1.7.4 undefined

  • 未被初始化的變量都有一個初始值,即undefined;表示這個變量等待被賦值
// 很差的寫法
let person;
console.log(person === undefined); // true
  • 儘可能避免在代碼中使用undefined,這個值經常與返回'undefined'的typeof運算符混淆。
// foo未被聲明
let person;
console.log(typeof person); // 'undefined'
console.log(typeof foo);    // 'undefined'
  • 經過禁止使用特殊值 undefined, 能夠有效的保證只有一種狀況下typeof纔會返回'undefined'。若是使用了一個可能(或不可能)賦值爲一個對象的變量時,則將其賦值爲null。
// 好的作法
let person = null;
console.log(person === null);   // true

1.7.5 對象直接量

  • 對象直接量容許將全部的屬性都括在一對花括號中;
// 好的寫法
let book = {
    title: '',
    author: ''
}

// 很差的寫法
let book = new Object();
book.title = '';
book.author = '';
  • 單行定義的對象,最後一個成員不以逗號結尾。多行定義的對象,最後一個成員以逗號結尾。
// bad
const a = { k1: v1, k2: v2, };
const b = {
  k1: v1,
  k2: v2
};

// good
const a = { k1: v1, k2: v2 };
const b = {
  k1: v1,
  k2: v2,
};
  • 對象儘可能靜態化,一旦定義,就不得隨意添加新的屬性。若是添加屬性不可避免,要使用Object.assign方法。
// bad
const a = {};
a.x = 3;

// if reshape unavoidable
const a = {};
Object.assign(a, { x: 3 });

// good
const a = { x: null };
a.x = 3;
  • 若是對象的屬性名是動態的,能夠在創造對象的時候,使用屬性表達式定義。
// bad
const obj = {
  id: 5,
  name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
  id: 5,
  name: 'San Francisco',
  [getKey('enabled')]: true,
};
  • 上面代碼中,對象obj的最後一個屬性名,須要計算獲得。這時最好採用屬性表達式,在新建obj的時候,將該屬性與其餘屬性定義在一塊兒。這樣一來,全部屬性就在一個地方定義了。
  • 另外,對象的屬性和方法,儘可能採用簡潔表達法,這樣易於描述和書寫。
var ref = 'some value';

// bad
const atom = {
  ref: ref,

  value: 1,

  addValue: function (value) {
    return atom.value + value;
  },
};

// good
const atom = {
  ref,

  value: 1,

  addValue(value) {
    return atom.value + value;
  },
};

1.7.6 數組直接量

  • 使用兩個方括號將數組初始元素括起來,來替代Array的方式建立數組;
// 好的寫法
let colors = ['red', 'green', 'blur'];
let numbers = [1,2,3,4];

// 很差的寫法
let color = new Array('red', 'green', 'blur')
let numbers = new Array(1,2,3,4);
  • 使用擴展運算符(...)拷貝數組。
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];
  • 使用 Array.from 方法,將相似數組的對象轉爲數組。
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);

1.8 ES6解構賦值

  • 使用數組成員對變量賦值時,優先使用解構賦值。
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;
  • 函數的參數若是是對象的成員,優先使用解構賦值。
// bad
function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;
}

// good
function getFullName(obj) {
  const { firstName, lastName } = obj;
}

// best
function getFullName({ firstName, lastName }) {
}
  • 若是函數返回多個值,優先使用對象的解構賦值,而不是數組的解構賦值。這樣便於之後添加返回值,以及更改返回值的順序。
// bad
function processInput(input) {
  return [left, right, top, bottom];
}

// good
function processInput(input) {
  return { left, right, top, bottom };
}

const { left, right } = processInput(input);

2. 註釋

  • 代碼晦澀難懂
  • 可能被誤認爲錯誤的代碼
  • 必要但不明顯的針對特定瀏覽器的代碼
  • 對於對象、方法或者屬性,生成文檔是有必要的(使用恰當的文檔註釋)

2.1 單行註釋

  • 獨佔一行,用來解釋下一行代碼,且縮進層級和下一行代碼保持一致;
  • 在代碼行的尾部的註釋,代碼結束到註釋之間至少有一個縮進,且不超過單行最大字符數限制,若是超過,則放置在代碼行的上方;
// 好的寫法
if (condition) {

    // 代碼執行邏輯
    allowed();
}

// 很差的寫法:註釋以前沒有空行
if (condition) {
    // 代碼邏輯
    allowed();
}

// 很差的寫法: 錯誤的縮進
if (condition) {
// 代碼執行邏輯
    allowed();
}

// 好的寫法
let result = something + somethingElse; // 代碼執行邏輯

// 很差的寫法: 代碼和註釋之間沒有間隔;
let result = something + somethingElse;// 代碼執行邏輯

2.2 多行註釋

  • 多行註釋以前應當有一個空行,且縮進層級和其描述的代碼保持一致;
// 好的寫法
if (condition) {

    /*
    * 代碼執行邏輯
    * 代碼執行邏輯
    */
    allowed();
}

2.3 使用註釋

  • 當代碼不夠清晰時添加註釋,當代碼很明瞭時不該當添加註釋;
  • 添加註釋的通常原則是:在須要讓代碼變的更清晰時添加註釋;
// 很差的寫法

// 初始化count
let count = 10;

// 好的寫法

//改變這個值可能會讓它變成青蛙
let count = 10;

3. 語句和表達式

  • 在JavaScript中,不論塊語句包含多行代碼仍是單行代碼,都應該使用花括號
// 好的寫法
if (condition) {
    doSomething();
}

// 很差的寫法
if(condition)
    doSomething();

// 很差的寫法
if(condition) doSomething();

// 很差的寫法
if (condition) { doSomething(); }

3.1 花括號的對齊方式

  • 推薦使用的花括號對齊風格是:將左花括號放置在塊語句的第一句代碼的末尾。
// 好的作法
if (condition) {
    doSomething();
} else {
    doSomethingElse();
}

3.2 塊語句間隔

  • 推薦風格:在括左圓括號以前和括右圓括號以後各添加一個空格
// 好的作法
if (condition) {
    doSomething();
}

3.3 switch語句

  • 不管什麼時候都不該該省略default
// 好的作法
switch (condithing) {
    case 'first'
        // 代碼
        break;
    default:
        // default中沒有邏輯
}

4.變量、函數、運算符、Class

4.1 變量聲明

  • ES6 提出了兩個新的聲明變量的命令:let和const。其中,let徹底能夠取代let,由於二者語義相同,並且let沒有反作用。
'use strict';

if (true) {
  let x = 'hello';
}

for (let i = 0; i < 10; i++) {
  console.log(i);
}
  • 建議再也不使用var命令,而是使用let命令取代。
'use strict';

if (true) {
  console.log(x); // ReferenceError
  let x = 'hello';
}
  • let命令存在變量提高效用,let命令沒有這個問題正則表達式

  • 必須將全部的變量聲明放在函數頂部,而不是散落在各個角落

// 好的作法
function doSomething(items) {
    let i, len;
    let value = 10;
    let result = value + 10;

    for (i = 0; len = items.length; i < len; i++) {
        doSomething();
    }
}
  • 合併let語句,可讓代碼更短更快:每一個變量的初始化獨佔一行,賦值運算符應當對齊,沒有初始值的變量,應當出如今let語句的尾部;
// 好的作法
function doSomething() {
    let value = 10,
        result = value + 10,
        i, len;
    for (i = 0; len = items.length; i < len; i++) {
        doSomething();
    }
}
  • 在let和const之間,建議優先使用const,尤爲是在全局環境,不該該設置變量,只應設置常量
// 很差的寫法
var a = 1, b = 2, c = 3;

// 好的寫法
const a = 1;
const b = 2;
const c = 3;

// 最好的寫法
const [a, b, c] = [1, 2, 3];
  • 全部的函數都應該設置常量

4.2 函數

  • 必須先聲明javascript函數再使用函數
// 好的寫法
function doSomething() {
    alert('');
}

doSomething();

// 很差的寫法
doSomething();

function doSomething() {
    alert('');
}
  • 當即執行函數能夠寫成箭頭函數的形式。
(() => {
  console.log('Welcome to the Internet.');
})();
  • 那些須要使用函數表達式的場合,儘可能用箭頭函數代替。由於這樣更簡潔,並且綁定了 this。
// bad
[1, 2, 3].map(function (x) {
  return x * x;
});

// good
[1, 2, 3].map((x) => {
  return x * x;
});

// best
[1, 2, 3].map(x => x * x);
  • 箭頭函數取代Function.prototype.bind,不該再用 self/_this/that 綁定 this。
// bad
const self = this;
const boundMethod = function(...params) {
  return method.apply(self, params);
}

// acceptable
const boundMethod = method.bind(this);

// best
const boundMethod = (...params) => method.apply(this, params);
  • 簡單的、單行的、不會複用的函數,建議採用箭頭函數。若是函數體較爲複雜,行數較多,仍是應該採用傳統的函數寫法
  • 全部配置項都應該集中在一個對象,放在最後一個參數,布爾值不能夠直接做爲參數。
// bad
function divide(a, b, option = false ) {
}

// good
function divide(a, b, { option = false } = {}) {
}
  • 不要在函數體內使用 arguments 變量,使用 rest 運算符(...)代替。由於 rest 運算符顯式代表你想要獲取參數,並且 arguments 是一個相似數組的對象,而 rest 運算符能夠提供一個真正的數組。
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// good
function concatenateAll(...args) {
  return args.join('');
}
  • 使用默認值語法設置函數參數的默認值。
// bad
function handleThings(opts) {
  opts = opts || {};
}

// good
function handleThings(opts = {}) {
  // ...
}

4.3 嚴格模式

  • 不要在全局做用域中使用"use strict"
// 很差的寫法
"use strict"
function doSomething() {
    // 代碼
}

// 好的寫法
function doSomething() {
    "use strict"
    // 代碼
}

4.4 相等

  • 全部情景下都應當使用全等===和不全等!==

4.5 原始包裝類型

  • 儘可能禁止使用原始包裝類型(String、Number、Boolean)來建立新對象
// 很差的作法
let name = new String('Nicholas');
let author = new Boolean(true);
let count = new Number(10);

4.6 Map結構

  • 注意區分 Object 和 Map,只有模擬現實世界的實體對象時,才使用 Object。若是隻是須要key: value的數據結構,使用 Map 結構。由於 Map 有內建的遍歷機制。
let map = new Map(arr);

for (let key of map.keys()) {
  console.log(key);
}

for (let value of map.values()) {
  console.log(value);
}

for (let item of map.entries()) {
  console.log(item[0], item[1]);
}

4.7 Class

  • 老是用 Class,取代須要 prototype 的操做。由於 Class 的寫法更簡潔,更易於理解。
// bad
function Queue(contents = []) {
  this._queue = [...contents];
}
Queue.prototype.pop = function() {
  const value = this._queue[0];
  this._queue.splice(0, 1);
  return value;
}

// good
class Queue {
  constructor(contents = []) {
    this._queue = [...contents];
  }
  pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
  }
}
  • 使用extends實現繼承,由於這樣更簡單,不會有破壞instanceof運算的危險。
// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
  Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function() {
  return this._queue[0];
}

// good
class PeekableQueue extends Queue {
  peek() {
    return this._queue[0];
  }
}

4.8 模塊

  • Module 語法是 JavaScript 模塊的標準寫法,堅持使用這種寫法。使用import取代require。
// bad
const moduleA = require('moduleA');
const func1 = moduleA.func1;
const func2 = moduleA.func2;

// good
import { func1, func2 } from 'moduleA';
  • 使用export取代module.exports。
// commonJS的寫法
var React = require('react');

var Breadcrumbs = React.createClass({
  render() {
    return <nav />;
  }
});

module.exports = Breadcrumbs;

// ES6的寫法
import React from 'react';

class Breadcrumbs extends React.Component {
  render() {
    return <nav />;
  }
};

export default Breadcrumbs;
  • 若是模塊只有一個輸出值,就使用export default,若是模塊有多個輸出值,就不使用export default,export default與普通的export不要同時使用。

  • 不要在模塊輸入中使用通配符。由於這樣能夠確保你的模塊之中,有一個默認輸出(export default)。

// bad
import * as myObject from './importModule';

// good
import myObject from './importModule';
  • 若是模塊默認輸出一個函數,函數名的首字母應該小寫。
function makeStyleGuide() {
}

export default makeStyleGuide;
  • 若是模塊默認輸出一個對象,對象名的首字母應該大寫。
const StyleGuide = {
  es6: {
  }
};

export default StyleGuide;

5. 避免使用全局變量

  • 在瀏覽器中,window對象每每重載並等同於全局對象,,所以任何在全局做用域中聲明點變量和函數都是window對象的屬性;

6.事件處理

  • 最佳實踐:將應用邏輯從全部事件處理程序中抽離出來

  • 規則 1: 隔離應用邏輯;

// 好的寫法
let myApplication = {
    handleClick: function(event) {
        this.showPopup(event)
    },
    showPopup: function(event) {
        let popup = document.getElementById('popup');
        popup.style.left = event.clientX + 'px';
        popup.style.top = event.clientY + 'px';
        popup.className = 'reveal'
    }
};

addListener(element, 'click', function(event) {
    MyApplication.handleClick(event);
});
  • 規則 2: 不要分發事件對象, 最佳點辦法是讓事件處理程序使用event對象來處理事件;而後拿到所須要的數據傳給應用邏輯
// 好的寫法
let myApplication = {
    handleClick: function(event) {
        this.showPopup(event.clientX, event.clientY)
    },
    showPopup: function(x,y) {
        let popup = document.getElementById('popup');
        popup.style.left = x + 'px';
        popup.style.top = y + 'px';
        popup.className = 'reveal'
    }
};

addListener(element, 'click', function(event) {
    MyApplication.handleClick(event);
});

7. 避免空比較

let Controller = {
    process: function(items) {
        if (items !== null) {   // 很差的寫法
            items.sort();
            items.forEach(function() {
                // 執行邏輯
            })
        }
    }
}

7.1 檢測原始值

  • javascript有五種原始類型:字符串、數字、布爾值、null、undefined
  • 判斷五種原始類型,最好使用typeo運算符;
  • typeof運算符會返回一個表示值的類型的字符串
// 檢測字符串
if (typeof name === 'string') {
    anotherName = name.substring(3);
}

// 檢測數字
if (typeof count === 'number') {
    updateCount(count);
}

// 檢測布爾值
if (typeof found === 'boolean' && found) {
    message('Found');
}

// 檢測undefined
if (typeof MyApp === 'undefined') {
    MyApp = {}
}
  • 未定義到變量和值爲undefined的變量,經過typeof都將返回'undefined'
  • 原始值 null ,通常不用於檢測語句,若是須要檢測null,最好使用===或!==
typeof null 返回 'object'
編程時須要杜絕使用typeof來檢測null的類型

7.2 檢測引用值

  • 檢測某個引用值的類型的最好方法是使用instanceof運算符
value instanceof constructor

// 檢測日期
if (value instanceof Date) {
    console.log(value.getFullYear());
}

// 檢測正則表達式
if (value instanceof RegExp) {
    if (value.test(anotherValue)) {
        console.log('Mathces');
    }
}

// 檢測Error
if (value instanceof Error) {
    throw value;
}
  • instanceof運算符可用於檢測自定義的類型
function Person(name) {
    this.name = name;
}
let me = new Person('Nicholas');

console.log(me instanceof Object); // true
console.log(me instanceof Person); // true

7.2.1 檢測函數

  • javascript中的函數都是引用類型,一樣存在Function構造函數,每一個函數都是其實例;
function myFunc() {};

// 很差的寫法
console.log(myFunc instanceof Function); // true
// 然而這個方法不能跨幀(frame)使用,由於每一個幀都有各自的Function構造函數。可是可使用typeof運算符檢測函數

// 好的寫法
console.log(typeof myFunc === 'function')

7.3 檢測屬性

  • 判斷屬性是否存在的最好辦法是使用in運算符

  • in運算符僅僅會簡單的判斷屬性是否存在,而不會去讀取屬性到值

let object = {
    count: 0,
    related:null
}

// 好的寫法
if ('count' in object) {
    // 這裏的代碼會執行
}

// 很差的寫法: 檢測假值
if (object['count']) {
    // 這裏的代碼不會執行
}

// 好的寫法
if ('related' in object) {
    // 這裏的代碼會執行
}

// 很差的寫法: 檢測是否爲null
if (object['related'] !== null) {
    // 這裏的代碼不會執行
}
  • 檢測實例對象的某個屬性是否存在,則使用hasOwnProperty()方法,若是實例存在這個屬性則返回true(若是這個屬性只存在於原型裏,則返回false)
// 對於全部非dom對象,這是最好的辦法
if (object.hasOwnProperty('related')) {
    // 執行這裏的代碼
}

// 若是不肯定是否爲dom對象,
if ('hasOwnProperty' in object && object.hasOwnProperty('related')) {
    // 執行這裏的代碼
}

8 將配置數據從代碼中分離出來

8.1 什麼是配置數據

  • 配置數據是應用中寫死的值
function validate(value) {
    if (!value) {
        alert('Invalid value')  // 可能會修改替換
        location.href = '/errors/invalid.php';  // 可能會修改替換
    }
}

function toggleSelected(element) {
    if (hasClass(element, 'selected')) {    // 'selected' 可能會修改替換
        removeClass(element, 'selected');   // 'selected' 可能會修改替換
    } else {
        addClass(element, 'selected')   // 'selected' 可能會修改替換

    }
}
  • URL
  • 須要展示給用戶的字符串
  • 重複的值
  • 設置(好比每頁的配置項)
  • 任何可能發生變動的值

8.2 抽離配置數據

  • 將配置數據從代碼中抽離出來的第一步是將配置數據拿到外部,即將數據從JavaScript代碼之中拿掉
let config = {
    MSG_INVALID_VALUE: 'Invalid value',
    URL_INVALID: 'errors/invalid.php',
    CSS_SELECTED: 'selected'
}
function validate(value) {
    if (!value) {
        alert(config.MSG_INVALID_VALUE);
        location.href = config.URL_INVALID;
    }
}
function toggleSelected(element) {
    if (hasClass(element, config.CSS_SELECTED)) {    // 'selected' 可能會修改替換
        removeClass(element, config.CSS_SELECTED);   // 'selected' 可能會修改替換
    } else {
        addClass(element, config.CSS_SELECTED)   // 'selected' 可能會修改替換

    }
}

8.3 保存配置數據

  • 配置數據最好放在單獨的文件中,以便清晰的分隔數據和應用邏輯
  • 將配置文件置於單獨的JavaScript文件中,是一個不錯的開始。一旦配置數據存放於單獨的文件中,就能夠管理這些數據

9. 拋出自定義錯誤

9.1 在JavaScript中拋出錯誤

  • 使用throw操做符,將提供的一個對象做爲錯誤拋出。
  • 任何類型的對象均可以做爲錯誤拋出,Error對象是最經常使用的

throw new Error('something bad happend')

// 很差的寫法
  throw 'message';
  • 若是沒有經過try-catch語句捕獲,拋出任何值將引起一個錯誤
  • 惟一不出差錯的顯示自動移動錯誤消息的方式就是使用一個Error對象

9.2 拋出錯誤的好處

  • 拋出本身的錯誤可使用確切的文本供瀏覽器顯示
  • 在錯誤消息中包含函數名稱以及函數失敗的緣由

9.3 什麼時候拋出錯誤

  • 一旦修復了一個很難調試的錯誤,嘗試增長一兩個自定義錯誤,當再次發生錯誤時,這將有幫助於更容易的解決問題
  • 若是正在編寫代碼,思考一下:「我但願【某些事情】不會發生,若是發生,個人代碼會一團糟糕」。這時,若是「某些事情」發生,就拋出一個錯誤
  • 若是正在編寫的代碼別人也會使用,思考一下他們的使用方式,在特定的狀況下拋出錯誤

請牢記,咱們多目的不是防止錯誤,而是在錯誤發生時能更加容易的調試

9.4 try-catch語句

  • 將可能引起錯誤的代碼放在try塊中,處理錯誤的的代碼放在catch塊中
try {
    somethingThatMighCauseAnError();
} catch (ex) {
    handleError(ex)
}

當try塊中發生一個錯誤時,程序當即中止執行,而後跳到catch塊,並傳入一個錯誤對象。檢測該對象能夠肯定從錯誤中恢復的最佳動做。

  • 能夠增長finally塊。finally塊中的代碼無論是否有錯誤發生,最後都會被執行。
try {
    somethingThatMighCauseAnError();
} catch (ex) {
    handleError(ex)
} finally {
    continue;
}

注意:若是try塊中包含了一個return語句,實際上必須等到finally塊中的代碼執行後才能返回,所以,finally其實不太經常使用,可是若是處理錯誤必要,它還是處理錯誤的一個強大工具;

  • 使用throw仍是try-catch
  • 千萬不要將try-catch中的catch留空,需寫點錯誤處理邏輯來處理錯誤。
// 很差的寫法
try {
    somethingThatMighCauseAnError();
} catch (ex) {
    // 空
}

9.5 錯誤類型

  • Error: 全部錯誤的基本類型;
    • EvalError: 經過eval()函數執行代碼發生錯誤時拋出;
    • RangeError: 一個數字超出它的邊界時拋出;
    • SyntaxError: 指望的對象不存在時拋出;
    • TypeError: 變量不是指望的類型時拋出;
    • URIError: 給encodeURL()、encodeURLComponent()、decodeURL()等函數傳遞格式非法的URL字符串時拋出;
  • 建立本身的錯誤類型,繼承Error,這種作法容許你提供額外的信息,同時可區別與瀏覽器拋出的錯誤。
function MyError(message) {
    this.message = message;
}
MyError.prototype = new Error();

這段代碼有兩個重要的部分:message屬性,瀏覽器必需要知道錯誤消息字符串;設置prototype爲Error的一個實例,對JavaScript引擎就標識它是一個錯誤對象;

throw new MyError('Hello world');

10. 文檔化 JSDOC

  • JSDoc是一個根據javascript文件中註釋信息,生成JavaScript應用程序或庫、模塊的API文檔 的工具
  • JSDoc註釋通常應該放置在方法或函數聲明以前,它必須以/ **開始,以便由JSDoc解析器識別。其餘任何以/*,/***或者超過3個星號的註釋,都將被JSDoc解析器忽略
/**
 * Book類,表明一個書本.
 * @constructor
 * @param {string} title - 書本的標題.
 * @param {string} author - 書本的做者.
 */
function Book(title, author) {
    this.title=title;
    this.author=author;
}
Book.prototype={
    /**
     * 獲取書本的標題
     * @returns {string|*}
     */
    getTitle:function(){
        return this.title;
    },
    /**
     * 設置書本的頁數
     * @param pageNum {number} 頁數
     */
    setPageNum:function(pageNum){
        this.pageNum=pageNum;
    }
};

學習文檔:JSDoc中文文檔

學習資料《編寫可維護的JavaScript》《ECMAScript 6入門》

相關文章
相關標籤/搜索