Skip to content
az22c edited this page Mar 2, 2020 · 3 revisions

magic number特指代码中代表非数值的含义的数字。消除magic number的目的,在于提高代码的可维护性,消除令人费解的代码含义。

项目背景:在可能使用到magic number的场景中,用更加直观的数据结构替代之,以达到消除magic number。

项目介绍:一种方便select等控件遍历供选项、方便通过标识符替代或映射magic number的数据字典。

基本接口

// 声明:
const TRASH_LABEL_ARRAY = labelArray([
  { value: 0, label: '干垃圾', alias: 'TRASH_DRY' },
  { value: 1, label: '湿垃圾', alias: 'TRASH_WET' },
  { value: 2, label: '可回收垃圾', alias: 'TRASH_RECYCLE' },
  { value: 3, label: '有害垃圾', alias: 'TRASH_HARMFUL' }
]);
const log = (value) => console.log(value);

// (遍历)数据结构本身:
log(TRASH_LABEL_ARRAY) // [{ value: 0, label: '干垃圾' },{ value: 1, label: '湿垃圾' },{ value: 2, label: '可回收垃圾' },{ value: 3, label: '有害垃圾' }]

// value到label的映射:
log(TRASH_LABEL_ARRAY.labels) // value到label的映射
log(TRASH_LABEL_ARRAY.labels[0]) // 干垃圾
log(TRASH_LABEL_ARRAY.toLabel(1)) // '湿垃圾',toLabel是提供函数式调用,只在非小程序端可用

// 替代原来的magic number:
log(TRASH_LABEL_ARRAY.TRASH_RECYCLE) // 2

使用

可遍历:

<!-- 使用: -->
<el-select v-model="myForm.ad_type">
  <el-option v-for="item in TRASH_LABEL_ARRAY" :key="item.value"
   :label="item.label" :value="item.value"></el-option>
</el-select>

value到label的映射:

<!-- 使用: -->
<el-table-column label="垃圾类型">
  <template slot-scope="scope">
    {{TRASH_LABEL_ARRAY.labels[scope.row.user_type]}}
  </template>
</el-table-column>

替代原来的业务代码中的magic number:

<script>
const haldleSelect = (value) => {
 if (value === TRASH_LABEL_ARRAY.TRASH_RECYCLE) {
   // do something....
 }
};
</script>
<el-select v-model="myForm.ad_type" @onSelect="handleSelect">
  <el-option v-for="item in TRASH_LABEL_ARRAY" :key="item.value"
   :label="item.label" :value="item.value"></el-option>
</el-select>

附录

1其他数字和文字相互映射的方案

1.1 使用es6 Map结构

出现以上用到这么多辅助对象的问题,是因为js在设计之初没有实现一个完全满足map的结构。es6的Map出现,对这种结构做了一定的补充。

  • Map对象以插入的顺序遍历元素。即,可以根据声明顺序确定插入顺序。
  • for...of循环Map为每一次循环返回一个[key, value]数组。

缺点是,

  • 单单一个map无法实现反查value到key,你还需要自行invert map产生一个对象。
  • 各框架和平台迭代器的实现不齐全:值得注意的是,v-for目前还不直接支持遍历Map实例。无法一份代码,多处运行。
  • 即便有babel在中间转译,在老浏览器中还是无法使用Map的。

从 2.6 起,v-for 也可以在实现了可迭代协议的值上使用,包括原生的 Map 和 Set。不过应该注意的是 Vue 2.x 目前并不支持可响应的 Map 和 Set 值,所以无法自动探测变更。

1.2 使用lodash,把数组转成对象和invert map都是能实现的。

这种方案就是一开始声明数组类型。然后需要用到对象类型的时候生成一个。同一系列的数组和对象也是分开管理的。

2 之前做的轮子的缺点

  • 取辅助对象和求值用同一接口,dom diff 的时候可能会有性能问题(数据劫持利用函数灵活的特性同理)==> 所以改成属性访问,分开接口的职责
  • 惰性求值性价比不高+invert接口性价比不高 ==> 所以改成在创建时就生成全部辅助对象
  • 诱导用户不用变量来承载对象,这条路走不通

3 小程序退化(失败)

  • Object.defineProperty
  • 每次通过原型链继承。不需要遍历出来的属性放在原型对象上
  • Symbol.iterator