掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢(xún)/運(yùn)營(yíng)咨詢(xún)/技術(shù)建議/互聯(lián)網(wǎng)交流
許多編程語(yǔ)言都有一個(gè)稱(chēng)為null的非值。它指示一個(gè)變量當(dāng)前不指向一個(gè)對(duì)象,例如,當(dāng)它還沒(méi)有初始化的時(shí)候。

德城ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18980820575(備注:SSL證書(shū)合作)期待與您的合作!
相比之下,JavaScript有兩個(gè)這樣的非值:undefined和null。在這篇博文中,我們將研究它們有什么不同,以及如何最好地使用或避免它們。
這兩種值非常相似,經(jīng)?;Q使用。因此,它們的區(qū)別是微妙的。
ECMAScript語(yǔ)言規(guī)范對(duì)其描述如下。
在JavaScript中擁有兩個(gè)非值現(xiàn)在被認(rèn)為是一個(gè)設(shè)計(jì)錯(cuò)誤(甚至被JavaScript的創(chuàng)造者Brendan Eich認(rèn)為)。
那為什么不從 JavaScript 中刪除其中的一個(gè)值呢?JavaScript 的一個(gè)核心原則是絕不破壞向后兼容。這個(gè)原則有很多好處。它最大的缺點(diǎn)是,設(shè)計(jì)上的錯(cuò)誤無(wú)法被刪除。
在Java中(它啟發(fā)了JavaScript的許多方面),初始化值取決于變量的靜態(tài)類(lèi)型。
在JavaScript中,每個(gè)變量都可以容納對(duì)象值和基本類(lèi)型值。因此,如果null意味著 "不是一個(gè)對(duì)象",那么JavaScript也需要一個(gè)初始化值,意味著 "既不是一個(gè)對(duì)象也不是一個(gè)原始值"。這個(gè)初始化值就是 undefined。
如果一個(gè)變量myVar還沒(méi)有被初始化,它的值是undefined。
- let myVar;
- assert.equal(myVar, undefined);
如果一個(gè)屬性.unknownProp丟失,訪問(wèn)該屬性會(huì)產(chǎn)生undefined的值。
- const obj = {};
- assert.equal(obj.unknownProp, undefined);
如果一個(gè)函數(shù)有一個(gè)沒(méi)有參數(shù)的返回語(yǔ)句,該函數(shù)隱含地返回undefined。
- function myFunc() {
- return;
- }
- assert.equal(myFunc(), undefined);
如果一個(gè)參數(shù)x被省略了,語(yǔ)言將該參數(shù)初始化為undefined。
- function myFunc() {
- return;
- }
- assert.equal(myFunc(), undefined);
- myFunc();
通過(guò)obj?.someProp的可選鏈路,如果obj未定義或?yàn)榭?,則返回未定義。
- > undefined?.someProp
- undefined
- > null?.someProp
- undefined
一個(gè)對(duì)象的原型要么是一個(gè)對(duì)象,要么在原型鏈的末端是null。Object.prototype沒(méi)有原型。
- > Object.getPrototypeOf(Object.prototype)
- null
如果我們將一個(gè)正則表達(dá)式(如/a/)與一個(gè)字符串(如'x')相匹配,我們要么得到一個(gè)帶有匹配數(shù)據(jù)的對(duì)象(如果匹配成功),要么得到空(如果匹配失敗)。
- > /a/.exec('x')
- null
JSON數(shù)據(jù)格式不支持undefined,只支持null。
- > JSON.stringify({a: undefined, b: null})
- '{"b":null}'
在以下情況下,將使用參數(shù)默認(rèn)值。
例如:
- function myFunc(arg='abc') {
- return arg;
- }
- assert.equal(myFunc('hello'), 'hello');
- assert.equal(myFunc(), 'abc');
- assert.equal(myFunc(undefined), 'abc');
undefined也觸發(fā)了參數(shù)的默認(rèn)值,這說(shuō)明它是一個(gè)元值。
下面的例子說(shuō)明了這一點(diǎn)的用處。
- function concat(str1='', str2='') {
- return str1 + str2;
- }
- function twice(str) { // (A)
- return concat(str, str);
- }
在A行,我們沒(méi)有為str指定一個(gè)參數(shù)默認(rèn)值。當(dāng)這個(gè)參數(shù)缺失時(shí),我們把這個(gè)狀態(tài)轉(zhuǎn)發(fā)給concat(),讓它選擇一個(gè)默認(rèn)值。
解構(gòu)中的缺省值與參數(shù)缺省值的工作原理類(lèi)似--如果一個(gè)變量在數(shù)據(jù)中沒(méi)有匹配,或者它與undefined的變量匹配,就會(huì)使用它們。
- 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');
當(dāng)通過(guò) value?.prop 進(jìn)行可選的鏈接時(shí)
- 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);
下面兩個(gè)操作的效果類(lèi)似。
- obj?.[?expr?] // optional dynamic property access
- func?.(?arg0?, ?arg1?) // optional function or method call
nullish 合并算子??如果一個(gè)值undefined或?yàn)閚ull,則允許我們使用默認(rèn)值:
- > 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'}
- );
下面分享一下我們自己的代碼中處理undefined 和null的最常見(jiàn)方法。
舉例,我們可能希望一個(gè)屬性file.title永遠(yuǎn)存在,并且永遠(yuǎn)是一個(gè)字符串。有兩種常見(jiàn)的方法來(lái)實(shí)現(xiàn)這一點(diǎn)。
這里,只檢查undefined和null,而不檢查一個(gè)值是否是字符串。你必須自己決定是否要把它作為一個(gè)額外的安全措施來(lái)實(shí)施。
如下所示
- function createFile(title) {
- if (title === undefined || title === null) {
- throw new Error('`title` must not be nullish');
- }
- // ···
- }
為什么選擇這種方法?
我們想把undefined和null當(dāng)作一樣的東西,因?yàn)镴avaScript代碼經(jīng)常這樣做--比如說(shuō)。
- // 檢測(cè)一個(gè)屬性是否存在
- if (!obj.requiredProp) {
- obj.requiredProp = 123;
- }
- const myValue = myParameter ?? 'some default';
如果我們的代碼中出現(xiàn)了問(wèn)題,出現(xiàn)了undefined或null,我們希望它盡可能快地失敗。
如下所示
- function createFile(title) {
- title ??= '(Untitled)';
- // ···
- }
我們不能在這里使用參數(shù)缺省值,因?yàn)樗挥蓇ndefined 觸發(fā)。相反,我們依靠nullish 合并賦值運(yùn)算符??=。
為什么選擇這種方法?
例如,我們可能希望一個(gè)屬性file.title是一個(gè)字符串或 "關(guān)閉"(文件沒(méi)有標(biāo)題)。有幾種方法可以實(shí)現(xiàn)這一點(diǎn)。
- function createFile(title) {
- if (title === undefined) {
- throw new Error('`title` must not be undefined');
- }
- return {title};
- }
另外,undefined可以觸發(fā)一個(gè)默認(rèn)值。
- function createFile(title = '(Untitled)') {
- return {title};
- }
為什么選擇這種方法?
- function createFile(title) {
- if (title === null) {
- throw new Error('`title` must not be null');
- }
- return {title};
- }
為什么選擇這種方法?
當(dāng)接收一個(gè)值時(shí),把undefined和null都當(dāng)作 "不是一個(gè)值 "是有意義的。然而,當(dāng)我們創(chuàng)建值時(shí),我們希望不含糊,以便處理這些值時(shí)保持簡(jiǎn)單。
這指向了一種不同的方法。如果我們需要一個(gè) "關(guān)閉"的值,但又不想使用undefined或null作為這樣的值,怎么辦?請(qǐng)往下看。
我們可以創(chuàng)建一個(gè)特殊的值,每當(dāng)屬性.title被關(guān)閉時(shí),我們就使用這個(gè)值。
- const UNTITLED = Symbol('UNTITLED');
- const file = {
- title: UNTITLED,
- };
空對(duì)象模式來(lái)自于面向?qū)ο蟮木幊獭?/p>
在下面的例子中,UntitledFile實(shí)現(xiàn)了 "null" 模式。
- // 抽象的超類(lèi)
- 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)',
- ]);
我們也可以只對(duì)標(biāo)題使用空對(duì)象模式(而不是對(duì)整個(gè)文件對(duì)象)。
Maybe 類(lèi)型是一種函數(shù)編程技術(shù)。
- 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)',
- ]);
我們可以通過(guò)Arrays對(duì) "just "和 "nothing "進(jìn)行編碼。我們的方法的好處是,它得到了TypeScript的良好支持(通過(guò)判別性聯(lián)合)。
我不喜歡用undefined作為 "關(guān)閉 "的值,有三個(gè)原因。
因此,如果我需要一個(gè)特殊的值,我會(huì)使用以下兩種方法中的一種。
作者:Michael Thiessen
譯者:前端小智
來(lái)源:dev原文:https://2ality.com/2021/01/undefined-null-revisited.html

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢(xún)/運(yùn)營(yíng)咨詢(xún)/技術(shù)建議/互聯(lián)網(wǎng)交流