❝成为高手需要在没有感觉的情况下,蹚过漫长的无聊和低成就感时期, 蹚不过就一直是二流水平。 ❞
undefined
2. null
Object.prototype
不存在原型对象且值为null
false
1. undefined
2. null
3. Boolean: false
4. Numbers: 0
, NaN
5. Bigint: 0n
6. String: ''
undefined
vs null
undefined
和null
??
)的默认值 [es2020]undefined
和 null
没有任何属性undefined
和 null
的历史许多编程语言都有一个空值(non-value)null
:表示存在一个变量但是没有指向一个对象。
但是,在JS中,存在两个空值
1. undefined
2. null
undefined
vs null
一般情况下,这两个值在使用上都可以互换使用。只有在一些细微的方面存在差别。
undefined
意味着:「未初始化」(例如:定义一个变量但是未初始化)或者「不存在」(例如:访问一个在对象中不存在的属性)null
意味着:故意将某个对象置为空 (可以参考tc39对Null的解释)我们可以从使用上对其进行分类
undefined
: 是语言层面上使用的非值(定义一个变量,但未赋值,此时该变量会被JS引擎自动赋为undefined
)null
: 蓄意控制变量的值undefined
和null
下面我们将从语言层面讨论undefined
和null
是如何产生。
undefined
的产生myVar
)但未进行初始化let myVar;
myVar// undefined
x
)function func(x) {
return x;
}
func() // undefined
.unknownProp
)const obj = {};
// 访问属性
obj.unknownProp // undefined
return
语句的函数function func() {}
//调用函数
func() // undefined
null
的产生一个对象的原型属性(__proto__
)要么指向一个对象,要么是指向原型链的末端(null
)。
Object.prototype
不存在原型对象且值为null
Object.getPrototypeOf(Object.prototype) // null
如果将一个正则表达式(如/a/
)与一个字符串(如'x'
)进行匹配,要么得到一个具有匹配数据的对象(如果匹配成功),要么得到null(如果匹配失败)
// 匹配成功
/a/.exec('x') // ["a",index:0,input:"a",groups:undefined]
// 匹配失败
/a/.exec('x') // null
undefined
,支持null
JSON.stringify({a: undefined, b: null})
//格式化后的数据(不支持的数据被过滤了)
//'{b:null}'
??
)的默认值有时候,我们只有在值为非undefined
和非null
的时候使用它,否则使用该值的默认值。我们可以通过Null 判断运算符(??
)来实现该操作。
const valueToUse = receivedValue ?? defaultValue;
下面的语句是相等的。
a ?? b
a !== undefined && a !== null ? a : b
function countMatches(regex, str) {
const matchResult = str.match(regex); // null or Array
return (matchResult ?? []).length;
}
// 匹配成功
countMatches(/a/g, 'ababa') // 3
// 匹配失败
countMatches(/x/g, 'ababa') // 0
在满足匹配条件的时候,matchResult
为数组;而未匹配成功时,matchResult
为null
。
我们也可以利用「链判断运算符」(optional chaining operator)?.
(左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined)对上述代码进行优化处理。
return matchResult?.length ?? 0;
function getTitle(fileDesc) {
return fileDesc.title ?? '(Untitled)';
}
const files = [
{path: 'index.html', title: 'Home'},
{path: 'tmp.html'},
];
files.map(f => getTitle(f)) //['Home', '(Untitled)']
function getTitle(fileDesc) {
const {title = '(Untitled)'} = fileDesc;
return title;
}
undefined || 'default' // 'default'
null || 'default' // 'default'
但是,针对「假值」,或运算也是会返回默认值。
❝假值:通过Boolean(X)强制类型转换后的值为
false
1.undefined
2.null
3. Boolean:false
4. Numbers:0
,NaN
5. Bigint:0n
6. String:''
❞
false || 'default' //'default'
0 || 'default' //'default'
0n || 'default' //'default'
'' || 'default' //'default'
??=
) [es2021]下面的代码是等价的。
a ??= b
a ?? (a = b)
??=
会发生「截断现象」:只有变量a
的值为undefined
或null
才会发生赋值操作。
const books = [
{
isbn: '123',
},
{
title: '柒八九',
isbn: '456',
},
];
// 如果对象不存在.title属性,才会被赋值
for (const book of books) {
book.title ??= '(Untitled)';
}
books
/*[
{
isbn: '123',
title: '(Untitled)', // 发生了赋值操作
},
{
title: 'ECMAScript Language Specification',
isbn: '456',
},
]
*/
undefined
和 null
没有任何属性undefined
和null
是JS中仅有的两个变量:当试图读取它们的属性,会得到一个错误。
我们定义一个函数,读取变量(x
)的foo
属性,并将结果返回。
function getFoo(x) {
return x.foo;
}
如果,我们将getFoo()
应用于各种不同的值,我们发现只有undefined
和null
会报错。
getFoo(undefined)
//TypeError: Cannot read property 'foo' of undefined
getFoo(null)
//TypeError: Cannot read property 'foo' of null
getFoo(true) // undefined
getFoo({}) // undefined
如果,继续深究的话,其实,这涉及到JS类型转换,而undefined
和null
不存在包装函数。前面的文章中,也有对变量类型的转换做了分析。
undefined
和 null
的历史在Java(它启发了JavaScript的许多方面)中,初始化值取决于变量的静态类型。
在JavaScript中,每个变量都可以保存对象值和原始值。
❝每个变量只不过是一个用于保存任意值的命名占位符 ❞
因此,如果null表示不是对象,那么JavaScript还需要一个初始化值,这个初始化值既不是对象,也不是原始值。该初始化值就是undefined
。