原则
- 能用
- 快
- 易读
- 短路优先:
- 函数尽可能短,太长的函数拆分
- 控制块尽可能短,太长的控制块抽出函数
- 条件判断时,短分支写在长分支前面
- 变量声明和变量使用的间隔尽可能地短
- 变量的作用域尽可能的短
- 得到返回值尽可能早的return,清理操作放finally
- 发现异常尽可能早的throw,哪怕catch再rethrow
- 对称: 有上就有下,有左就有右, 有主动就有被动
- 命名对称: set/get、start/stop、begin/ end 和 push/pop
- 条件与反条件
- 层次: 主从关系、前后关系和本末关系, 不同层次各司其职,同种处理不跨越多个层次
- 安全: 在编写代码时刻意将不可能的条件考虑进去
- 好名字 > 坏名字 + 注释
- 缓存常用的值
插件
vscode必须安装插件Prettier - Code formatter, 格式化代码快捷键 Shift + Alt + F
Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.
文档注释
各类标签@param, @method等请参考usejsdoc和JSDoc Guide;
/** * @func * @desc 一个带参数的函数 * @param {string} a - 参数a * @param {number} b=1 - 参数b默认值为1 * @param {string} c=1 - 参数c有两种支持的取值</br>1—表示x</br>2—表示xx * @param {object} d - 参数d为一个对象 * @param {string} d.e - 参数d的e属性 * @param {string} d.f - 参数d的f属性 * @param {object[]} g - 参数g为一个对象数组 * @param {string} g.h - 参数g数组中一项的h属性 * @param {string} g.i - 参数g数组中一项的i属性 * @param {string} [j] - 参数j是一个可选参数 */ function foo(a, b, c, d, g, j) { ... }
变量命名
- 标准变量采用驼峰式命名(除了对象的属性外,主要是考虑到cgi返回的数据)
- 'ID'在变量名中全大写
- 'URL'在变量名中全大写
- 'Android'在变量名中大写第一个字母
- 'iOS'在变量名中小写第一个,大写后两个字母
- 常量全大写,用下划线连接
- 构造函数,大写第一个字母
- jquery对象必须以'$'开头命名
var thisIsMyName; var goodID; var reportURL; var AndroidVersion; var iOSVersion; var MAX_COUNT = 10; function Person(name) { this.name = name; } // not good var body = $('body'); // good var $body = $('body');
变量声明
一个函数作用域中所有的变量声明尽量提到函数首部,用一个var声明,不允许出现两个连续的var声明。
function doSomethingWithItems(items) { // use one var var value = 10, result = value + 10, i, len; for (i = 0, len = items.length; i < len; i++) { result += 10; } }
null
适用场景:
- 初始化一个将来可能被赋值为对象的变量
- 与已经初始化的变量做比较
- 作为一个参数为对象的函数的调用传参
- 作为一个返回对象的函数的返回值
不适用场景:
- 不要用null来判断函数调用时有无传参
- 不要与未初始化的变量做比较
// not good function test(a, b) { if (b === null) { // not mean b is not supply ... } } var a; if (a === null) { ... } // good var a = null; if (a === null) { ... }
undefined
永远不要直接使用undefined进行变量判断;
使用typeof和字符串'undefined'对变量进行判断。
// not good if (person === undefined) { ... } // good if (typeof person === 'undefined') { ... }
格式化字符串
obj = { name: "牙叔教程", add(a, b) { return a + b; }, }; r = util.format("数字%d, 文本: %s, 对象%j", 1, "abc", obj); console.log(r); // 数字1, 文本: abc, 对象{"name":"牙叔教程"}
模块测试
"use strict"; function add(num1, num2) { return num1 + num2; } let obj = { name: "牙叔教程", add: add, }; if (typeof module === "undefined") { let r = obj.add(1, 2); console.log(r); } else { module.exports = obj; }
importClass和require谁先谁后
runtime.loadDex("./yashu.dex"); importClass(android.graphics.Rect); let yashu = require("./yashu");
- 提示框和提示信息,例如 showDialog
- updateXX:更新某个东西,例如 updateData
- saveXX:保存某个东西,例如 saveData
- resetXX:重置某个东西,例如 resetData
- clearXX:清除某个东西,例如 clearData
- removeXX:移除数据或者视图等,例如 removeView
- drawXX:绘制数据或效果相关的,使用 draw 前缀标识,例如 drawText
- 避免单个字符名,让你的变量名有描述意义。
// bad function q() { // ...stuff... } // good function query() { // ..stuff.. }
当命名对象、函数和实例时使用驼峰命名规则
// bad var OBJEcttsssss = {}; var this_is_my_object = {}; var this-is-my-object = {}; function c() {}; var u = new user({ name: 'Bob Parr' }); // good var thisIsMyObject = {}; function thisIsMyFunction() {}; var user = new User({ name: 'Bob Parr' });
- 当命名构造函数或类时使用驼峰式大写
// bad function user(options) { this.name = options.name; } var bad = new user({ name: 'nope' }); // good function User(options) { this.name = options.name; } var good = new User({ name: 'yup' });
- 命名私有属性时前面加个下划线
_
// bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; // good this._firstName = 'Panda';
当保存对 this
的引用时使用 _this
.v
// bad function() { var self = this; return function() { console.log(self); }; } // bad function() { var that = this; return function() { console.log(that); }; } // good function() { var _this = this; return function() { console.log(_this); }; }
文件命名规范
- 谷歌
File names must be all lowercase and may include underscores (_
) or dashes (-
), but no additional punctuation. Follow the convention that your project uses. Filenames’ extension must be .js
.
- github
What is the javascript filename naming convention?
- 其他1
Js 文件命名规范
- 字母全部小写
- 不要带空格
- 用破折号(-)连接单词
- 库文件可用逗点(.),用于体现版本或从属关系
Demo
- vue.min.js
- vue-router.js
- jquery.form.js
- jquery-1.4.2.min.js
- 其他2
1、项目命名
全部采用小写方式,以下划线分割战场
例如:come_money
2、目录命名
参照项目命名,如有复数结构时,采用复数命名法
例如: moneys,assets,components。。。。
3、js、css、less、sass、 stylus文件命名
全部采用小驼峰命名规则
例如: comeMoney.js、moneyCome.css、moneyCome.stylus
4、VUE组件Components命名
所有组件名字大驼峰格式
例如: ComeMoney.vue、MoneyCome.vue
- 上面的惯例仅供参考,最终还是尊重个人习惯。不过如果你在项目中引用了某个框架或库,就最好优先使用他们的命名习惯。另外,一个项目中最好统一使用一种命名规则,这样方便自己和其他开发人员识别。
对象
- 请使用对象方法的简写方式, 属性不可简写, 方法可以简写
// bad const item = { value: 1, addValue: function (val) { return item.value + val } } // good const item = { value: 1, addValue (val) { return item.value + val } }
- 不要直接使用
Object.prototype
的方法, 例如hasOwnProperty
,propertyIsEnumerable
和isPrototypeOf
方法
原因:这些方法可能会被对象自身的同名属性覆盖 - 比如 { hasOwnProperty: false }
或者对象可能是一个 null
对象(Object.create(null)
)
// bad console.log(object.hasOwnProperty(key)) // good console.log(Object.prototype.hasOwnProperty.call(object, key)) // best const has = Object.prototype.hasOwnProperty // cache the lookup once, in module scope. console.log(has.call(object, key))
数组
- 当你需要拷贝数组时使用slice
var len = items.length, itemsCopy = [], i; // bad for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // good itemsCopy = items.slice();
使用slice将类数组的对象转成数组
function trigger() { var args = Array.prototype.slice.call(arguments); ... }
解构赋值
- 当需要使用数组的多个值时,请使用解构赋值
const arr = [1, 2, 3, 4] // bad const first = arr[0] const second = arr[1] // good const [first, second] = arr
字符串
- 字符串太长的时候,请不要使用字符串连接符换行
\
,而是使用+
const str = '牙叔教程 牙叔教程 牙叔教程' + '牙叔教程 牙叔教程 牙叔教程' + '牙叔教程 牙叔教程'
不要在字符串中使用不必要的转义字符
// bad const foo = '\'this\' \i\s \"quoted\"' // good const foo = '\'this\' is "quoted"'
编程时使用join而不是字符串连接来构建字符串
var items, messages, length, i; messages = [{ state: 'success', message: 'This one worked.' },{ state: 'success', message: 'This one worked as well.' },{ state: 'error', message: 'This one did not work.' }]; length = messages.length; // bad function inbox(messages) { items = '<ul>'; for (i = 0; i < length; i++) { items += '<li>' + messages[i].message + '</li>'; } return items + '</ul>'; } // good function inbox(messages) { items = []; for (i = 0; i < length; i++) { items[i] = messages[i].message; } return '<ul><li>' + items.join('</li><li>') + '</li></ul>'; }
函数
- 函数表达式
// 匿名函数表达式 var anonymous = function() { return true; }; // 有名函数表达式 var named = function named() { return true; }; // 立即调用函数表达式 (function() { console.log('Welcome to the Internet. Please follow me.'); })();
用圆括号包裹自执行匿名函数
(function () { console.log('Welcome to the Internet. Please follow me.') }())
- 不要在一个非函数块里声明一个函数,把那个函数赋给一个变量。
注: ECMA-262定义把块
定义为一组语句,函数声明不是一个语句。阅读ECMA-262对这个问题的说明.
// bad if (currentUser) { function test() { console.log('Nope.'); } } // good if (currentUser) { var test = function test() { console.log('Yup.'); }; }
绝对不要把参数命名为 arguments
, 这将会逾越函数作用域内传过来的 arguments
对象.
// bad function nope(name, options, arguments) { // ...stuff... } // good function yup(name, options, args) { // ...stuff... }
箭头函数
- 当你必须使用函数表达式(传递匿名函数)时,使用箭头函数标记
// bad [1, 2, 3].map(function (x) { const y = x + 1 return x * y }) // good [1, 2, 3].map((x) => { const y = x + 1 return x * y })
对象属性
- 使用
.
来访问对象属性
const joke = { name: 'haha', age: 28 } // bad const name = joke['name'] // good const name = joke.name
当访问的属性是变量时使用 []
const luke = { jedi: true, age: 28, } function getProp (prop) { return luke[prop] } const isJedi = getProp('jedi')
变量声明
- 在作用域顶部声明变量,避免变量声明和赋值引起的相关问题。
// bad function() { test(); console.log('doing stuff..'); //..other stuff.. var name = getName(); if (name === 'test') { return false; } return name; } // good function() { var name = getName(); test(); console.log('doing stuff..'); //..other stuff.. if (name === 'test') { return false; } return name; } // bad function() { var name = getName(); if (!arguments.length) { return false; } return true; } // good function() { if (!arguments.length) { return false; } var name = getName(); return true; }
- 将所有的
const
和let
分组
// bad let a const b let c const d let e // good const b const d let a let c let e
变量不要进行链式赋值
原因:变量链式赋值会创建隐藏的全局变量
// bad (function example() { // JavaScript interprets this as // let a = ( b = ( c = 1 ) ); // The let keyword only applies to variable a; variables b and c become // global variables. let a = b = c = 1 }()) console.log(a) // throws ReferenceError console.log(b) // 1 console.log(c) // 1 // good (function example() { let a = 1 let b = a let c = a }()) console.log(a) // throws ReferenceError console.log(b) // throws ReferenceError console.log(c) // throws ReferenceError // the same applies for `const`
- 不允许出现未被使用的变量
原因:声明但未被使用的变量通常是不完全重构犯下的错误.这种变量在代码里浪费空间并会给读者造成困扰
// bad var some_unused_var = 42 // Write-only variables are not considered as used. var y = 10 y = 5 // A read for a modification of itself is not considered as used. var z = 0 z = z + 1 // Unused function arguments. function getX (x, y) { return x } // good function getXPlusY (x, y) { return x + y } const x = 1 const y = a + 2 alert(getXPlusY(x, y)) // 'type' is ignored even if unused because it has a rest property sibling. // This is a form of extracting an object that omits the specified keys. const { type, ...coords } = data // 'coords' is now the 'data' object without its 'type' property.
Hoisting
var
存在变量提升的情况,即var
声明会被提升至该作用域的顶部,但是他们的赋值并不会。而const
和let
并不存在这种情况,他们被赋予了 Temporal Dead Zones, TDZ, 了解 typeof 不再安全很重要
function example () { console.log(notDefined) // => throws a ReferenceError } function example () { console.log(declareButNotAssigned) // => undefined var declaredButNotAssigned = true } function example () { let declaredButNotAssigned console.log(declaredButNotAssigned) // => undefined declaredButNotAssigned = true } function example () { console.log(declaredButNotAssigned) // => throws a ReferenceError console.log(typeof declaredButNotAssigned) // => throws a ReferenceError const declaredButNotAssigned = true }
匿名函数的变量名会提升,但函数内容不会
function example () { console.log(anonymous) // => undefined anonymous() var anonymous = function () { console.log('test') } }
命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会
function example() { console.log(named) // => undefined named() // => TypeError named is not a function superPower() // => ReferenceError superPower is not defined var named = function superPower () { console.log('Flying') } } function example() { console.log(named) // => undefined named() // => TypeError named is not a function var named = function named () { console.log('named') } }
比较运算符&相等
- 使用
===
和!==
而非==
和!=
,eslint: eqeqeq - 条件声明例如
if
会用ToBoolean
这个抽象方法将表达式转成布尔值并遵循如下规则
Objects
等于true
Undefined
等于false
Null
等于false
Booleans
等于布尔值
Numbers
在+0
,-0
, 或者NaN
的情况下等于false
, 其他情况是true
Strings
为''
时等于false
, 否则是true
if ([0] && []) { // true // 数组(即使是空数组)也是对象,对象等于true }
// bad if (name !== '') { // ...stuff... } // good if (name) { // ...stuff... } // bad if (collection.length > 0) { // ...stuff... } // good if (collection.length) { // ...stuff... }
分号
- 自执行函数前后必须加分号
const test = '牙叔教程' ;(() => { const str = '简单易学' })();
块
- 给所有多行的块使用大括号
// bad if (test) return false; // good if (test) return false; // good if (test) { return false; } // bad function() { return false; } // good function() { return false; }
注释
- 使用
/** ... */
进行多行注释,包括描述,指定类型以及参数值和返回值
// bad // make() returns a new element // based on the passed in tag name // // @param <String> tag // @return <Element> element function make(tag) { // ...stuff... return element; } // good /** * make() returns a new element * based on the passed in tag name * * @param <String> tag * @return <Element> element */ function make(tag) { // ...stuff... return element; }
使用 //
进行单行注释,在评论对象的上面进行单行注释,注释前放一个空行.
// bad var active = true; // is current tab // good // is current tab var active = true; // bad function getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type'; return type; } // good function getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type'; return type; }
如果你有一个问题需要重新来看一下或如果你建议一个需要被实现的解决方法的话需要在你的注释前面加上 FIXME
或 TODO
帮助其他人迅速理解
function Calculator() { // FIXME: shouldn't use a global here total = 0; return this; }
function Calculator() { // TODO: total should be configurable by an options param this.total = 0; return this; }
存取器
- 属性的存取器函数不是必需的
- 如果你确实有存取器函数的话使用getVal() 和 setVal('hello')
// bad dragon.age(); // good dragon.getAge(); // bad dragon.age(25); // good dragon.setAge(25);
如果属性是布尔值,使用isVal() 或 hasVal()
// bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; }
可以创建get()和set()函数,但是要保持一致
function Jedi(options) { options || (options = {}); var lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } Jedi.prototype.set = function(key, val) { this[key] = val; }; Jedi.prototype.get = function(key) { return this[key]; };
构造器
- 给对象原型分配方法,而不是用一个新的对象覆盖原型,覆盖原型会使继承出现问题。
function Jedi() { console.log('new jedi'); } // bad Jedi.prototype = { fight: function fight() { console.log('fighting'); }, block: function block() { console.log('blocking'); } }; // good Jedi.prototype.fight = function fight() { console.log('fighting'); }; Jedi.prototype.block = function block() { console.log('blocking'); };
方法可以返回 this
帮助方法可链。
// bad Jedi.prototype.jump = function() { this.jumping = true; return true; }; Jedi.prototype.setHeight = function(height) { this.height = height; }; var luke = new Jedi(); luke.jump(); // => true luke.setHeight(20) // => undefined // good Jedi.prototype.jump = function() { this.jumping = true; return this; }; Jedi.prototype.setHeight = function(height) { this.height = height; return this; }; var luke = new Jedi(); luke.jump() .setHeight(20);
可以写一个自定义的toString()方法,但是确保它工作正常并且不会有副作用。
function Jedi(options) { options || (options = {}); this.name = options.name || 'no name'; } Jedi.prototype.getName = function getName() { return this.name; }; Jedi.prototype.toString = function toString() { return 'Jedi - ' + this.getName(); };
参考
为何部分程序员从不使用 break 或 continue 语句?
名人名言
思路是最重要的, 其他的百度, bing, stackoverflow, 安卓文档, autojs文档, 最后才是群里问问
--- 牙叔教程
声明
部分内容来自网络
本教程仅用于学习, 禁止用于其他用途