JavaScript 规范整理

栏目: JavaScript · 发布时间: 4年前

内容简介:如果不熟悉英语,可以使用错误示例正确示例

不要使用拼音命名

如果不熟悉英语,可以使用 Codelf 或者 Google 翻译 ,避免使用拼音命名。

错误示例

// 这里是用户状态
const yongHuZhuangTai = 1

正确示例

const userStatus = 1

函数中的变量

js 中普通变量使用 小写开头驼峰命名法 ,而非不区分大小写,或使用下划线命名等等。

错误示例

// 用户操作日志备注
const useroperatinglogremark = '新增用户'

正确示例

const userOperatingLogRemark = '新增用户'

内部变量

如果需要不想让使用者使用的属性(能够看到),需要使用下划线开头。例如 _value ,代表内部的值,外部不应该直接访问(实际上可以做到)。

class DateFormat {
  constructor(fmt) {
    // 不想让外部使用
    this._fmt = fmt
  }
  format(date) {
    // 具体格式化代码
  }
  parse(str) {
    // 具体解析代码
  }
}

不要使用无意义的前缀命名

如果一个对象的变量名已经很好的标识了该对象,那么内部的属性就不能使用对象名作为前缀!

错误示例

// 活跃的日志信息
const activeLog = {
  activeUserId: 'rx',
  activeTime: new Date(),
}

正确示例

const activeLog = {
  userId: 'rx',
  time: new Date(),
}

ES6

优先使用 const/let

一般情况下,使用 const/let 声明变量,而不是使用 var 。因为使用 var 声明的变量会存在变量提升。

示例代码

;(function() {
  // 使用 var 声明的变量(初始值为 undefined)
  console.log(i)
  i = 1
  console.log(i)
  // 此时使用 var 声明的变量 i 相当于在 function 顶部声明,然后在此处进行了赋值操作
  var i = 0

  // 使用 const 声明的变量(抛出异常 k is not defined)
  // console.log(k)
  k = 1
  const k = 0
})()

关于可以参考 let 与 var 在 for 循环中的区别

使用新的函数声明方式

ES6 推出了一种更简洁的函数声明方式,不需要在写 function ,只要 名字 + () 即可在 classObject 中声明函数。

错误示例

const user = {
  name: 'rx',
  hello: function() {
    console.log('hello' + this.name)
  },
}

正确示例

const user = {
  name: 'rx',
  hello() {
    console.log('hello' + this.name)
  },
}

优先使用箭头函数而非 function

优先使用 箭头函数 而不是使用传统的函数,尤其是使用 匿名函数 时,更应如此。

错误示例

const sum = [1, 2, 3, 4]
  // 过滤出偶数
  .filter(function(i) {
    return i % 2 === 0
  })
  // 将偶数翻倍
  .map(function(i) {
    return i * 2
  })
  // 计算总和
  .reduce(function(res, i) {
    return (res += i)
  })

console.log(sum)

正确示例

const sum = [1, 2, 3, 4]
  // 过滤出偶数
  .filter(i => i % 2 === 0)
  // 将偶数翻倍
  .map(i => i * 2)
  // 计算总和
  .reduce((res, i) => (res += i))

console.log(sum)

不要使用 if 判断再赋予默认值

如果函数需要对参数做默认值处理,请不要使用 if 判空之后再修改参数,而是使用 ES6 的 默认参数解构赋值

主要优点

  • 减少代码,JavaScript 是动态语言,维护起来较为麻烦,代码越少,错误越少
  • 清晰明了,可以让人一眼就能看出这个参数的默认值,而不需要关心函数内部的逻辑
  • IDE 大多对此进行了支持,代码提示时便会告诉我们参数是可选的并且有默认值

错误示例

/**
 * 格式化日期
 * @param {Date} [date] 日期对象。默认为当前时间
 * @return {String} 格式化日期字符串
 */
function formatDate(date) {
  if (date === undefined) {
    date = new Date()
  }
  return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
}

正确示例

function formatDate(date = new Date()) {
  return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
}

这里如果展开来讲实在太多,请参考 JavaScript 善用解构赋值

优先使用 Map 做键值对映射而非传统的对象

如果需要 键值映射 ,不要使用一般的对象,而是用 ES6 的 Map 。它不仅可以使用 任意类型的键 ,另外 Map 本身也是 有序 的哦

错误示例

const obj = {
  2: '琉璃',
  1: 'rx',
  '1': 'liuli',
}
// 结果为 true,因为属性 1 实际上会被转换为 '1'
console.log(obj[1] === obj['1'])
// 结果为 [ '1', '2'],因为是按照属性字符串 排序 的
console.log(Object.keys(obj))

正确示例

const map = new Map()
  .set(2, '琉璃')
  .set(1, 'rx')
  .set('1', 'liuli')
// 结果为 false
console.log(map.get(1) === map.get('1'))
// 结果为 [ 2, 1, '1' ],因为是按照插入顺序排序的
console.log(Array.from(map.keys()))

优先使用模板字符串拼接多个字符串变量

如果需要拼接多个对象以及字符串时,不要使用 + 进行拼接,使用 es6 的 模板字符串 会更好一点。一般而言,如果需要拼接的变量超过 3 个,那么就应该使用模板字符串了。

错误示例

function hello(name, age, sex) {
  return 'name: ' + name + ', age: ' + age + ', sex: ' + sex
}

正确示例

function hello(name, age, sex) {
  return `name: ${name}, age: ${age}, sex: ${sex}`
}

当独立参数超过 3 个时使用对象参数并解构

错误示例

function hello(name, age, sex) {
  return `name: ${name}, age: ${age}, sex: ${sex}`
}

正确示例

function hello({ name, age, sex }) {
  return `name: ${name}, age: ${age}, sex: ${sex}`
}

不要写多余的 await

如果 await 是不必要的(在返回语句时,那么就不要用 async 标识函数),这是没有必要的 – 除非,你需要在这个函数内异步操作完成后有其他操作。

错误示例

const login = async ({ username, password }) => {
  if (!useranme) {
    console.log('用户名不能为空')
    return
  }
  if (!password) {
    console.log('密码不能为空')
    return
  }
  // 真正发起登录请求
  return await userApi.login(user)
}

正确示例

const login = ({ username, password }) => {
  if (!useranme) {
    console.log('用户名不能为空')
    return
  }
  if (!password) {
    console.log('密码不能为空')
    return
  }
  // 真正发起登录请求
  return userApi.login(user)
}

不要使用 == 进行比较

在 js 中使用 == 比较相当危险,你永远不知道 js 到底是按照什么类型比较的,因为 js 会做各种隐式转换。而如果使用 === 比较,则会同时比较 类型 是否都相同,避免了各种不确定的问题。

错误示例

console.log(1 == true) // true
console.log(1 == '1') // true
console.log('1' == true) // true
console.log('0' == true) // false
console.log([] == []) // false

扪心自问,你真的知道上面为什么会出现这种结果么?即便知道,对于其他人而言仍然是难以预测的,所以抛弃掉 == 吧,学会使用更好的 === 最好

console.log(1 == true) // false
console.log(1 == '1') // false
console.log('1' == true) // false
console.log('0' == true) // false
console.log([] == []) // false

使用计算属性名替代使用方括号表示法赋值

目前而言已经有了 计算属性名 用以在初始化时计算属性名,所以不需要再先声明对象再使用 方括号表示法 进行赋值了。

ES5 写法

const state = {
  'user.username': function() {},
}

state[Date.now()] = new Date()

console.log(state)

ES6 写法

const state = {
  'user.username'() {},
  [Date.now()]: new Date(),
}

console.log(state)

简单的选项列表优先使用 Map 而非数组

对于复选框,想必很多人相当熟悉。下面使用 js 模拟一个复选框

const item = {
  id: 1,
  role: ['1', '2'],
  name: '',
}
const options = [
  {
    roleid: '1',
    label: '黄金糕',
  },
  {
    roleid: '2',
    label: '双皮奶',
  },
  {
    roleid: '3',
    label: '蚵仔煎',
  },
]

现在的需求是根据 role 计算显示值 name 的值

item.name = item.role
  .map(role => options.find(op => op.roleid === role))
  .filter(s => s)
  .join(',')

但实际上这里应该使用 Map 替代数组,因为数组的 find 其实非常低效,也需要进行遍历,使用 Map 的实现

const item = {
  id: 1,
  role: [1, 2],
  name: '',
}
const options = new Map()
  .set(1, '黄金糕')
  .set(2, '双皮奶')
  .set(3, '蚵仔煎')

function calcName(role) {
  return role
    .map(k => options.get(k))
    .filter(s => s)
    .join(',')
}

item.name = calcName(item.role)

可以看到,获取时使用了 Map#get ,在效率上应该是极好的。

附: 该问题来自 https://segmentfault.com/q/1010000019426996

存放 id 标识列表使用 Set 而非数组

还是上面的例子,当你需要存取当前选中复选框的值 role 使用数组时,有可能遇到 id 重复的问题,实际上导致每次添加前需要使用 Array#includes 判断是否已存在。这里可以使用 Set 从数据结构层面避免掉可能重复的问题。

修改后的实现

const item = {
  id: 1,
  role: new Set([1, 2]),
  name: '',
}
const options = new Map()
  .set(1, '黄金糕')
  .set(2, '双皮奶')
  .set(3, '蚵仔煎')

function calcName(role) {
  return Array.from(role)
    .map(k => options.get(k))
    .filter(s => s)
    .join(',')
}

item.name = calcName(item.role)

先判断是否存在执行某些操作也非常方便,可以使用 Set#has 进行判断,当然时间复杂度时 O1。

逻辑代码

不要判断一个 Boolean 值并以此返回 Boolean 值

不要在得到一个 Boolean 的值后使用 if-else 进行判断,然后根据结果返回 truefalse ,这真的显得非常非常蠢!

错误示例

// 模拟登录异步请求
const login = async ({ username, password }) => {
  const res = username === 'rx' && password === 'rx'
  if (res) {
    return true
  } else {
    return false
  }
}

正确示例

// 模拟登录异步请求
const login = async ({ username, password }) =>
  username === 'rx' && password === 'rx'

不要使用多余的变量

如果一个表达式立刻被使用并且只会被使用一次,那就不要使用变量声明,直接在需要的地方使用好了。

错误示例

// 模拟登录异步请求
const login = async ({ username, password }) => {
  const res = username === 'rx' && password === 'rx'
  return res
}

正确示例

// 模拟登录异步请求
const login = async ({ username, password }) => {
  return username === 'rx' && password === 'rx'
}

不要使用嵌套 if

不要使用多级的 if 嵌套,这会让代码变得丑陋且难以调试,应当优先使用 提前 return 的策略。

错误示例

// 模拟登录异步请求
const login = async ({ username, password }) =>
  username === 'rx' && password === 'rx'

async function submit(user) {
  const { username, password } = user
  if (username) {
    if (password) {
      const res = await login(user)
      if (res) {
        console.log('登录成功,即将跳转到首页')
      } else {
        console.log('登录失败,请检查用户名和密码')
      }
    } else {
      console.log('用户密码不能为空')
    }
  } else {
    console.log('用户名不能为空')
  }
}

正确示例

// 模拟登录异步请求
const login = async ({ username, password }) =>
  username === 'rx' && password === 'rx'

async function submit(user) {
  const { username, password } = user
  if (!username) {
    console.log('用户名不能为空')
    return
  }
  if (!password) {
    console.log('用户密码不能为空')
    return
  }
  const res = await login(user)
  if (!res) {
    console.log('登录失败,请检查用户名和密码')
    return
  }
  console.log('登录成功,即将跳转到首页')
}

不要先声明空对象然后一个个追加属性

有时候会碰到这种情况,先声明一个空对象,然后在下面一个个追加属性,为什么创建对象与初始化不放到一起做呢?

错误示例

// 模拟登录异步请求
const login = async ({ username, password }) =>
  username === 'rx' && password === 'rx'

async function submit(username, password) {
  // 数据格式校验处理。。。

  const user = {}
  user.username = username.trim()
  user.password = password.trim()

  const res = await login(user)

  // 后续的错误处理。。。
}

正确示例

// 模拟登录异步请求
const login = async ({ username, password }) =>
  username === 'rx' && password === 'rx'

async function submit(username, password) {
  // 数据格式校验处理。。。

  const user = {
    username: username.trim(),
    password: password.trim(),
  }

  const res = await login(user)

  // 后续的错误处理。。。
}

不要使用无意义的函数包裹

使用函数时,如果你想包裹的函数和原来函数的参数/返回值相同,那就直接应该使用函数作为参数,而非在包裹一层。给人的感觉就像是大夏天穿着棉袄吃雪糕 – 多此一举!

错误示例

// 判断是否是偶数的函数
const isEven = i => i % 2 === 0
//过滤出所有偶数
const res = [1, 2, 3, 4].filter(i => isEven(i))

正确示例

// 判断是否是偶数的函数
const isEven = i => i % 2 === 0
//过滤出所有偶数
const res = [1, 2, 3, 4].filter(isEven)

不要使用三元运算符进行复杂的计算

三元运算符适合于替代简单的 if-else 的情况,如果碰到较为复杂的情况,请使用 if + return 或者解构/默认参数的方式解决。

错误示例

function formatUser(user) {
  return user === undefined
    ? 'username: noname, password: blank'
    : 'username: ' +
        (user.username === undefined ? 'noname' : user.username) +
        ', password: ' +
        (user.password === undefined ? 'blank' : user.password)
}

看到上面的代码就感觉到一股烂代码的味道扑面而来,这实在是太糟糕了!实际上只需要两行代码就好了!

正确示例

function formatUser({ username = 'noname', password = 'blank' } = {}) {
  return `username: ${username}, password: ${password}`
}

如果变量有所关联则使用对象而非多个单独的变量

如果变量有所关联,例如一个表单,存储的时候不要使用单独的变量,将之存储到一个表单变量中更好。

错误示例

function login(){
  const username = document.querySelector('#username')
  const password = document.querySelector('#password')
  const remeberMe = document.querySelector('#remeberMe')

  // 一些校验。。。
  if (!validate(username, password, remeberMe)) {
    return
  }
  // 请求后台
  const res = await userLogin.login(username, password, remeberMe)
  // 后处理。。。
}

正确示例

function login(){
  const user = {
    username : document.querySelector('#username'),
    password : document.querySelector('#password'),
    remeberMe : document.querySelector('#remeberMe'),
  }
  // 一些校验。。。
  if (!validate(user)) {
    return
  }
  // 请求后台
  const res = await userLogin.login(user)
  // 后处理。。。
}

应该尽量解决编辑器警告

如果编辑器对我们的代码发出警告,那么一般都是我们代码出现了问题(一般开发人员的能力并不足以比肩编辑器 #MS 那些 dalao 的能力)。所以,如果出现了警告,应该先去解决它 – 如果你确认发生了错误,则通过注释/配置禁用它!

使用类型定义参数对象

如果一个函数需要一个对象参数,最好专门定义一个类型,并在注释上说明,便于在使用时 IDE 进行提示,而不需要去查找文档手册。

错误示例

/**
 * 格式化用户
 * @param {Object} user 格式化的用户对象
 */
function formatUser(user) {
  const { username, password } = user || {}
  return `user, username: ${username}, password: ${password}`
}

// 此处别人并不知道 User 里面到底有什么属性,只能去查看文档
const str = formatUser({ username: 'rx', password: '123456' })
console.log(str)

正确示例

class User {
  constructor(username, password) {
    this.username = username
    this.password = password
  }
}

/**
 * 格式化用户
 * @param {User} user 格式化的用户对象
 */
function formatUser(user) {
  const { username, password } = user || {}
  return `user, username: ${username}, password: ${password}`
}

const str = formatUser(new User('rx', '123456'))
console.log(str)

尽量扁平化代码

尽量将 a 调用 b , b 调用 c ,然后 b 调用 d ,优化为依次调用 a, b, c, d

注意: 这里使用的是 尽量 而非 不要 ,深层嵌套不可避免,但在局部上,应该采取扁平化的策略, 提前 return 避免嵌套 if-else 是个很好的例子。


以上所述就是小编给大家介绍的《JavaScript 规范整理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

九败一胜

九败一胜

李志刚 / 北京联合出版公司 / 2014-9-1 / 42.00元

所有的创业者都面临着很多问题,困惑不是个人的,是有共性的。 除了自身去摸索着石头走路,他们还可以通过学习,从那些在创业路上走得更远的创业者身上学到经验、教训。 这本书的主角——王兴,恰好就是一个很好的学习对象。出生于1979年的王兴,很早就创业了,2004他就开始和同学一块创业,2005年做出了校内网;2007年,他又做出了饭否网——这是中国最早的类似twitter的网站。 ......一起来看看 《九败一胜》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具