如果我们停止使用 null 会怎样?(译)
初见
目的:一边学技术一边学英文。😋
小编翻译过程可能不会按原文一句一字翻译,稍微会插入一些自己的理解与补充,但是原文本意不会改变,如有错误之处,欢迎指出,也望海涵。
原文链接 👈👈👈
遵纪守法,科学上网。🪜🪜🪜
相识
目前,DOM
和浏览器 API
在某些情况下有时使用 null
,有时使用 undefined
。虽然它们有其历史原因,但这种不一致的结果导致我们不得不更多地依赖类型转换,但这会带来一系列的问题。
这句话可能读着有点懵😵,我们来用实际例子说明:
例如:当我们使用
document.getElementById
方法但没有找到对应元素时,它会返回null
;而当我们声明一个变量但没有赋值,那么这个变量的默认值就是undefined
。就是这种不一致性可能导致开发者在比较值时必须更频繁地依赖类型转换,这可能会引入错误和混淆。
不过,这次我想讨论的是,如果"我们在用户代码中停止使用 null
并坚持只使用 undefined
,我们会得到什么呢?"
统一语义需要考虑的事情更少
语义上真的有那么大的区别吗?对我来说,null
和 undefined
的含义几乎是相同 - 缺少值。
语义上,我们经常用
null
表示一个对象为空或者对象属性没有值并且没有引用任何对象,而用undefined
表示一个变量被声明但没有初识化,也就是值为空的情况。
const map = new Map();
map.get('abc'); // undefined
localStorage.get('abc'); // null
在同一种缺值的情况下,我们得到却是两个不一样的结果,有时是 undefined
有时是 null
,这对开发者的代码真的有区别吗?
现在全局undefined是安全的
在 ECMA 规范 1 到 5.1 之前,规范中是没有定义 undefined
作为一个全局属性的,只有 undefined
的类型和值。因此,为了更安全地使用 undefined
,你需要在全局范围内对其进行一些保护措施,否则,一旦 undefined
被不小心或者某些其他原因篡改,这个后果是不可预测的。
window.undefined = '¯\_(ツ)_/¯'; // 篡改undefined
var a;
a === undefined; // ECMA1~5.1之前是:false;后面版本的是true
从 ECMA 规范 6.0 / 2015开始,全局 undefined
是只读的,不会被篡改,因此可以安全使用。
意图明确
如果你需要检查一个对象的属性是否存在,可以使用 in
运算符,虽然它很少被使用到,但 in
运算符的设计目的就是为此而存在的,以便你可以轻易区分对象属性是否已经定义或者是已经定义但还未定义其值。
const obj = {a: undefined};
'a' in obj; // true
'b' in obj; // false
请注意,如果对象未定义自己的属性,in
运算符将往原型链上查找。如果你希望只检查对象本身的属性,可以使用 hasOwnProperty
。
这段话看着可能难以理解作者想表达的意图,小编反复阅读原文的语句,个人理解总结如下:
当你想检查对象属性是否存在时,可以直接使用
in
运算符或者hasOwnProperty
方法,这没毛病吧,很直观。而当你想知道对象属性的值是否为"空"的时候,如果我们只使用
undefined
的话,那我们直接判断一下就行了,但是此时如果再引入null
来表达空的情况,事情就变得稍微麻烦一点了。业务数据的统计要是严格一点,我们就会陷入是否要区分两者情况的境地。如果再有引入"0"的情况就更麻烦了,这些情况小编曾经遇到过。😐
![]()
typeof
众所周知,Javascript
中的 typeof null
会返回一个对象类型(object
)。无论该语言早期背后的设计目标是什么,从开发者角度来看,它都没有多大意义。因为 null
是一个"空"值,通常你希望将其与其他值类型区分开来。而实际上,我们为了进行正确的类型检查,最终都会检查 typeof
和 !==null
。
在 undefined
情况下,typeof undefined
返回 undefined
类型,这在我们检查空值类型时,给我们提供了一个很好的、一致的且直观的类型。
其实说的就是
typeof null
没有typeof undefined
好用,前者typeof
后你还是很难确定它是谁,后者直接就能确定了。😗
JSON.stringify
当我们序列化数据时,JSON.stringify()
会删除所有未定义值的属性,这很好,因为我们在进行网络请求或其他地方的时候就不需要发送它们了。
JSON.stringify({b: undefined, c: null}); // {"c": null}
但是,如果你想保持未定义值的属性不被删除,这很容易做到:
JSON.stringify(
{b: undefined, c: null},
(key, value) => value === undefined ? 'undefined' : value
); // {b: "undefined", "c": null}
其实就是转成字符串。
对于 JSON.parse()
也是如此。
函数-默认值
自 ECMAScript 2015 起(其实就是ES6),Javascript
语言中添加了函数默认值参数,如果你不传递参数或者传递 undefined
,默认值都将生效。
const f = (a = 1) => a;
f(); // 1
f(undefined); // 1
f(null); // null
此时,我们应该注意到 Javascript
语言实际上希望我们使用 undefined
来表示空值,而不是 null
。
解构赋值-默认值
ES2015 中也引入了解构赋值-默认值,其工作方式与函数默认值相同。
const {a = 1} = {a: null};
a; // null
const {a = 1} = {};
a; // 1
const {a = 1} = {a: undefined};
a; // 1
const [a = 1] = [];
a; // 1
const [a = 1] = [undefined];
a; // 1
const {a: {b = 1}} = {a: {b: undefined}};
b; // 1
默认值现在变得更加有用,因为它们不仅可以在函数参数中使用,还可以在数组、对象甚至嵌套对象和数组中使用。
类型语言和注释
由于 JavaScript
类型注释的兴起,我们还需要让类型系统单独识别 null
和 undefined
类型。
类型系统指
Typescript
、Flow
、JSDoc
等等。如果没有null
类型,这些类型系统也能省点事了。😗
例如,在 Flow
中,对于最终存在的值有一个特殊的表示法,称为 Maybe Types。
// @flow
const acceptsMaybeNumber = (value: ?number) => {
// ...
/* 下面一句话的解释表示我们要多加这些长长的判断
if (value !== undefined && value !== null) {
return value * 2;
}
*/
}
acceptsMaybeNumber(42); // 正常!
acceptsMaybeNumber(); // 正常!
acceptsMaybeNumber(undefined); // 正常!
acceptsMaybeNumber(null); // 正常!
acceptsMaybeNumber("42"); // 错误!
你现在必须编写代码来检查值是否为 undefined
以及值是否为 null
。
在
Flow
中,?number
等同于number | null | void
联合类型,也就是允许数字、null
与undefined
作为值,这就是Maybe Types
。
同时,还有一个可选值语法(?:
)的设计是围绕 undefined
来工作的,在代码中,你只需检查是否为 undefined
即可决定如何处理。
// @flow
const acceptsMaybeNumber = (value?: number) => {
if (value !== undefined) {
return value * 2;
}
}
acceptsMaybeNumber(42); // 正常!
acceptsMaybeNumber(); // 正常!
acceptsMaybeNumber(undefined); // 正常!
acceptsMaybeNumber(null); // 错误!
acceptsMaybeNumber("42"); // 错误!
value?: number
表示仅支持number | undefined
的联合类型,null
并没有被规划在其中。
如果我们想要定义一个对象属性,它仅支持 number
、 null
、undefined
类型,或者这个对象属性可能不存在,这在 Flow
中要如何定义呢?
type A = {p: ?number};
const a: A = {}; // 错误! 属性未定义也不行
为了支持这种情况,我们需要使用 Maybe
来描述可选属性:
type A = {p?: ?number};
const a: A = {}; // 正常! 允许属性未定义
其实这一小段讲来讲去就是说
null
带了更多的麻烦,但规划"空值"的设计还是要偏向使用undefined
,用Flow
与可选属性示例来论述这个结果。😐
告别
正如你所看到的,如果你坚持使用 undefined
,你的代码将会更加干净、直观,因为某些语言功能确实不适用 null
,并且混合使用这两者会导致到处进行额外的检查。
其实,感觉 Javascript
语言是希望我们一直使用 undefined
就好,而 null
是一个历史遗留问题,不希望也不应该被继续扩大滥用下去。
🙌如有翻译偏差之处,欢迎评论区指出批评,拜谢🙌
God bless and happy coding!🍊
转载自:https://juejin.cn/post/7350866242007973903