# 提升代码健壮性

# 更安全地访问数据

当我们请求完一个接口,返回一个response时,我们会下意识地用解构赋值,将data取出来然后做逻辑操作。但后台有时给的data并不是数组类型。因此用上Array.isArray检查数据类型。

const {data} = res;
Array.isArray(data) && data.forEach(()=>{});

# 空值合并运算符

假设有个对象里还嵌套着2层对象,为了取最里面的值,开发可能会这么写。

let obj = { a:{b:{c:1}}};
// 用&&挨个判断是否存在
let value = obj && obj.a && obj.a.b&& obj.a.b.c;

而在ES2020中,提出了控制合并运算符的草案,包括??和?.运算符,实现了安全访问对象属性的功能。

let obj = { a:{b:{c:1}}};
// 用了空值合并运算符后
let value = obj?.a?.b?.c;

# 封装一个安全获取对象属性API

function getObjectValueByKeyStr(obj, key, defaultVal = undefined) {
    if (!key) return defaultVal;
    let namespace = key.toString().split(".");
    let value,
        i = 0,
        len = namespace.length;
    for (; i < len; i++) {
        value = obj[namespace[i]];
        // 若未定义或为空,则返回undefinded
        if (value === undefined || value === null) return defaultVal;
        obj = value; // 下轮循环,obj将为深层
    }
    return value;
}
var x = { y: { z: 100,},};

var val = getObjectValueByKeyStr(x, "y.z");
// var val = getObjectValueByKeyStr(x, "zz");
console.log(val);

用reduce简写

function getSecurityValueByObject(obj,key){
 let keyArr = String.prototype.split.call(key,".");
 return  keyArr.reduce((pre,cur)=>{
     if(pre == null || pre == undeinfed) return undeinfed;
     return pre = pre[cur];
 },obj);
    
}
var x = { y: { z: 100,},};
var val = getSecurityValueByObject(x, "y.z");
console.log(val); // 100

# 更稳定的第三方模块

平时在项目开发中,可能会遇到一些小需求,比如日期格式化、数据深拷贝等。因为不习惯于在npm寻找模块,导入项目中来用,这之间包括了寻找模块、下载模块、导入模块、查看使用教程,需要短时间的学习成本。所以选择了自己手写一个功能供自己或项目组里的同事一起使用。

# 设备适配例子

export function getOSType() {
  const ua = navigator.userAgent

  const isWindowsPhone = /(?:Windows Phone)/.test(ua)
  const isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone
  const isAndroid = /(?:Android)/.test(ua)

  // 判断是否是平板
  const isTablet =
    /(?:iPad|PlayBook)/.test(ua) ||
    (isAndroid && !/(?:Mobile)/.test(ua)) ||
    (/(?:Firefox)/.test(ua) && /(?:Tablet)/.test(ua))

  // 是否是iphone
  const isIPhone = /(?:iPhone)/.test(ua) && !isTablet

  // 是否是pc
  const isPc = !isIPhone && !isAndroid && !isSymbian && !isTablet
  return {
    isIPhone,
    isAndroid,
    isSymbian,
    isTablet,
    isPc
  }
}

上述例子在某些设备上报错了,在添加修复之后,想到如果又除了其他新的设备,那将还是会报错。于是决定习惯去寻找npm里的模块,下载并使用它们。比如mobile-detect这个库。

# 模块导入的副作用

为实现功能而导入模块,将会带来下列问题:

  1. 会增加文件打包时的体积。
  2. 可能会给项目带来安全问题,使用前需要对源码code review。

# 在本地配置文件

在开发环境中,有时候需要配置本地的调试入口。那么可以在本地新创建一个配置文件,并在.gitignore中忽略他

// config.local.js
module.export = {
	needMock,
}
// .gitignore
config.local.js

如若在其他的电脑设备上拉代码,则需要try...catch来让其他设备环境中进入catch流程。

// mock/entry.js
// 因为只有本地有config.local.js,因此直接进入catch
try {
  const { needMock } = require('./config.local')
  if (needMock) {
    require('./index') // 对应的mock入口
    console.log('====start mock api===')
  }
} catch (e) {
  console.log('未引入mock,如需要,请创建/mock/config.local并导出 {needMock: true}')
}

最后配置启动的环境

if (process.env.NODE_ENV === 'development') {
  require('../mock/entry')
}

# Code Review

CR在开发中是非常重要的一个点,它将消除开发与需求对文档中的功能的理解。该过程中,开发可以提出自己对技术的看法、在开发过程中遇到的困难,或者对需求有更好的逻辑想法,同时可以确认每一位开发人员的进度。总结上述几点:

  1. 确认消除对需求理解的偏差。
  2. 优化代码质量,包括冗余代码、变量命名和过分封装等等,保证每个函数没有副作用、保证模块低耦合。该注释的地方需要强调等。

# 总结

提高JavaScript代码的健壮性的方法,主要有以下几点:

  1. 对接收数据进行安全检验。数组使用Array.isArray。对象则手写个迭代api,检测是否是null或undefined值。
  2. 本地环境配置,注意在.gitignore忽略到该配置文件,并写上try...catch语句控制引入,防止其他人拉代码时报错。
  3. 开发过程中,难免需要一些小功能,这时候可以选择手写,也可以选择导入模块,在确保不会给项目带来安全隐患的情况下,导入模块会更好一些,避免手写API带来的错误。
  4. 每隔一段时间施行一次Code Review,能有效减少开发与需求理解上的偏差。并且保证代码能长期保持高质量水平,易于维护。