Skip to content

Latest commit

 

History

History
468 lines (334 loc) · 8.12 KB

structure.md

File metadata and controls

468 lines (334 loc) · 8.12 KB

JavaScript 数据结构规范

数据类型及规范

Boolean

值为 true 或者 false

禁止以对象方式声明 因为封包解包可能会带来意想不到的效果

 // bad
 let bol = new Boolean(false)
 let bol2 = new Boolean(bol) // 意外的结果

 // good
 let bol = true

Null

空值,表示这里什么都没有。

判断时使用三个等号,规避隐式转换的问题

 let empty = null

 // bad
 empty == undefined // 意外的结果

 // good
 empty === null

Undefined

声明并且未赋值的变量、调用没有 return 值的方法、调用不存在的属性获得该值

判断时使用三个等号,规避隐式转换的问题

 let foo

 // bad
 foo == undefined

 // good
 foo === undefined

不建议用 undefined 直接量, 因为它可以被赋予其他的值, 并且各厂商实现方式不同, 请使用 void 0 或者自定义 getUndefined() 来获取

Number

数字类型,包含 整数浮点数

赋值应使用直接量

 // bad
 let count = new Number(0)

 // good
 let count = 0

使用 Number 函数可以将 String 转换为 Number,参数不合法返回 NaN,非字符串参数会尝试先转换成字符串再转换数字

使用 Number.parseInt 函数将合法字符串转换为整数 Number,全部非法返回 NaN ,字符前部分包含合法数字则只转换合法部分

使用 Number.parseFloat 函数将合法字符串转换为浮点数 Number,全部非法返回 NaN ,字符前部分包含合法数字则只转换合法部分

String

字符类型,用于处理字符串 文本

字符串应使用 单引号字面量 赋值,禁止使用 String 对象创建,规避解包封包问题

 // bad
 let str = new String('hello world')
 str === 'hello world' // 意外的结果

 // good
 var str = 'hello world'

关于 String 转换 Number

转换分为显示转换、显示隐式转换、隐示隐式转换

建议使用显示转换、显示隐式转换

 var str = '100.2'
 // 显式转换
 Number.parseFloat(str)
 Number(str)


 // 显式隐式转换
 +str
 str * 1


 // 隐式隐式转换,会给 debug 增加难度
 str >> n
 str * n
 str / n
 str - n

常用对象及规范

JSON

永远使用直接量创建 JSON 对象,避免使用 new Object() 声明。

JSONkey 应按照驼峰命名,

并且应当保证语义化与简洁

 // bad
 let data = {
   "0": 0, // 虽然合法,但是以下 key 语义不明
   "_": "_",
   "$-1": ""
 }
 // good
 let data = {
   key: 'value',
   data: {
     powerList: [1, 2, 3]
   },
   code: 0
 }

 // 特殊情况
 let nameList = ['苹果', '苹果', '苹果', '雪梨', '雪梨', '雪梨', 1, 2, 3, 1, 2, 3]
 let counter = {
   "苹果": 3,
   "雪梨": 3,
   "1": 2,
   "2": 2,
   "3": 2,
 }

拷贝 JSON

var obj = {
 numb: 100
}

// bad
var obj2 = JSON.parse(JSON.stringify(obj)) // 会造成性能问题

// good
var obj2 = { 
  ...obj 
}

// 深拷贝请使用递归

合并 JSON

 let obj = {
   price: 100,
   name: 'tom'
 }
 let obj2 = {
   price: 80,
   age: 30
 }

 // bad
 for(let key in obj2) {
   obj[key] = obj2[key]
 }
 // normal
 Object.assign(obj, obj2)

 // good
 {
   ...obj,
   ...obj2
 }

关于后端返回的 JSON 数据

后端返回 JSON 数据应严格按照上述要求,禁止夹带字符串格式的对象和数组

并且 key 应当用双引号包括

 // bad
 {
   "someData": "{\"name\": 1, \"age\": 2}"
 }
 // bad
 {
   "someData": "[1, 2, 3]"
 }
 // bad
 {
   "非英文 KEY": "[1, 2, 3]"
 }

为什么要给 key 加双引号包括?

因为大括号还可以表示一个代码块

 // 作为一个对象字面量
 let obj = {}

 // 作为一个代码块
 {
   alert('test')
   thisway: console.log('hello') // goto 节点
 }

考虑到 JSPNPkey 不加双引号的情况,

key 会对解释器造成困惑,因为 key : 有可能是一个 goto 的节点,

从而造成 { } 被认为是一个代码块执行。

key 被双引号包括后,解释器则明确知道,这是一个 key

Array

数组,由于 Array 构造函数的参数存在不同的语义, 建议创建数组使用直接量

 // bad
 let arr = new Array()
 // good
 let arr = []

复制与合并

let arr = [1, 2, 3]

// copy
let arr2 = [...arr]
// concat
let arr3 = [...arr2, ...arr]

Error

JS 定义了六种类型的错误

  • ReferenceError

  • TypeError

  • RangeError

  • SyntaxError

  • EvalError

  • URIError

    不建议操作原生 Error 对象

    需要抛出异常时,自定义异常对象

    // bad
    throw '这里出现了已知的异常'
    
    // good
    class MyError extends Error {
    constructor(...args) {
      super(...args)
      this.otherData = {}
    }
    }
    
    throw new MyError('这里出现了已知的异常')

NaN

表示 not a number , 不等于任何值包括自己。

数字运算遇到非法数值时返回。

可以使用 Number.isNaN 方法判断是否 not a number

 Number.isNaN('test') // true

 Number.isNaN(NaN) // true

但如果判断是否已经产生 NaN 的运算结果, 只能用不等于自己的方式判断

 let foo = 'test' - 1 // NaN

 foo ===  foo // false

请不要在代码中赋 NaN 做任何用途

Date

时间对象,可以传入时间戳、字符串获得对应的时间对象,无参数以浏览器时间为准

由于各浏览器厂商支持度,不建议直接传入 各种格式的字符串 转换时间对象。

 // bad
 let date = new Date('yyyy/MM/dd hh:mm:ss')
 // bad
 let date = new Date('yyyy')
 // bad
 let date = new Date('yyyy/MM/dd')

 // good
 let date = new Date()
 // good
 let date = new Date(1541411873158)
 // good
 let date = new Date('yyyy-MM-dd hh:mm:ss')

Promise

创建 Promise 时,传入构造函数的方法参数的定义应该为 resolve reject

 // bad
 new Promise((ok, notOk) => {
   ...
 })
 // good
 new Promise((resolve, reject) => {
   ...
 })

禁止多层嵌套 Promise,操作父级的 resolve reject

// bad
new Promise((resolve, reject) => {
  ...
  new Promise((resolve2, reject2) => {
    resolve()
  })
})

Proxy

Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。

禁止对未定义的属性返回固定或随机的值

get set 必须成对出现

// bad
var obj = {
  name: '',
  age: 100
}

var proxy = new Proxy(obj, {
    get(target, key) {
      return target[key] || 'safe value'
    }
})

obj.name
obj.other

// good
var obj = {
  name: '',
  age: 100
}

var proxy = new Proxy(obj, {
    get(target, key) {
      return target[key] || 'safe value'
    },
    set(target, key, value) {
      target[key] = value
    }
})

obj.name
obj.other

Map

Map 是 键-值 对, 其中的键不只是字符串/原生类型, 也可以是对象

由于键可以是对象, 所以当对象销毁之后将无法再获值, 而值会一直存在于 Map 实例中

let map = new Map()

// bad
map.set({a: 1}, true) // true 将无法再取出

// good
let keyObj = {a: 1}
map.set(keyObj, true)

WeakMap

WeakMapMap 变体, 当键值对象销毁时, WeakMap 会自动移除这个值

Set

Set 是成员值唯一的列表, 通过 add delete has 插入, 移除, 判断是否存在

Map 一样, 如果成员对象被销毁, 将无法再判断是否拥有

let set = new Set()

// bad
map.add({a: 1})
map.has({a: 1}) // false

// good
let keyObj = {a: 1}
map.add(keyObj)
map.has(keyObj) // true

WeakSet

WeakSetSet 变体, 当成员对象销毁时, WeakSet 会自动移除这个成员

前后端交互规范

返回数据模板

{
  "code": 0, // 请求响应状态
  "message": "", // 请求出错时返回错误原因
  "data": {} // 返回的数据体, 可以是任意数据
}

关于状态的可能值

  • 0 请求正常返回
  • -1 请求出错
  • -99 未登录