作者:Michael Thiessen
译者:前端小智
起源:dev
有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。
许多编程语言都有一个称为null
的非值。它批示一个变量以后不指向一个对象,例如,当它还没有初始化的时候。
相比之下,JavaScript有两个这样的非值:undefined
和null
。在这篇博文中,咱们将钻研它们有什么不同,以及如何最好地应用或防止它们。
1. undefined vs. null
这两种值十分类似,常常调换应用。因而,它们的区别是奥妙的。
1.1 ECMAScript语言标准:undefined vs. null
ECMAScript语言标准对其形容如下。
undefined
是 "当一个变量没有被赋值时应用"null
"示意无意不存在任何对象值"
1.2 两个非值--一个无奈打消的谬误
在JavaScript中领有两个非值当初被认为是一个设计谬误(甚至被JavaScript的创造者Brendan Eich认为)。
那为什么不从 JavaScript 中删除其中的一个值呢?JavaScript 的一个外围准则是绝不毁坏向后兼容。这个准则有很多益处。它最大的毛病是,设计上的谬误无奈被删除。
1.3 undefined和null的历史
在Java中(它启发了JavaScript的许多方面),初始化值取决于变量的动态类型。
- 具备对象类型的变量被初始化为
null
。 - 每个原始类型都有本人的初始化值。例如,
int
变量被初始化为0
。
在JavaScript中,每个变量都能够包容对象值和根本类型值。因而,如果null
意味着 "不是一个对象",那么JavaScript也须要一个初始化值,意味着 "既不是一个对象也不是一个原始值"。这个初始化值就是 undefined
。
undefined 的呈现
如果一个变量myVar
还没有被初始化,它的值是undefined
。
let myVar;assert.equal(myVar, undefined);
如果一个属性.unknownProp
失落,拜访该属性会产生undefined
的值。
const obj = {};assert.equal(obj.unknownProp, undefined);
如果一个函数有一个没有参数的返回语句,该函数隐含地返回undefined
。
function myFunc() { return;}assert.equal(myFunc(), undefined);
如果一个参数x
被省略了,语言将该参数初始化为undefined
。
function myFunc() { return;}assert.equal(myFunc(), undefined);myFunc();
通过obj?.someProp
的可选链路,如果obj未定义或为空,则返回未定义。
> undefined?.somePropundefined> null?.somePropundefined
3.null 呈现状况
一个对象的原型要么是一个对象,要么在原型链的末端是null
。Object.prototype
没有原型。
> Object.getPrototypeOf(Object.prototype)null
如果咱们将一个正则表达式(如/a/
)与一个字符串(如'x'
)相匹配,咱们要么失去一个带有匹配数据的对象(如果匹配胜利),要么失去空(如果匹配失败)。
> /a/.exec('x')null
JSON数据格式不反对undefined
,只反对null
。
> JSON.stringify({a: undefined, b: null})'{"b":null}'
4.特地解决 undefined 或 null 的运算符
4.1 undefined 和参数默认值
在以下状况下,将应用参数默认值。
- 短少一个参数。
- 一个参数的值是
undefined
。
例如:
function myFunc(arg='abc') { return arg;}assert.equal(myFunc('hello'), 'hello');assert.equal(myFunc(), 'abc');assert.equal(myFunc(undefined), 'abc');
undefined
也触发了参数的默认值,这阐明它是一个元值。
上面的例子阐明了这一点的用途。
function concat(str1='', str2='') { return str1 + str2;}function twice(str) { // (A) return concat(str, str);}
在A
行,咱们没有为str
指定一个参数默认值。当这个参数缺失时,咱们把这个状态转发给concat()
,让它抉择一个默认值。
4.2 undefined 的和解构的默认值
解构中的缺省值与参数缺省值的工作原理相似--如果一个变量在数据中没有匹配,或者它与undefined
的变量匹配,就会应用它们。
const [a='a'] = [];assert.equal(a, 'a');const [b='b'] = [undefined];assert.equal(b, 'b');const {prop: c='c'} = {};assert.equal(c, 'c');const {prop: d='d'} = {prop: undefined};assert.equal(d, 'd');
4.3 undefined 和 null 以及可选链
当通过 value?.prop
进行可选的链接时
- 如果
value
是undefined
或null
,则返回undefined
。也就是说,只有value.prop
会抛出一个异样,就会产生这种状况。 - 否则,返回
value.prop
。
function getProp(value) { // optional static property access return value?.prop;}assert.equal( getProp({prop: 123}), 123);assert.equal( getProp(undefined), undefined);assert.equal( getProp(null), undefined);
上面两个操作的成果相似。
obj?.[«expr»] // optional dynamic property accessfunc?.(«arg0», «arg1») // optional function or method call
4.4 undefined 和 null 和 nullish 聚结
nullish 合并算子??
如果一个值undefined
或为null
,则容许咱们应用默认值:
> undefined ?? 'default value''default value'> null ?? 'default value''default value'> 0 ?? 'default value'0> 123 ?? 'default value'123> '' ?? 'default value'''> 'abc' ?? 'default value''abc'
null合并赋值操作符??=
:
function setName(obj) { obj.name ??= '(Unnamed)'; return obj;}assert.deepEqual( setName({}), {name: '(Unnamed)'});assert.deepEqual( setName({name: undefined}), {name: '(Unnamed)'});assert.deepEqual( setName({name: null}), {name: '(Unnamed)'});assert.deepEqual( setName({name: 'Jane'}), {name: 'Jane'});
5. 解决 undefined 和 null
上面分享一下咱们本人的代码中解决undefined
和null
的最常见办法。
5.1 undefined 或 null 都不能作为理论值应用
举例,咱们可能心愿一个属性file.title
永远存在,并且永远是一个字符串。有两种常见的办法来实现这一点。
这里,只查看undefined
和null
,而不查看一个值是否是字符串。你必须本人决定是否要把它作为一个额定的安全措施来施行。
5.1 undefined 和 null 都是禁止的
如下所示
function createFile(title) { if (title === undefined || title === null) { throw new Error('`title` must not be nullish'); } // ···}
为什么抉择这种办法?
咱们想把undefined
和null
当作一样的货色,因为JavaScript代码常常这样做--比如说。
// 检测一个属性是否存在if (!obj.requiredProp) { obj.requiredProp = 123;}const myValue = myParameter ?? 'some default';
如果咱们的代码中呈现了问题,呈现了undefined
或null
,咱们心愿它尽可能快地失败。
5.1.2 undefined 和 null 都会触发默认值
如下所示
function createFile(title) { title ??= '(Untitled)'; // ···}
咱们不能在这里应用参数缺省值,因为它只由undefined
触发。相同,咱们依附nullish 合并赋值运算符??=
。
为什么抉择这种办法?
- 咱们想把
undefined
和null
同等对待(见上一节)。 - 咱们心愿咱们的代码可能持重地、无声地解决
undefined
和null
。
5.2 undefined 或 null 都是“敞开”值
例如,咱们可能心愿一个属性file.title
是一个字符串或 "敞开"(文件没有题目)。有几种办法能够实现这一点。
5.2.1 null是 "敞开的 "值
function createFile(title) { if (title === undefined) { throw new Error('`title` must not be undefined'); } return {title};}
另外,undefined
能够触发一个默认值。
function createFile(title = '(Untitled)') { return {title};}
为什么抉择这种办法?
- 咱们须要一个示意“敞开”的非值。
- 咱们不心愿咱们的非值触发参数默认值和解构默认值。
- 咱们想把非值字符串化为JSON(这是咱们不能用
undefined
做的)。
5.2.2 undefined 是"敞开的 "值。
function createFile(title) { if (title === null) { throw new Error('`title` must not be null'); } return {title};}
为什么抉择这种办法?
- 咱们须要一个示意“敞开”的非值。
- 咱们的确想让咱们的非值触发参数默认值和解构默认值。
5.2.3 为什么不同时应用undefined和null作为 "敞开 "的值?
当接管一个值时,把undefined
和null
都当作 "不是一个值 "是有意义的。然而,当咱们创立值时,咱们心愿不含糊,以便解决这些值时放弃简略。
这指向了一种不同的办法。如果咱们须要一个 "敞开"的值,但又不想应用undefined
或null
作为这样的值,怎么办?请往下看。
5.3 解决“敞开”的其余形式
5.3.1 非凡值
咱们能够创立一个非凡的值,每当属性.title
被敞开时,咱们就应用这个值。
const UNTITLED = Symbol('UNTITLED');const file = { title: UNTITLED,};
5.3.2 空对象模式
空对象模式来自于面向对象的编程。
- 一个独特的超类的所有子类都有雷同的接口。
- 每个子类都实现了一个实例运行的不同模式。
- 其中一个模式是 "null"。
在上面的例子中,UntitledFile
实现了 "null" 模式。
// 形象的超类class File { constructor(content) { if (new.target === File) { throw new Error('Can’t instantiate this class'); } this.content = content; }}class TitledFile extends File { constructor(content, title) { super(content); this.title = title; } getTitle() { return this.title; }}class UntitledFile extends File { constructor(content) { super(content); } getTitle() { return '(Untitled)'; }}const files = [ new TitledFile('Dear diary!', 'My Diary'), new UntitledFile('Reminder: pick a title!'),];assert.deepEqual( files.map(f => f.getTitle()), [ 'My Diary', '(Untitled)', ]);
咱们也能够只对题目应用空对象模式(而不是对整个文件对象)。
5.3.3 Maybe 类型
Maybe 类型是一种函数编程技术。
function getTitle(file) { switch (file.title.kind) { case 'just': return file.title.value; case 'nothing': return '(Untitled)'; default: throw new Error(); }}const files = [ { title: {kind: 'just', value: 'My Diary'}, content: 'Dear diary!', }, { title: {kind: 'nothing'}, content: 'Reminder: pick a title!', },];assert.deepEqual( files.map(f => getTitle(f)), [ 'My Diary', '(Untitled)', ]);
咱们能够通过Arrays对 "just "和 "nothing "进行编码。咱们的办法的益处是,它失去了TypeScript的良好反对(通过判断性联结)。
6.我的办法
我不喜爱用undefined
作为 "敞开 "的值,有三个起因。
undefined
常常意外地呈现在JavaScript中。ndefined
会触发参数和结构化的默认值(出于同样的起因,有些人喜爱undefined
)。
因而,如果我须要一个非凡的值,我会应用以下两种办法中的一种。
- 我应用
null
作为一个 "敞开 "的值。(作为一个旁观者,这种办法在TypeScript中失去了比拟好的反对)。) - 我通过上述的技术之一来防止
undefined
和null
。这样做的益处是比拟洁净,害处是波及更多的工作。
~ 完,我是小智,刷碗去了,当初唯有刷碗能力体验到生存中的一点乐趣,逃 ~
代码部署后可能存在的BUG没法实时晓得,预先为了解决这些BUG,花了大量的工夫进行log 调试,这边顺便给大家举荐一个好用的BUG监控工具 Fundebug。
原文:https://2ality.com/2021/01/un...
交换
有幻想,有干货,微信搜寻 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。
本文 GitHub https://github.com/qq44924588... 已收录,有一线大厂面试残缺考点、材料以及我的系列文章。