游客发表

许多编程语言都有一个称为null的年前非值。它指示一个变量当前不指向一个对象,学习现有新例如,认知当它还没有初始化的位人时候。
相比之下,才说JavaScript有两个这样的年前非值:undefined和null。在这篇博文中,学习现有新我们将研究它们有什么不同,认知以及如何最好地使用或避免它们。位人
这两种值非常相似,才说经常互换使用。年前因此,学习现有新它们的认知区别是微妙的。
ECMAScript语言规范对其描述如下。位人
undefined是才说 "当一个变量没有被赋值时使用" null "表示有意不存在任何对象值"在JavaScript中拥有两个非值现在被认为是一个设计错误(甚至被JavaScript的创造者Brendan Eich认为)。
那为什么不从 JavaScript 中删除其中的一个值呢?JavaScript 的一个核心原则是绝不破坏向后兼容。这个原则有很多好处。它最大的缺点是,设计上的服务器托管错误无法被删除。
在Java中(它启发了JavaScript的许多方面),初始化值取决于变量的静态类型。
具有对象类型的变量被初始化为null。 每个原始类型都有自己的初始化值。例如,int变量被初始化为0。在JavaScript中,每个变量都可以容纳对象值和基本类型值。因此,如果null意味着 "不是一个对象",那么JavaScript也需要一个初始化值,意味着 "既不是一个对象也不是一个原始值"。这个初始化值就是 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?.someProp undefined > null?.someProp undefined一个对象的原型要么是一个对象,要么在原型链的末端是null。Object.prototype没有原型。
> Object.getPrototypeOf(Object.prototype) null如果我们将一个正则表达式(如/a/)与一个字符串(如x)相匹配,我们要么得到一个带有匹配数据的对象(如果匹配成功),要么得到空(如果匹配失败)。
> /a/.exec(x) nullJSON数据格式不支持undefined,只支持null。
> JSON.stringify({a: undefined, b: null}) {"b":null}在以下情况下,将使用参数默认值。
缺少一个参数。 一个参数的值是undefined。例如:
function myFunc(arg=abc) { return arg; } assert.equal(myFunc(hello), hello); assert.equal(myFunc(), abc); assert.equal(myFunc(undefined), abc);undefined也触发了参数的默认值,这说明它是一个元值。
下面的例子说明了这一点的云南idc服务商用处。
function concat(str1=, str2=) { return str1 + str2; } function twice(str) { // (A) return concat(str, str); }在A行,我们没有为str指定一个参数默认值。当这个参数缺失时,我们把这个状态转发给concat(),让它选择一个默认值。
解构中的缺省值与参数缺省值的工作原理类似--如果一个变量在数据中没有匹配,或者它与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);当通过 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 access func?.(«arg0», «arg1») // optional function or method callnullish 合并算子??如果一个值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 abcnull合并赋值操作符??=:
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} );下面分享一下我们自己的代码中处理undefined 和null的最常见方法。
举例,我们可能希望一个属性file.title永远存在,并且永远是一个字符串。有两种常见的方法来实现这一点。
这里,只检查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,我们希望它尽可能快地失败。
如下所示
function createFile(title) { title ??= (Untitled); // ··· }我们不能在这里使用参数缺省值,因为它只由undefined 触发。相反,我们依靠nullish 合并赋值运算符??=。
为什么选择这种方法?
我们想把undefined和null同等对待(见上一节)。 我们希望我们的代码能够稳健地、无声地处理 undefined 和null。例如,我们可能希望一个属性file.title是一个字符串或 "关闭"(文件没有标题)。有几种方法可以实现这一点。
另外,undefined可以触发一个默认值。
function createFile(title = (Untitled)) { return {title}; }为什么选择这种方法?
我们需要一个表示“关闭”的非值。 我们不希望我们的非值触发参数默认值和解构默认值。 我们想把非值字符串化为JSON(这是我们不能用undefined做的)。为什么选择这种方法?
我们需要一个表示“关闭”的非值。 我们确实想让我们的非值触发参数默认值和解构默认值。当接收一个值时,把undefined和null都当作 "不是一个值 "是有意义的。然而,当我们创建值时,我们希望不含糊,以便处理这些值时保持简单。
这指向了一种不同的方法。如果我们需要一个 "关闭"的值,但又不想使用undefined或null作为这样的值,怎么办?请往下看。
我们可以创建一个特殊的值,每当属性.title被关闭时,我们就使用这个值。
const UNTITLED = Symbol(UNTITLED); const file = { title: UNTITLED, };空对象模式来自于面向对象的编程。
一个共同的超类的所有子类都有相同的接口。 每个子类都实现了一个实例运行的不同模式。 其中一个模式是 "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), ]);我们也可以只对标题使用空对象模式(而不是对整个文件对象)。
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的良好支持(通过判别性联合)。
我不喜欢用undefined作为 "关闭 "的值,有三个原因。
undefined经常意外地出现在JavaScript中。 ndefined会触发参数和结构化的默认值(出于同样的原因,有些人喜欢undefined)。因此,如果我需要一个特殊的值,我会使用以下两种方法中的一种。
我使用null作为一个 "关闭 "的值。(作为一个旁观者,这种方法在TypeScript中得到了比较好的支持)。) 我通过上述的技术之一来避免undefined和null。这样做的好处是比较干净,坏处是涉及更多的工作。作者:Michael Thiessen
译者:前端小智
来源:dev原文:https://2ality.com/2021/01/undefined-null-revisited.html

随机阅读
热门排行
友情链接