JS中的空值

尽管从第一次遇到空值引起的bug开始,我就一直要求自己注意空值,但还是经常犯这样的错误,JS中的空值真的需要多加注意。这里说的空值包括undefinednull

为什么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。而且这种空值只在少部分情况下出现,很容易躲过测试。

1.

function fn (obj) {
  console.log(obj[key].id);
}

这里obj或者obj[key]为空都会导致报错。

2.

function fn (authArray) {
  authArray.forEach(function (i, auth) {
    console.log(auth.visible);
  })
}

这种形式就更具迷惑性,你很容易就觉得authArray里面肯定都是auth吧,既然是auth肯定有visible属性吧。然而并没有人能保证这一点,authArray里面可能有别的数据:空值或者没有visible属性的auth对象。

3.

function fn (authIdArray, authMap) {
  authIdArray.forEach(function (i, authId) {
    var auth = authMap[authId];
    console.log(auth.visible);
  })
}

同样,看到authIdArrayauthMap,很容易觉得它们一定是一一对应的,应该可以取到一个auth对象。

总而言之,JS并不能保证数据的结构化,而开发过程中很容易默认取到的是结构化数据。

怎么解决

遇到空值bug有两种解决方案。

  1. 在取值的地方加判断

    function fn (auth) {
      var val = obj[key];
      if (val) {
        console.log(val.id);
      }
    }
    

    还有一种写法:

    function fn (obj) {
      console.log(obj[key] && obj[key].id);
    }
    
  2. 在变量声明和赋值的地方保证数据结构一致
    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{}的变量的注释尤其要详细。

原文:大专栏  JS中的空值

原文地址:https://www.cnblogs.com/wangziqiang123/p/11632063.html

时间: 2024-10-12 05:08:21

JS中的空值的相关文章

关于js中空值比较和传值的问题

昨天写导出功能时,有个条件审核状态,其中一个审核状态的key为0,我也是醉了. 然后我注意到这方面的问题,在网上找了找,我的理解可能有点问题.但是目前也就是这样了,以后在学习吧! 正文: js中各种类型都是有个默认空值的,这个空值转成boolean类型的时候就是false null.undefined.0."" 这些数值都是其对应数据类型上的无效值或空值.还有这五个值作!运算,结果全为:true. 还有如果是json请求传值的话,就是说如果转成string类型的话: 1 String(

js中的null和undefined

null为js中的keyword,表示空值.null能够看作是object类型的一个特殊值,假设一个object的值为null,表示这个对象不是有效的对象. 这里的null和0不是一回事.在c++中null通常定义为0,可是在js中并非这样. undefined不是jskeyword,其为一个全局变量,即Global的一个属性,一下3种情况会返回undefined: 1.使用了一个没有定义的变量: 2.使用了已经定义但未赋值的变量.             3.使用了一个对象属性.可是该属性不存

聊一聊js中的null、undefined与NaN

零.寒暄 翻翻自己的博客,上一篇竟然是六月26号的,说好的更新呢?回顾刚刚过去的这个七月,整天都是公司的入职培训加上自己的小论文,每天奋战到凌晨1点多,这是要挂的节奏啊!但是不论怎么说,自己的时间管理还是出了问题,以后一定要好好的规划一下,不多说,来正题! 自己为什么要写这篇文章呢?因为在吃完早饭,消化的时候,整了几行代码,然后发现整挂了... 大致代码如下: function getByClass(clsName, parent){ if(parent=="undefined"){

浅谈js中的数据类型,使用typeof获取js数据类型

JS中的数据类型 1):Undefined——值未定义 注:Undefined类型只有一个值,即特色的undefined.在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined 2):boolean——布尔值 注:true和false是区分大小写的.也就是说,True和False(以及其他的混合大小写形式)都不是Boolean值,只是标识符 3):string——字符串 注:用双引号和单引号表示的字符完全相同 4):number——数值 注:表示整数和浮点数值 5):Nul

JS中获取CSS样式的方法

1.对于内联样式,可以直接使用ele.style.属性名(当然也可以用键值对的方式)获得.注意在CSS中单词之间用-连接,在JS中要用驼峰命名法 如 <div id="dv" style="width: 100px;height: 200px;background-color: pink; border: 1px solid green;"></div> <script> var dv = document.getElementB

js中获取时间new date()的用法

js中获取时间new date()的用法 获取时间:   var myDate = new Date();//获取系统当前时间 获取特定格式的时间: 1 myDate.getYear(); //获取当前年份(2位) 2 myDate.getFullYear(); //获取完整的年份(4位,1970-????) 3 myDate.getMonth(); //获取当前月份(0-11,0代表1月) 4 myDate.getDate(); //获取当前日(1-31) 5 myDate.getDay();

JS中的运算符&amp;JS中的分支结构

一.JS中的运算符 1.算术运算(单目运算符) + 加.- 减.* 乘./ 除.% 取余.++ 自增.-- 自减 >>> +:有两种作用,连接字符串/加法运算.当+两边全为数字时,进行加法运算: 当+两边有任意一边为字符串时,起连接字符串的作用,连接之后的结果为字符串 除+外,其余符号运算时,会先尝试将左右变量用Number函数转为数字 >>> /: 结果会保留小数点 >>> ++: 自增运算符,将变量在原有基础上+1: --: 自减运算符,将变量在原

Js中的数据属性和访问器属性

Js中的数据属性和访问器属性 在javaScript中,对象的属性分为两种类型:数据属性和访问器属性. 一.数据属性 1.数据属性:它包含的是一个数据值的位置,在这可以对数据值进行读写. 2.数据属性包含四个特性,分别是: configurable:表示能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或能否把属性修改为访问器属性,默认为true enumerable:表示能否通过for-in循环返回属性 writable:表示能否修改属性的值 value:包含该属性的数据值.默

在Node.js中使用RabbitMQ系列二 任务队列

在上一篇文章在Node.js中使用RabbitMQ系列一 Hello world我有使用一个任务队列,不过当时的场景是将消息发送给一个消费者,本篇文章我将讨论有多个消费者的场景. 其实,任务队列最核心解决的问题是避免立即处理那些耗时的任务,也就是避免请求-响应的这种同步模式.取而代之的是我们通过调度算法,让这些耗时的任务之后再执行,也就是采用异步的模式.我们需要将一条消息封装成一个任务,并且将它添加到任务队列里面.后台会运行多个工作进程(worker process),通过调度算法,将队列里的任