1. 代码格式篇(写得漂亮)
代码写的漂亮,能让看代码的人心情更加愉悦,心情愉悦了,就能够悄无声息的提高编码效率
1.1 缩进
一般使用 tab,两个空格,或者四个空格,具体看个人喜好
// 两个缩进
export default {
name: 'App',
provide() {},
computed: {},
data() {
return {
bool: true
}
},
methods: {}
}
// 四个缩进
export default {
name: 'App',
provide() {},
computed: {},
data() {
return {
bool: true
}
},
methods: {}
}
1.2 单行长度
不要超过 80,但如果编辑器开启 word wrap 可以不考虑单行长度。
1.3 空格
- 二元运算符前后
- 三元运算符'?:'前后
- 代码块'{'前
- 下列关键字前:
else
,while
,catch
,finally
- 下列关键字后:
if
,else
,for
,while
,do
,switch
,case
,try
,catch
,finally
,with
,return
,typeof
- 单行注释'//'后(若单行注释和代码同行,则'//'前也需要),多行注释'*'后
- 对象的属性值前
- for 循环,分号后留有一个空格,前置条件如果有多个,逗号后留一个空格
- 无论是函数声明还是函数表达式,'{'前一定要有空格
- 函数的参数之间
// not good
var a = {
b :1
};
// good
var a = {
b: 1
};
// not good
++ x;
y ++;
z = x?1:2;
// good
++x;
y++;
z = x ? 1 : 2;
// not good
var a = [ 1, 2 ];
// good
var a = [1, 2];
// not good
var a = ( 1+2 )*3;
// good
var a = (1 + 2) * 3;
// 函数调用的括号前不需要加括号
doSomething(item);
// not good
for(i=0;i<6;i++){
x++;
}
// good
for (i = 0; i < 6; i++) {
x++;
}
1.4 换行
换行的地方,行末必须有','或者运算符;
以下几种情况不需要换行:
- 下列关键字后:
else
,catch
,finally
- 代码块'{'前
以下几种情况需要换行:
- 代码块'{'后和'}'前
- 变量赋值后
// not good
var a = {
b: 1
, c: 2
};
x = y
? 1 : 2;
// good
var a = {
b: 1,
c: 2
};
x = y ? 1 : 2;
x = y ?
1 : 2;
// 在 'else', 'catch', 'finally' 这些关键字后面不需要换行
if (condition) {
...
} else {
...
}
try {
...
} catch (e) {
...
} finally {
...
}
// not good
function test()
{
...
}
// good
function test() {
...
}
// not good
var a, foo = 7, b,
c, bar = 8;
// good
var a,
foo = 7,
b, c, bar = 8;
1.5 单行注释
双斜线后,必须跟一个空格;
缩进与下一行代码保持一致;
可位于一个代码行的末尾,与代码间隔一个空格。
if (condition) {
// if true .....
doSomthing();
}
var name = '张三'; // one space after code
1.6 多行注释
最少三行, '*'后跟一个空格,具体参照右边的写法;
建议在以下情况下使用:
- 难于理解的代码段
- 可能存在错误的代码段
- 业务逻辑强相关的代码
/*
* '*'后面留一个空格
* |
* |
*/
var x = 1;
虽然现在编辑器都有一键格式化代码功能,但是,还是需要有自己的代码格式风格
2. 代码规范篇(写得好)
2.1 变量命名
变量命名,要尽量能够描述改变量的具体含义,除此,还应符合规范
- 标准变量采用驼峰式命名(除了对象的属性外)
- 常量全大写,用下划线连接
- 构造函数,大写第一个字母
- jQuery 对象必须以'$'开头命名
let isRouterAlive = true
const MIN_NUM = 100
class User {
....
}
let $body = $('body')
2.2 方法命名
和变量命名一样,方法命名也需要尽量能够描述改方法的具体含义和作用
- 使用驼峰命名法
- 通过交互触发的事件,需要在事件前面加上
handle
或on
前缀
function findMax(){
// dosomthing
}
// 交互触发的事件
function handelSubmitForm(){
// dosomthing
}
function onSubmitForm(){
// dosomthing
}
2.3 按强类型语言风格写代码
JS 是弱类型的,但是写代码的时候不能太随意,写得太随意也体现了编码风格不好。
2.3.1 给变量初始化类型
定义变量的时候要指明类型,告诉 JS 解释器这个变量是什么数据类型的,而不要让解释器去猜。
// not good
let num,
str,
obj;
// good
let num = 0,
str = '',
obj = null;
2.4.2 不要随意地改变变量的类型
例如下面代码:
let num = 5;
num = "-" + num;
2.4.3 函数的返回类型应该是要确定的
函数的返回值,在定义函数的时候,就需要考虑清楚,不要在一个函数中,返回多个类型的返回值,如果这样做,有可能会出现 NAN
或者达不到理想结果的情况
function getValue() {
let i = ~Math.random() * 10
if (i < 5) {
return 'value1'
}
return 10
}
//
2.4 有必要减少作用域的查找
2.4.1 保护好自己的变量
不要把代码暴露在全局变量中,因为这样既不安全,又影响解释器的执行效率
// not good
<script>
var a = 10
</script>
// good
// 使用立即执行函数(IFFE),来保护自己的变量,提供一个私有的命名空间
<script>
;(function(w, d){
var a = 10
})(window, document)
</script>
2.4.2 定义缓存变量
在写代码过程中,需要避免使用 a.b.c.d.e.f
a.b.c.d.e.g
这种情况的出现,在需要长 path
去获取变量值是,有必要把变量缓存起来
let { f, g } = a.b.c.d.e
// 在vue中
let { data1, data2, method1, method3 } = this
另外,在频繁使用一个对象是,也因该缓存变量
for (let i = 0,len = array.length; i < len; i++) {
const element = array[i];
}
2.4.3 全局变量
代码中往往需要使用,window
全局变量上的一些方法,这些方法不需要定义,可以直接使用,例如:location
,localStorage
在使用这些方法的时候,因该使用 window.xxx
来代替直接使用,不仅能够减少 js 解释器的查找,还可以是代码更加清晰易懂
// not good
location.href
localStorage.setItem('key','value')
// good
window.location.href
window.localStorage.setItem('key','value')
2.5 减少冗余的代码
2.5.1 按需导入
在写 vue 项目是,要尽量使用按需引入,一个工具模块导出了多个函数的时候,避免使用 export default
,因为这样不利于按需导入
// utils/index.js
export default function a(){}
export default function b(){}
export default function c(){}
export default function d(){}
// 这样写,在使用的时候,就只能这样导入
// xxx.vue
import utils from '@/utils'
更妥当一点的写法:
// utils/index.js
export function a(){}
export function b(){}
export function c(){}
export function d(){}
// 或者
export default {
a:function(){},
b:function(){},
c:function(){},
d:function(){}
}
// 这样写,在使用的时候,才能做到真正的按需映入
// xxx.vue
import { a, b } from '@/utils
2.5.2 去掉无用代码
代码中没有用到的变量,方法,应尽量删除
2.6 杂项
2.6.1 避免 ==
使用
使用 ==
可能会出现和预期不一样的情况,甚至出现一些怪异不好理解的现象。在 JavaScript 中,==
是会进行隐式类型转换的,所以在做 if
判断的时候,使用 ===
是最佳的,同时也符合上面提到的 强类型编码风格
console.log('1' == true) // true
console.log('10' == 10) // true
console.log('' == 0) // true
console.log('' == undefined) // true
console.log(0 == undefined) // false
console.log('0' == 0) // true
console.log(null == undefined) // true
2.6.2 不确定类型的比较
不确定类型的变量比较,要手动转换一下变量类型
var a = '5';
if(parseInt(a) === 1){
console.log(true)
}else{
console.log(false)
}
3. 代码技巧篇(写得妙)
要想把代码写得妙,自然是少不了一些奇巧淫技
3.1 快速深拷贝与浅拷贝
// 浅拷贝
let arr = [1,2,2,3,4]
let copyArr = [...arr]
let copyArr2 = Object.assign([],arr)
// 深拷贝
let obj = {
a:{
b:1
},
c:1,
d:[1,2,2,3,4]
}
// 这样的数组怎样深拷贝
// 通常是
// 定义一个深拷贝函数 接收目标target参数
function deepClone(target) {
// 定义一个变量
let result;
// 如果当前需要深拷贝的是一个对象的话
if (typeof target === 'object') {
// 如果是一个数组的话
if (Array.isArray(target)) {
result = []; // 将result赋值为一个数组,并且执行遍历
for (let i in target) {
// 递归克隆数组中的每一项
result.push(deepClone(target[i]))
}
// 判断如果当前的值是null的话;直接赋值为null
} else if(target===null) {
result = null;
// 判断如果当前的值是一个RegExp对象的话,直接赋值
} else if(target.constructor===RegExp){
result = target;
}else {
// 否则是普通对象,直接for in循环,递归赋值对象的所有值
result = {};
for (let i in target) {
result[i] = deepClone(target[i]);
}
}
// 如果不是对象的话,就是基本数据类型,那么直接赋值
} else {
result = target;
}
// 返回最终结果
return result;
}
// 更简便的方法
JSON.parse(JSON.stringify(obj))
3.2 判断数组是否存在某一项
var arr = [1, 2, 3];
console.log(!!~arr.indexOf(2));
注意:只适用于原始值数组
3.3 交换连个变量的值
let a = 10
let b = 20
// 通常的做法
let temp = a
a = b
b = temp
// 其实这并不需要第三个变量
a = [b][(b=a, 0)]
3.4 一行代码数组去重
let arr = [1, 2, 3, 4, 5, 4, '0', '0', false, 1, 1, 2, 5, 6, 7]
// 一行神奇的代码
let arr2 = [...new Set([1, 2, 3, 4, 5, 4, '0', '0', false, 1, 1, 2, 5, 6, 7])]
3.5 用 &&
来代替一些 if
let a = false
function foo(){}
if(a){
foo()
}
//
a && foo()
3.6 函数必传参数
es6 中有函数默认参数的语法,我们可以给函数参数默认的值,来保证函数在调用时,即使没有传参数,也可以正常运行。但是有的时候,我们需要某个参数是必传参数是,就可以这样:
function requiredParams(){
throw new Error('params is required')
}
// 使用
function a(bb = requiredParams()){
console.log(123)
}
a()
// params is required
a(1)
// 123
原理是:我们没有传递参数的时候,函数就会使用默认的参数,默认的参数是一个函数,函数就会执行
3.7 数字千分位格式化
let num = 123123123.012311
console.log(num.toLocaleString('en-US') // 123,123,123.01
// 在不传第二个参数的情况下,默认保留两位小数
console.log(num.toLocaleString('en-US',{
minimunFractionDigits:5
})
// 123,123,123.01231
3.8 去除数组中 boolean
值为 false
的值
es6 中的 filter
方法,可以将数组中的值进行过滤,该方法只保留回调函数返回 true
的数组项,我们就可以利用这个特性,来过滤数组中的 false
值,只保留 true
的值
Boolean 类型构造函数的使用:
// 声明一个boolean值,可以let一个变量直接赋予true或false
let bool1 = true
let bool2 = false
// 除此之外,还可以
let bool3 = Boolean(false) // false
let bool4 = Boolean('') // false
let bool5 = Boolean(1) // true
// 它会将我们传递的参数,自动转换为boolean值
所以,我们就可以这样写:
let arr = [1, 2, 2, 3, false, null, true, undefined, '']
let cleanArr = arr.filter(Boolean)
// [1, 2, 2, 3, true]
3.8 最后一点,也是至关重要的
附加篇---有意思的几道题
1. 浏览器控制台上会打印什么?
var a = 10;
function foo() {
console.log(a); // ??
var a = 20;
}
foo();
换成 let
或者 const
会打印什么?
var a = 10;
function foo() {
console.log(a); // ??
let a = 20;
}
foo();
为什么?
let
和 const
声明可以让变量在其作用域上受限于它所使用的块、语句或表达式。与 var
不同的是,这些变量没有被提升,并且有一个所谓的暂时死区(TDZ)。试图访问 TDZ 中的这些变量将引发 ReferenceError
,因为只有在执行到达声明时才能访问它们。
2. 这样的代码,会不会导致堆栈溢出?
function foo() {
setTimeout(foo, 0);
};
为什么?

3. 如何实现一个这样的效果
👏 👏 👏