JS中的空值
尽管从第⼀次遇到空值引起的bug开始,我就⼀直要求⾃⼰注意空值,但还是经常犯这样的错误,js中的空值真的需要多加注意。这⾥说的空值包括undefined和null
为什么js容易出现空值bug?
⾸先JS是⼀个动态类型语⾔,与之相对的是静态类型语⾔如Java。在Java中要定义数据模型意味着定义⼀个类——JavaBean,⽆论这个数据模型的结构多么简单。在JS中就简单的多:
var auth = {
visible: true,
editable: true
}
这种灵活的⽅式带来了很⼤的便捷,但这种便捷是有代价的。JS并不能保证所有的auth对象是同样的结构,auth.visible并不⼀定是布尔型,有可能是undefined或者其它类型的数据。尽管定义⼀个JavaBean有些⿇烦,Java中不会出现这样的情况——你可以确定⼀个auth对象⼀定有⼀个布尔型的visible属性。
空值bug会以哪些形式出现?
有的时候定义数据时会默认{}或者undefined就是{visible: false, enbale: false},或者{name: 'afei'}就是{name: 'afei': phone: ''}。在使⽤数据时很容易遗漏这种隐藏逻辑,从⽽引发bug。⽽且这种空值只在少部分情况下出现,很容易躲过测试。
function fn (obj) {
console.log(obj[key].id);
}
这⾥obj或者obj[key]为空都会导致报错。
function fn (authArray) {
authArray.forEach(function (i, auth) {
console.log(auth.visible);
})
}
这种形式就更具迷惑性,你很容易就觉得authArray⾥⾯肯定都是auth吧,既然是auth肯定有visible属性吧。然⽽并没有⼈能保证这⼀
点,authArray⾥⾯可能有别的数据:空值或者没有visible属性的auth对象。
function fn (authIdArray, authMap) {
authIdArray.forEach(function (i, authId) {
var auth = authMap[authId];
console.log(auth.visible);
})
}
同样,看到authIdArray和authMap,很容易觉得它们⼀定是⼀⼀对应的,应该可以取到⼀个auth对象。
总⽽⾔之,JS并不能保证数据的结构化,⽽开发过程中很容易默认取到的是结构化数据。
怎么解决
遇到空值bug有两种解决⽅案。
在取值的地⽅加判断
function fn (auth) {
var val = obj[key];
if (val) {
js的基本数据类型console.log(val.id);
}
}
还有⼀种写法:
function fn (obj) {
console.log(obj[key] && obj[key].id);
}
在变量声明和赋值的地⽅保证数据结构⼀致
var auth = {
visible: booleanVal || false
}
authArray.push(authObj || {});
authMap[id] = authObj || {};
如果auth的某个属性还不是基础类型,可能更⿇烦⼀些——不能⽤{}来代替auth。
第⼀种⽅法改动起来更简单,但是第⼆种⽅法能保持数据结构⼀致,有利于代码的长期维护。⼀种⽐较中庸的⽅法是:在私有⽅法中声明和赋值时保证数据的结构⼀致,在公共⽅法中接收的变量则检查数据结构(JS不区分公共和私有⽅法,⼀个约定俗成的做法是私有⽅法加上下划线前缀)。
从TypeScript得到的启⽰
TypeScript是⼀种由微软开发的⾃由和开源的编程语⾔。它是JavaScript的⼀个严格超集,并添加了可选的静态类型和基于类的⾯向对象编程。所谓可选的静态类型可以理解为灵活范式——本来是⽆范式的。TypeScript可以声明参数或属性的数据类型,在编译时进⾏类型检查,也可以通过any类型来跳过类型检查。使⽤TypeScript后IDE的补全和跳转可以像静态语⾔⼀样,对开发者更加友好,数据类型引起的bug也会⼤⼤减少。从使⽤者评价来看,⼤型项⽬中使⽤TypeScript有助于提⾼开发效率和减少bug。
fx-code⼯程引⼊TypeScript的障碍有两点:⼀是⼯程中没有实现模块化⽽是AllInOne模式,不能在⼀个组件中引⼊另外⼀个组
件,TypeScript也就没办法发挥作⽤。⼆是遗留代码需要编写⼤量的声明⽂件,⽽且需要开发者转换静态类型语⾔的思维。
在引⼊之前或者不引⼊的话,我们也可以通过注释来起到申明数据类型的作⽤。
/**
* @param fields {Array<Field>}
* @interface
*  Field {
*  name: string,
*  text: string,
*  enbale: boolean | undefined,
*  layout: 6 | 12
*  }
* @return void
*/
function fn (fields, fieldAuth) {
// ...
}
对于可能为undefined和{}的变量的注释尤其要详细。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。