今天来讨论另一个容易被忽略的主题,如果要表达「有值」的情况,大家都很熟悉:
const score = 95;
const name = 'Joey';
const arr = ['Jack', 'Allen'];
const person = { name: 'Joey', age: 20 };
const isOpened = true;
但如果遇上「无值」,或者「未知」的情况,很容易会遇到以下这几个:
undefined、null、NaN
让我们今天一个一个来剖析它们吧!
直接翻译叫做「尚未定义」的变数,也就是像这样:
let name;
console.log(name); // undefined
听起来好像只要经过赋值,就回不去 undefined
了吗?其实还是可以的,来源於全域物件的 undefined
:
let name = 'Joey';
console.log(name); // Joey
name = undefined;
console.log(name); // undefined
所以,undefined
不是一种「状态」,它就跟字串、数字、阵列一样,undefined
就是一种「值」,只是它的值叫做 undefined。
同时它的 type 也是很特别的,就叫做 undefined
:
console.log(typeof undefined); // "undefined"
而这个 undefined
会在变数初始化时,如果没有一开始就赋值,那 Javascript 就是直接给它一个 undefined
的值。
因此,原本我们会说「某个变数没有初始值」,但其实,只要你有用 let
或 var
去宣告它,它就一定有初始值:
let name;
// 变数只要经过宣告,就会有个 undefined 的值
console.log(name); // undefined
// 变数没有宣告就使用会产生 Reference Error
console.log(name2); // Uncaught ReferenceError: name2 is not defined
大部分的实际情况是,如果程序都是自己写的,基本上变数是不是 undefined
自己都很清楚。
但现在经常去使用第三方的套件,或者是自己串接他人的後端,往往你也不知道对方到底传了什麽过来,最基本的判断就是先确保不是 undefined
。
尤其如果你写的函式要给很多人呼叫,就更要在函式最开头先做基本的 validation,像这个有瑕疵的版本:
const displayName = (firstName, lastName) => {
return `${firstName} ${lastName}`;
};
displayName('Joey'); // "Joey undefined"
可以改成:
const displayName = (firstName, lastName) => {
const nameAry = [];
if (typeof firstName !== 'undefined') nameAry.push(firstName);
if (typeof lastName !== 'undefined') nameAry.push(lastName);
return nameAry.join(' ');
};
displayName('Joey'); // "Joey"
css 要设定 style 的时候,会遇到「if 某种情况,要有这个 style,else 就维持原样」,如果用 if/else 来写会这样:
const isRedColor = true;
let color;
if (isRedColor) {
color = 'red';
}
// elem 是模拟一个 DOM 元素
elem.style.color = color;
但其实上面这段的第 2 行,还记得吗?其实就等於:
let color = undefined;
所以改用三元运算子会轻松一点,而且 color
可以用 const
来宣告,其实比较符合这个变数的原意(初始化之後就没有要改了)。
const isRedColor = true;
const color = isRedColor ? 'red' : undefined;
// elem 是模拟一个 DOM 元素
elem.style.color = color;
上面提过 undefined
也是一种「值」,所以 Object 里面也可以放 undefined
:
const person = {
name: 'Joey',
height: 173,
weight: 63,
son: undefined
};
但还记得我们在 Day 4 - Object 提到过,多余的 property 很容易在执行 Object.keys
系列的函式时,出现意想不到的状况:
const person = {
name: 'Joey',
height: 173,
weight: 63,
son: undefined
};
Object.entries(person)
.forEach(([key, value]) => `${key}:${value}`);
执行结果
name:'Joey'
height:173
weight:63
son:undefined
null
是一种值,它的意思是「故意地没有值」。好吧我知道大家看不懂我在写什麽,所以如果看原文应该比较好理解:
The value null represents the intentional absence of any object value.
简单说,就是这个变数有宣告而且有值,而它的值是「空值」的概念。
拿来跟前面提到的 undefined
比较一下:
let name;
console.log(name); // undefined
const nullName = null;
console.log(nullName); // null
console.log(typeof name); // "undefined"
console.log(typeof nullName); // "object"
我们可以用比较白话的方式来解释这段 code:
我宣告了一个变数叫做
name
,但这个变数我还没想到要给它什麽值
我宣告了一个变数叫做nullName
,这个变数我决定让它代表空值
所以,即便这两个变数都可以说是「什麽都没有」,但比较细微的差距在於,开发者有没有「意图」要定义这个变数:
undefined
null
另一个有趣的点是,透过 typeof
取得的值:
console.log(typeof name); // "undefined"
console.log(typeof nullName); // "object"
这是一个很神奇的设计,有 undefined
这个类别,但没有 null
这个类别,事实上,就连 MDN 都说这是
bug in ECMAScript
所以现阶段如果要判断是不是 null
,可以单纯就用严格相等:
console.log(nullName === null); // true
DB 要 update data 时,如果这个栏位「没有变动」,通常会放 undefined
或直接就不放,但如果要强调这个栏位叫做「空值」,则应该要放 null
。
比如一个原本又瘦又有车的 Joey,变成又胖又没车的 Joey:
// 假设 DB 目前有这笔资料:
// {
// id: '61226502e1c26332bcb5f9ca',
// name: 'Joey',
// weight: 63,
// car: 'TOYOTA'
// }
const updateObject = {
id: undefined, // 这行可以移除
name: undefined, // 这行可以移除
weight: 73,
car: null
};
updateById('61226502e1c26332bcb5f9ca', updateObject);
// 更新完後可能会是这样
// {
// id: '61226502e1c26332bcb5f9ca',
// name: 'Joey',
// weight: 73,
// car: null
// }
今天介绍的东西真的一个比一个奇葩,这位选手叫做「不是个数字」。
NaN:Not-A-Number
通常是在 Math 函式计算失败(如:Math.sqrt(-1)
)或函式解析数字失败(如:parseInt("blabla")
)後才会回传:
console.log(Math.sqrt(-1)); // NaN
console.log(parseInt("blabla")); // NaN
奇特的是,它虽然「不是个数字」,但如果印出它的 type:
console.log(typeof NaN); // "number"
没错,「不是个数字」的类别是「数字」。
而且如果它不像 null
可以用严格相等来判断出来:
console.log(null === null); // true
console.log(NaN === NaN); // false
console.log(parseInt("blabla") === NaN); // false
只能够使用 Number.isNaN()
或 isNaN()
来判断,我个人比较习惯用前者,比较单纯一点,想知道差异可以看 MDN:
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(parseInt("blabla"))); // true
由於它跟 number
相关,所以我个人最常用到的时机点就是 string to number 的时候,比如网址上的参数:
const url = 'https://example.com?score=8';
// 拆 url 的 query 参数没这麽简单,这边是偷吃步
const score = url.split('=')[1];
const scoreNum = parseInt(score, 10);
console.log(scoreNum * 3); // 24
我们没有办法预期网址上的 score
是不是真的都能够被 parseInt
解析,比方说这个来乱的:
const url = 'https://example.com?score=八';
// 中间都跟上面一样
console.log(scoreNum * 3); // NaN
这时就还是要透过特别的判断式,以确保是一个可以被进行数学运算的 number
:
const url = 'https://example.com?score=八';
// 中间都跟上面一样
if (Number.isNaN(scoreNum)) {
console.log('score 请带入数字');
} else {
console.log(scoreNum * 3);
}
执行结果
score 请带入数字
今天介绍了 undefined
、null
、NaN
三个「非主流」,除了比较细微的差异,也带到了常常会碰到它们的地方。
很多时候看到有些人会想用 「!
」 这个运算子,来一次排除掉这三种空值的概念:
const a = undefined;
const b = null;
const c = NaN;
if (!a && !b && !c) {
console.log('用一个惊叹号似乎就可以判断了?');
}
但会衍生出什麽问题呢?我们留到明天来探讨罗!
周旋在这个世界
你害怕的
是未知
还是一无所有?
undefined MDN
null MDN
NaN MDN
>>: 用React刻自己的投资Dashboard Day18 - 选单列active style功能
我们目前所学到的状态机已知,我们能透过 transition 限制状态转换的路径 对的 state ...
前言 写程序,设定好 IDE,可以增加自己的效率,今天来纪录一下安装 Visual Studio C...
今天文章的内容是Sum(总和)跟Value Count(数量计算) 今天的示范资料 Sum Sum大...
前言 在了解 cookie 的安全性设定之後,接下来的两天要来讲讲跟 session 安全性有关的注...
https://wolkesau.medium.com/资料结构和演算法-c3a453c9c64c ...