共计 字 • 阅读约 min

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 方法命名

和变量命名一样,方法命名也需要尽量能够描述改方法的具体含义和作用

  • 使用驼峰命名法
  • 通过交互触发的事件,需要在事件前面加上 handleon 前缀
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();
为什么?

letconst 声明可以让变量在其作用域上受限于它所使用的块、语句或表达式。与 var 不同的是,这些变量没有被提升,并且有一个所谓的暂时死区(TDZ)。试图访问 TDZ 中的这些变量将引发 ReferenceError,因为只有在执行到达声明时才能访问它们。

2. 这样的代码,会不会导致堆栈溢出?
function foo() {
  setTimeout(foo, 0);
};
为什么?
3. 如何实现一个这样的效果

👏 👏 👏



文章更新于: 2021-4-25 17:30:3