写代码的时候,你肯定见过 undefined
。控制台里冷不丁冒出来一个,有时候程序直接就崩了,报错信息里也带着它。这玩意儿到底是个啥?
它不是一个错误。这是第一件你需要知道的事。
undefined
是 JavaScript 里的一个基本类型值。它的意思很简单,就一句话:一个变量声明了,但是还没有给它赋值。
就这么直接。
比如你看这段代码:
javascript
let myVariable;
console.log(myVariable);
你运行它,控制台就会打印出 undefined
。为什么?因为你用 let
告诉程序:“嘿,我要创建一个叫 myVariable
的箱子”,但你没告诉它箱子里要放什么东西。所以 JavaScript 很老实,它就告诉你:“行,箱子在这,但里面是空的,状态是 undefined
。”
这是一种状态,不是错误。它在告诉你信息。
咱们再看几个 undefined
经常出现的场景,这些你肯定都遇到过。
1. 函数没有返回值
你写了一个函数,但忘了写 return
,或者函数逻辑走下来就是没有 return
任何东西。这时候,你如果去接收这个函数的执行结果,拿到的就是 undefined
。
看例子:
“`javascript
function greet(name) {
console.log(“你好, ” + name);
// 这里没有 return 语句
}
let result = greet(“小明”);
console.log(result); // 这里会打印 undefined
“`
greet("小明")
这句代码执行了函数,打印出了 “你好, 小明”。但是函数本身没有返回任何值。所以,变量 result
接收到的就是默认的返回值,也就是 undefined
。
JavaScript 就是这么设计的:所有函数都有返回值。如果你不给,它就默认给你一个 undefined
。
2. 访问对象上不存在的属性
这也是一个高频场景。你有一个对象,想去拿它里面的一个属性值,结果手一抖,属性名写错了,或者那个属性压根就不存在。
“`javascript
let user = {
name: “张三”,
age: 30
};
console.log(user.name); // 打印 “张三”
console.log(user.gender); // 打印 undefined
“`
这里,user
对象里有 name
和 age
,但没有 gender
。所以当你尝试访问 user.gender
时,JavaScript 找不到这个属性,它不会报错,而是告诉你这个属性的值是 undefined
。这很合理,它告诉你“你要找的这个坑位存在,但里面没东西”。
但是,如果你在一个 undefined
的值上继续尝试访问属性,程序就会崩溃。
“`javascript
let user = {
name: “张三”
// 没有 address 属性
};
// console.log(user.address.city); // 这行代码会直接报错
// TypeError: Cannot read properties of undefined (reading ‘city’)
“`
这个错误信息非常直白。它说,我没办法在一个 undefined
的东西上读取 city
属性。因为 user.address
本身就是 undefined
,你让它怎么给你 city
?
3. 函数的参数没有传
你定义了一个需要两个参数的函数,但调用的时候只给了一个。那第二个参数在函数内部就是 undefined
。
“`javascript
function calculate(a, b) {
console.log(“a 的值是:”, a);
console.log(“b 的值是:”, b);
return a + b;
}
calculate(10);
“`
输出会是:
a 的值是: 10
b 的值是: undefined
最后一行 return a + b
的结果会是 NaN
(Not a Number),因为 10 + undefined
在数学上没意义。
所以,undefined
就像一个信号灯,它在提醒你:“你是不是忘了传参数?”或者“你访问的这个东西可能不存在”。
undefined
vs. null
vs. “is not defined”
这三个是新手最容易搞混的。我用最简单的方式给你讲清楚。
undefined
:是系统告诉你的。意思是“声明了,但没赋值”。它是一个无意的、默认的状态。null
:是你告诉系统的。意思是“声明了,我给它赋的值就是‘空’”。它是一个有意的、明确的赋值。你主动把一个变量设为null
,通常表示这个地方现在是空的,但以后可能会有值。比如let currentUser = null;
,等用户登录后,再把它赋值成用户信息对象。ReferenceError: xxx is not defined
:这是一个彻底的错误。它的意思是“你连声明都没声明这个变量”。程序根本不知道你在说啥。
咱们看代码对比:
“`javascript
// 场景一:undefined
let a;
console.log(a); // 输出 undefined
// 场景二:null
let b = null;
console.log(b); // 输出 null
// 场景三:is not defined
console.log(c); // 直接报错: ReferenceError: c is not defined
“`
总结一下:undefined
和 null
的变量都存在,只是值不同。而 is not defined
的变量连存在都不存在。
怎么在代码里处理 undefined
?
知道了它是什么,关键是怎么对付它。你不能放任 undefined
在代码里到处跑,那样程序会变得不可靠。
1. 检查一个变量是不是 undefined
最稳妥的方式是使用 typeof
。
“`javascript
let myVar;
if (typeof myVar === ‘undefined’) {
console.log(“myVar 是 undefined”);
}
“`
为什么用 typeof
?因为它绝对安全。就算 myVar
这个变量根本没声明,typeof myVar
也不会报错,它会返回字符串 'undefined'
。
直接用 myVar === undefined
也可以。在现代浏览器环境下,undefined
这个全局变量是只读的,不能被修改,所以这样比较是安全的。但在一些非常古老的 JavaScript 环境里(比如 IE8 之前的),undefined
可能会被意外地重新赋值,导致判断出错。不过现在基本不用担心这个问题了。
2. 给变量设置默认值
经常遇到的情况是,一个变量可能是 undefined
,如果是,你想给它一个默认值。
以前大家喜欢用 ||
操作符。
javascript
// 老方法
let username = incomingName || "游客";
这行代码的意思是,如果 incomingName
是一个“假值”(falsy value),就把 “游客” 赋给 username
。问题在于,“假值”不仅仅包括 undefined
,还包括 null
、false
、0
、空字符串 ""
。
如果你希望当 incomingName
是空字符串 ""
时,username
也应该是空字符串,那 ||
就不行了。
现在,我们有更好的工具:空值合并操作符(??
)。
javascript
// 新方法
let username = incomingName ?? "游客";
??
只在左边的值是 undefined
或者 null
的时候,才会使用右边的默认值。如果 incomingName
是 0
或者空字符串 ""
,username
就会被正确地赋值为 0
或 ""
。这个操作符更精确,是我现在处理默认值的首选。
3. 安全地访问嵌套对象的属性
前面我们提到了访问 user.address.city
可能会因为 user.address
是 undefined
而报错。以前为了避免这个错误,代码写得很难看:
javascript
// 老方法,层层判断
let city;
if (user && user.address) {
city = user.address.city;
}
现在,用可选链操作符(?.
) 就行了。
javascript
// 新方法
let city = user?.address?.city;
这行代码的意思是:如果 user
存在,就继续访问 address
;如果 address
也存在,就继续访问 city
。链条中任何一环是 null
或 undefined
,整个表达式就会立刻停止,并返回 undefined
,而且完全不报错。代码干净多了,也安全多了。
我刚开始工作的时候,就因为一个 undefined
问题,加班到半夜。当时在做一个电商网站的订单页面,需要显示用户的收货地址。有个 API 会返回订单信息,里面包含 order.user.shippingAddress
。大部分情况都正常,但偶尔有几个新用户,他们没填收货地址,API 返回的 user
对象里就没有 shippingAddress
这个字段。
我的代码是这样写的:
let address = order.user.shippingAddress.street;
结果就是,只要遇到一个没地址的用户,整个页面就白屏了,控制台一个红色的 TypeError
。我查了两个小时,一开始还以为是数据格式问题,最后才定位到是 shippingAddress
成了 undefined
。
后来改成 let address = order.user.shippingAddress?.street ?? "地址未提供";
,问题就解决了。从那以后,我对 undefined
就有了敬畏之心。它不是敌人,它是在给你递信息,告诉你代码里有某种“可能性”没有被处理。
所以,下次你再看到 undefined
,别烦。先想想它为什么会出现:是变量没赋值?是函数没返回?还是对象属性不存在?然后用我们上面说的方法,比如 ??
和 ?.
,去优雅地处理它。这样你的代码才会更健壮,不会那么容易就崩掉。