使用空格符进行缩进;
javascript
四个空格符表示一个缩进层级;
php
function demo() { console.log('hello world'); }
使用分号结尾;
// 正确的代码 let name = 'Nicholas'; function sayName() { alert(name); }
// 原始代码 function getData() { return { title: '', author: '' } } // 分析器会理解成 function getData () { return; { title: '', author: '' }; }
指定一行代码长度限定在80个字符;
当一行长度超过最大字符数限制(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; // 确保代码的可读性,并能一眼看清折行的上下文
在方法之间加入空行
前端
在方法中的局部变量和第一条语句之间加入空行
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() {}
驼峰式大小写:小写字母开始,后续每一个单词首字母都大写;
// 好的写法 let thisIsMyName; let anotherletiable; let aVeryLongariableName;
变量名需老是遵照驼峰式大小写命名法,且命名前缀为【名词】,以名词作前缀可让变量与函数区分开来;函数名前缀为【动词】
// 变量: 好的写法 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 | 函数用来保存一个值 |
常量使用大写字母和下划线来命名,下划线用以分割单词
let MAX_COUNT = 10; let URL = 'http://www.nczonline.net/';
构造函数以'驼峰式大小写'命名,首字母为大写,名词
// 好的作法 function Person(name) { this.name = name } Person.prototype.sayName = function() { alert(this.name); }; let me = new Person('Nicholas')
静态字符串一概使用单引号或反引号,不使用双引号。动态字符串使用反引号。并从头至尾保持一种风格
// 很差的写法 const a = "foobar"; const b = 'foo' + a + 'bar'; // 可接受的写法 const c = `foobar`; // 好的写法 const a = 'foobar'; const b = `foo${a}bar`; const c = 'foobar';
在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;
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最好的方式时将它看成对象的占位符;
未被初始化的变量都有一个初始值,即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
对象直接量容许将全部的属性都括在一对花括号中;
// 好的写法 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; }, };
使用两个方括号将数组初始元素括起来,来替代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);
使用数组成员对变量赋值时,优先使用解构赋值。
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);
代码晦涩难懂
可能被误认为错误的代码
必要但不明显的针对特定浏览器的代码
对于对象、方法或者属性,生成文档是有必要的(使用恰当的文档注释)
独占一行,用来解释下一行代码,且缩进层级和下一行代码保持一致;
在代码行的尾部的注释,代码结束到注释之间至少有一个缩进,且不超过单行最大字符数限制,若是超过,则放置在代码行的上方;
// 好的写法 if (condition) { // 代码执行逻辑 allowed(); } // 很差的写法:注释以前没有空行 if (condition) { // 代码逻辑 allowed(); } // 很差的写法: 错误的缩进 if (condition) { // 代码执行逻辑 allowed(); } // 好的写法 let result = something + somethingElse; // 代码执行逻辑 // 很差的写法: 代码和注释之间没有间隔; let result = something + somethingElse;// 代码执行逻辑
多行注释以前应当有一个空行,且缩进层级和其描述的代码保持一致;
// 好的写法 if (condition) { /* * 代码执行逻辑 * 代码执行逻辑 */ allowed(); }
当代码不够清晰时添加注释,当代码很明了时不该当添加注释;
添加注释的通常原则是:在须要让代码变的更清晰时添加注释;
// 很差的写法 // 初始化count let count = 10; // 好的写法 //改变这个值可能会让它变成青蛙 let count = 10;
在JavaScript中,不论块语句包含多行代码仍是单行代码,都应该使用花括号
// 好的写法 if (condition) { doSomething(); } // 很差的写法 if(condition) doSomething(); // 很差的写法 if(condition) doSomething(); // 很差的写法 if (condition) { doSomething(); }
推荐使用的花括号对齐风格是:将左花括号放置在块语句的第一句代码的末尾。
// 好的作法 if (condition) { doSomething(); } else { doSomethingElse(); }
推荐风格:在括左圆括号以前和括右圆括号以后各添加一个空格
// 好的作法 if (condition) { doSomething(); }
不管什么时候都不该该省略default
// 好的作法 switch (condithing) { case 'first' // 代码 break; default: // default中没有逻辑 }
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];
全部的函数都应该设置常量
必须先声明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 = {}) { // ... }
不要在全局做用域中使用"use strict"
// 很差的写法 "use strict" function doSomething() { // 代码 } // 好的写法 function doSomething() { "use strict" // 代码 }
全部情景下都应当使用全等===和不全等!==
尽可能禁止使用原始包装类型(String、Number、Boolean)来建立新对象
// 很差的作法 let name = new String('Nicholas'); let author = new Boolean(true); let count = new Number(10);
注意区分 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]); }
老是用 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]; } }
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;
在浏览器中,window对象每每重载并等同于全局对象,,所以任何在全局做用域中声明点变量和函数都是window对象的属性;
最佳实践:将应用逻辑从全部事件处理程序中抽离出来
规则 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); });
let Controller = { process: function(items) { if (items !== null) { // 很差的写法 items.sort(); items.forEach(function() { // 执行逻辑 }) } } }
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的类型
检测某个引用值的类型的最好方法是使用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
javascript中的函数都是引用类型,一样存在Function构造函数,每一个函数都是其实例;
function myFunc() {}; // 很差的写法 console.log(myFunc instanceof Function); // true // 然而这个方法不能跨帧(frame)使用,由于每一个帧都有各自的Function构造函数。可是可使用typeof运算符检测函数 // 好的写法 console.log(typeof myFunc === 'function')
判断属性是否存在的最好办法是使用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')) { // 执行这里的代码 }
配置数据是应用中写死的值
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
须要展示给用户的字符串
重复的值
设置(好比每页的配置项)
任何可能发生变动的值
将配置数据从代码中抽离出来的第一步是将配置数据拿到外部,即将数据从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' 可能会修改替换 } }
配置数据最好放在单独的文件中,以便清晰的分隔数据和应用逻辑
将配置文件置于单独的JavaScript文件中,是一个不错的开始。一旦配置数据存放于单独的文件中,就能够管理这些数据
使用throw操做符,将提供的一个对象做为错误抛出。
任何类型的对象均可以做为错误抛出,Error对象是最经常使用的
throw new Error('something bad happend')
// 很差的写法 throw 'message';
若是没有经过try-catch语句捕获,抛出任何值将引起一个错误
惟一不出差错的显示自动移动错误消息的方式就是使用一个Error对象
抛出本身的错误可使用确切的文本供浏览器显示
在错误消息中包含函数名称以及函数失败的缘由
一旦修复了一个很难调试的错误,尝试增长一两个自定义错误,当再次发生错误时,这将有帮助于更容易的解决问题
若是正在编写代码,思考一下:“我但愿【某些事情】不会发生,若是发生,个人代码会一团糟糕”。这时,若是“某些事情”发生,就抛出一个错误
若是正在编写的代码别人也会使用,思考一下他们的使用方式,在特定的状况下抛出错误
请牢记,咱们多目的不是防止错误,而是在错误发生时能更加容易的调试
将可能引起错误的代码放在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) { // 空 }
建立本身的错误类型,继承Error,这种作法容许你提供额外的信息,同时可区别与浏览器抛出的错误。
function MyError(message) { this.message = message; } MyError.prototype = new Error(); 这段代码有两个重要的部分:message属性,浏览器必需要知道错误消息字符串;设置prototype为Error的一个实例,对JavaScript引擎就标识它是一个错误对象;
throw new MyError('Hello world');
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入门》