前端项目-代码开发规范(个人整理版)

前端代码规范

基本的格式化

1.1 缩进层级

  • 使用空格符进行缩进;

  • 四个空格符表示一个缩进层级;

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);
  • 注意:逗号也算一个运算符,应当作为前一行当结尾;

  • 变量赋值时,第二行的位置应当与赋值运算符的位置保持对齐;

// 正确的做法
let result = something + anotherThing + yetAnotherThing + somethingElse +
             anotherSomethingElse;
// 确保代码的可读性,并能一眼看清折行的上下文

1.5 空行

  • 在方法之间加入空行

  • 在方法中的局部变量和第一条语句之间加入空行

  • 在多行或单行注释之前加入空行

  • 在方法内的逻辑片段之间插入空行,提高可读性

  • 在方法之间加入空行

// 好的写法
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入门》

原文地址:https://www.cnblogs.com/webSciprt/p/9566759.html