Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >null 和 undefined 的区别!

null 和 undefined 的区别!

作者头像
前端小智@大迁世界
发布于 2022-03-22 06:19:50
发布于 2022-03-22 06:19:50
1.2K00
代码可运行
举报
文章被收录于专栏:终身学习者终身学习者
运行总次数:0
代码可运行

作者:Michael Thiessen 译者:前端小智 来源:dev

许多编程语言都有一个称为null的非值。它指示一个变量当前不指向一个对象,例如,当它还没有初始化的时候。

相比之下,JavaScript有两个这样的非值:undefinednull。在这篇博文中,我们将研究它们有什么不同,以及如何最好地使用或避免它们。

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let myVar;
assert.equal(myVar, undefined);

如果一个属性.unknownProp丢失,访问该属性会产生undefined的值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const obj = {};
assert.equal(obj.unknownProp, undefined);

如果一个函数有一个没有参数的返回语句,该函数隐含地返回undefined

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function myFunc() {
  return;
}
assert.equal(myFunc(), undefined);

如果一个参数x被省略了,语言将该参数初始化为undefined

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function myFunc() {
  return;
}
assert.equal(myFunc(), undefined);
myFunc();

通过obj?.someProp的可选链路,如果obj未定义或为空,则返回未定义。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
> undefined?.someProp
undefined
> null?.someProp
undefined

3.null 出现情况

一个对象的原型要么是一个对象,要么在原型链的末端是nullObject.prototype没有原型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
> Object.getPrototypeOf(Object.prototype)
null

如果我们将一个正则表达式(如/a/)与一个字符串(如'x')相匹配,我们要么得到一个带有匹配数据的对象(如果匹配成功),要么得到空(如果匹配失败)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
> /a/.exec('x')
null

JSON数据格式不支持undefined,只支持null

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
> JSON.stringify({a: undefined, b: null})
'{"b":null}'

4.特别处理 undefined 或 null 的运算符

4.1 undefined 和参数默认值

在以下情况下,将使用参数默认值。

  • 缺少一个参数。
  • 一个参数的值是undefined

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function myFunc(arg='abc') {
  return arg;
}
assert.equal(myFunc('hello'), 'hello');
assert.equal(myFunc(), 'abc');
assert.equal(myFunc(undefined), 'abc');

undefined也触发了参数的默认值,这说明它是一个元值。

下面的例子说明了这一点的用处。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function concat(str1='', str2='') {
  return str1 + str2;
}
function twice(str) { // (A)
  return concat(str, str);
}

A行,我们没有为str指定一个参数默认值。当这个参数缺失时,我们把这个状态转发给concat(),让它选择一个默认值。

4.2 undefined 的和解构的默认值

解构中的缺省值与参数缺省值的工作原理类似--如果一个变量在数据中没有匹配,或者它与undefined的变量匹配,就会使用它们。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 进行可选的链接时

  • 如果valueundefinednull,则返回 undefined。也就是说,只要value.prop会抛出一个异常,就会发生这种情况。
  • 否则,返回value.prop
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);

下面两个操作的效果类似。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
obj?.[«expr»] // optional dynamic property access
func?.(«arg0», «arg1») // optional function or method call

4.4 undefined 和 null 和 nullish 聚结

nullish 合并算子??如果一个值undefined或为null,则允许我们使用默认值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
> 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合并赋值操作符??=:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

下面分享一下我们自己的代码中处理undefinednull的最常见方法。

5.1 undefined 或 null 都不能作为实际值使用

举例,我们可能希望一个属性file.title永远存在,并且永远是一个字符串。有两种常见的方法来实现这一点。

这里,只检查undefinednull,而不检查一个值是否是字符串。你必须自己决定是否要把它作为一个额外的安全措施来实施。

5.1 undefined 和 null 都是禁止的

如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createFile(title) {
  if (title === undefined || title === null) {
    throw new Error('`title` must not be nullish');
  }
  // ···
}

为什么选择这种方法?

我们想把undefinednull当作一样的东西,因为JavaScript代码经常这样做--比如说。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 检测一个属性是否存在
if (!obj.requiredProp) {
  obj.requiredProp = 123;
}

const myValue = myParameter ?? 'some default';

如果我们的代码中出现了问题,出现了undefinednull,我们希望它尽可能快地失败。

5.1.2 undefined 和 null 都会触发默认值

如下所示

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createFile(title) {
  title ??= '(Untitled)';
  // ···
}

我们不能在这里使用参数缺省值,因为它只由undefined 触发。相反,我们依靠nullish 合并赋值运算符??=

为什么选择这种方法?

  • 我们想把undefinednull同等对待(见上一节)。
  • 我们希望我们的代码能够稳健地、无声地处理 undefinednull

5.2 undefined 或 null 都是“关闭”值

例如,我们可能希望一个属性file.title是一个字符串或 "关闭"(文件没有标题)。有几种方法可以实现这一点。

5.2.1 null是 "关闭的 "值
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createFile(title) {
  if (title === undefined) {
    throw new Error('`title` must not be undefined');
  }
  return {title};
}

另外,undefined可以触发一个默认值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createFile(title = '(Untitled)') {
  return {title};
}

为什么选择这种方法?

  • 我们需要一个表示“关闭”的非值。
  • 我们不希望我们的非值触发参数默认值和解构默认值。
  • 我们想把非值字符串化为JSON(这是我们不能用undefined做的)。
5.2.2 undefined 是"关闭的 "值。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function createFile(title) {
  if (title === null) {
    throw new Error('`title` must not be null');
  }
  return {title};
}

为什么选择这种方法?

  • 我们需要一个表示“关闭”的非值。
  • 我们确实想让我们的非值触发参数默认值和解构默认值。
5.2.3 为什么不同时使用undefined和null作为 "关闭 "的值?

当接收一个值时,把undefinednull都当作 "不是一个值 "是有意义的。然而,当我们创建值时,我们希望不含糊,以便处理这些值时保持简单。

这指向了一种不同的方法。如果我们需要一个 "关闭"的值,但又不想使用undefinednull作为这样的值,怎么办?请往下看。

5.3 处理“关闭”的其他方式

5.3.1 特殊值

我们可以创建一个特殊的值,每当属性.title被关闭时,我们就使用这个值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const UNTITLED = Symbol('UNTITLED');
const file = {
  title: UNTITLED,
};
5.3.2 空对象模式

空对象模式来自于面向对象的编程。

  • 一个共同的超类的所有子类都有相同的接口。
  • 每个子类都实现了一个实例运行的不同模式。
  • 其中一个模式是 "null"。

在下面的例子中,UntitledFile实现了 "null" 模式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 抽象的超类
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 类型是一种函数编程技术。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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中得到了比较好的支持)。)
  • 我通过上述的技术之一来避免undefinednull。这样做的好处是比较干净,坏处是涉及更多的工作。

~ 完,我是小智,刷碗去了,现在唯有刷碗才能体验到生活中的一点乐趣

原文:https://2ality.com/2021/01/un...

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
undefined vs null
许多编程语言都有一个空值(non-value)null:表示存在一个变量但是没有指向一个对象。
前端柒八九
2022/08/25
1.1K0
【译】ES提案:公有类字段
原文链接https://2ality.com/2019/07/public-class-fields.html
腾讯IVWEB团队
2020/06/28
8530
比较三种非破坏性处理数组的方法
目的是帮助你在需要处理数组的时候在这些特性之间做出选择。如果你还不知道.reduce()和.flatMap(),这里将向你解释它们。
chuckQu
2023/09/01
1680
比较三种非破坏性处理数组的方法
[译] Cookbook of QUnit
本篇文章是QUnit的简介,可以作为很好的入门教程。文章原址 介绍 自动化测试时软件开发过程中必不可少的一部分,而单元测试则是自动化测试的最为基本的一块,软件的每一个组件, 每一个功能单元都需要经过不停地测试以保证在上线时可以正常的工作。当然,测试也不仅仅只有这些作用,最为人 所知的就是测试驱动设计(test-driven design),测试先于实现进行。先写出一个简单的测试,当然此时运行肯定会 出错,然后我们在开始完成具体的实现,直到测试通过。如果仅仅依靠我们自己实现测试函数,不仅仅难以抓住测试过程 中
欲休
2018/03/15
6740
一文让你彻底掌握 TS 枚举
在 JavaScript 中布尔类型的变量含有有限范围的值,即 true 和 false。而在 TypeScript 中使用枚举,你也可以自定义相似的类型。
阿宝哥
2020/04/03
5K0
30分钟QUnit入门教程
30分钟让你了解Javascript单元测试框架QUnit,并能在程序中使用。 QUnit是什么 QUnit是一个强大,易用的JavaScript单元测试框架,由jQuery团队的成员所开发,并且用在jQuery,jQuery UI,jQuery Mobile等项目。 ---- Hello World 学习QUnit还是从例子开始最好,首先我们需要一个跑单元测试的页面,这里命名为index-test.html: <!DOCTYPE html> <html> <head> <meta charset="u
Bob.Chen
2018/05/02
1.5K0
30分钟QUnit入门教程
【拓展】未来的JavaScript记录与元组
编者按:本文译者李松峰,资深技术图书译者,翻译出版过40余部技术及交互设计专著,现任360奇舞团Web前端开发资深专家,360前端技术委员会委员、W3C AC代表。
pingan8787
2020/08/21
6740
JavaScript中的算法
面试过程通常从最初的电话面试开始,然后是现场面试,检查编程技能和文化契合度。几乎毫无例外,最终的决定因素是还是编码能力。通常上,不仅仅要求能得到正确的答案,更重要的是要有清晰的思维过程。写代码中就像在生活中一样,正确的答案并不总是清晰的,但是好的推理通常就足够了。有效推理的能力预示着学习、适应和进化的潜力。好的工程师一直是在成长的,好的公司总是在创新的。
苏南
2020/12/16
1.6K0
JavaScript中的算法
共享可变状态中出现的问题以及如何避免[每日前端夜话0xDB]
这里有两个独立的部分:函数logElements()和函数main()。后者想要在对数组进行排序的前后都打印其内容。但是它到用了 logElements() ,会导致数组被清空。所以 main() 会在A行输出一个空数组。
疯狂的技术宅
2019/11/03
1.6K0
ES2020
ES2020(即 ES11)2020 年 6 月已经正式发布,在此之前进入 Stage 4 的 10 项提案均已纳入规范,成为 JavaScript 语言的新特性
4O4
2022/04/25
5430
ES2020
ES11 来了,还学得动吗?
ES2020(即 ES11)上周(2020 年 6 月)已经正式发布,在此之前进入 Stage 4 的 10 项提案均已纳入规范,成为 JavaScript 语言的新特性
ayqy贾杰
2020/06/30
5810
ES11 来了,还学得动吗?
JavaScript 的未来:它还少些什么? [每日前端夜话0x28]
近年来,JavaScript 的功能得到了大幅度的增加,本文探讨了其仍然缺失的东西。
疯狂的技术宅
2019/03/27
5420
JavaScript 的未来:它还少些什么? [每日前端夜话0x28]
JavaScript如何判断是否为null或undefined
JavaScript开发人员都有这样的经历——在使用变量之前,必须检查它是否为null或undefined。这导致了很多重复的条件检查,可能会使我们的代码混乱不堪。
泽霖
2023/11/12
8290
TypeScript中的类型断言
本文是关于 TypeScript 中的 type assertions 的,它与其他语言中的类型强制转换有相似之处,并通过 as 运算符执行。
疯狂的技术宅
2020/06/19
3.9K0
7个处理JavaScript值为undefined的技巧
译文 | https://www.zcfy.cc/article/7-tips-to-handle-undefined-in-javascript-dmitri-pavlutin
winty
2020/07/28
6.2K0
为什么我喜欢 JavaScript 可选链[每日前端夜话0xD7]
截至2019年8月,一项新提案可选链(optional chaining)进入了第3阶段,将是一个很好的改进。可选的链接更改了从深层对象结构访问属性的方式。
疯狂的技术宅
2019/10/24
7480
怎样在JavaScript中创建和填充任意长度的数组 [每日前端夜话0x29]
不过这并不是长久之计,比如当我们需要创建大型数组时。这篇博文探讨了在这种情况下应该怎么做。
疯狂的技术宅
2019/03/27
3.5K0
怎样在JavaScript中创建和填充任意长度的数组 [每日前端夜话0x29]
为什么我喜欢JavaScript的Optional Chaining[每日前端夜话0xB0]
JavaScript 的特性极大地改变了你的编码方式。从 ES2015 开始,对我代码影响最多的功能是解构、箭头函数、类和模块系统。
疯狂的技术宅
2019/08/27
1.2K0
为什么我喜欢JavaScript的Optional Chaining[每日前端夜话0xB0]
处理 JS中 undefined 的 7 个技巧
为了保证的可读性,本文采用意译而非直译。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 大约8年前,当原作者开始学习JS时,遇到了一个奇怪的情况,既存在undefined 的值
前端小智@大迁世界
2019/08/02
5.3K0
处理 JS中 undefined 的 7 个技巧
【JS】1847- JavaScript 中几个优雅的运算符使用技巧
ECMAScript 发展进程中,会有很多功能的更新,比如销毁,箭头功能,模块,它们极大的改变 JavaScript 编写方式,可能有些人喜欢,有些人不喜欢,但像每个新功能一样,我们最终会习惯它们。
pingan8787
2023/10/27
2790
【JS】1847- JavaScript 中几个优雅的运算符使用技巧
相关推荐
undefined vs null
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验