解构赋值默认值误区

综合技术 2018-05-18

问题还原

这是最近 CR 的时候在业务代码中发现了一个问题,先来看一下问题代码:

// data 为接口返回的数据
const { bizObject = {}, total = 0 } = data.result || {};
const list = bizObject.list || [];

// 其他逻辑,比如把 list 更新到 state 中,等等
  • A 接口正常的情况: data
    中有 result
    属性,且 result
    对象中 bizObject
    返回了一个数—— :white_check_mark:
  • B 接口异常: data
    对象中没有 result
    属性 —— :white_check_mark:
  • C 接口异常: data
    中有 result
    属性, result
    对象中也有 bizObject
    属性,但是, bizObject
    属性的值是 null
    ,然后呢?

从上下文来看,这位同学应该是期望解构赋值按以下方式执行:

const result = data.result || {};
const bizObject = result.bizObject || {};
// ...

但是,C 情形抛异常了:

Uncaught TypeError: Cannot read property 'list' of null

也就是 bizObject
的值是 null
而不是期望的 {}
。为什么呢?

解构赋值中的默认值

A variable can be assigned a default, in the case that the value unpacked from the object/array is undefined
.

数组、对象解构赋值时,只有当属性(数组索引对应的值)值为 undefined
时,才会使用默认值。

问题代码中,当 bizObject
null
时,解构出来的就是 null
,读取 null
list
属性,不报错才怪。

函数默认参数

再来看看函数的默认参数是不是同样的逻辑:

function doSomething(options = { foo: 'bar' }) {
  console.log(options);
}

doSomething(); // { foo: "bar" }
doSomething(undefined); // { foo: "bar" }
doSomething(null); // null

不传参数(隐式的 undefined
)或者显示地传递 undefined
,使用了默认参数,传 null
的时候没有使用默认值,和解构赋值的默认值同样的逻辑。

其实把上面的函数转成 ES5,就能直观地了解其逻辑:

function doSomething() {
  var options =
    arguments.length > 0 && arguments[0] !== undefined
      ? arguments[0]
      : {
          foo: "bar"
        };
  console.log(options);
}

解构赋值的默认值也一样:

// ES6
const { a = 1 } = { };

// 转换成 ES5
var _ref = {},
  _ref$a = _ref.a,
  a = _ref$a === void 0 ? 1 : _ref$a;

写在最后

之所以会有同学把解构赋值默认值等同于 const bizObject = result.bizObject || {}
,可能是对 ES6 的一些细节了解得不够透彻,可以多翻翻文档:

还有一个可能,前端同学并没有误解解构赋值默认值的工作原理,只是接口不规范引发了异常。一般而言,
接口约定好字段、类型后,就应该始终按约定的类型返回数据,约定的是对象,那没有数据的时候也应该返回一个空对象,即使不返回这个字段,前端也已经判断了,莫名其妙地返回一个 null
算哪门子事

接口写得有问题,有的人沟通一下还是会调整,有的人就始终一副「放荡不羁」样子,通了就行,才不管你什么规范、约定……对于不讲究的人,还是自己多写两行代码判断一下,说多了也是浪费,你懂的。

很多事情都是 100% 的期望,然后妥协,接受一个差不多的结果。(BGM 差不多先生 - MC Hot Dog

责编内容by:CSSPod (源链)。感谢您的支持!

您可能感兴趣的

PDF Tricks PDF Tricks This page is a summary of PDF tricks, either based on data encodings, JavaScript, or PD...
Node.js for Asynchronous Linux Scriptin... In other programs I've written, I've enjoyed the asynchronous aspects of node.js using promises ...
Creating a web based DBMS with 414 lines of code Most frameworks can’t even wire up their Ajax requests with 414 lines of code, and before stuff su...
riot.js教程【五】标签嵌套、命名元素、事件、标签条件... 标签嵌套 让我们定义一个父标签account,一个子标签subscription{ opts.plan.name }// Get JS handle to opti...
Find all words containing `p` ... Say, str = 'python php ruby javascript jsonp perhapsphpisoutdated' How do you find all words th...