為什么 typeof null === 'object' 到 2025 還在
如果你是靠搜索速成學(xué)的 JS,大概率早早踩過這個坑:
typeof null // "object"等等——null 表示“沒有值”“空”,怎么會是 object?
簡答:歷史遺留 bug,為了兼容被保留下來。長答:它很迷惑人,尤其坑初學(xué)者;當(dāng)你需要區(qū)分“真對象”和 null 時,別單靠 typeof,用更清晰的檢查方式。
下面用直觀示例 + 實戰(zhàn)模式講清楚原理、影響和寫法。
typeof 會返回什么
typeof 是一元運算符,返回一個字符串表示值的類型標(biāo)簽。例如:
typeof 123 // "number"
typeof "hi" // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof Symbol() // "symbol"
typeof 10n // "bigint"
typeof (() => {}) // "function"
typeof {} // "object"
typeof [] // "object"
typeof null // "object" // ← 這里出戲結(jié)論:你用 typeof x === "object" 判斷“對象”時,會把 null 也算進去。
它為什么會這樣(歷史原因)
早期 JS 引擎用緊湊的內(nèi)部表示:**對象類型的標(biāo)記是 0。 而 null 用 空指針 表示,在那套編碼里標(biāo)記也恰好是 0**。typeof 讀到 0,就返回了 "object"。
等到規(guī)范與瀏覽器實現(xiàn)逐步固定,這個行為已經(jīng)被大量網(wǎng)頁依賴。改掉會大面積破壞線上站點,于是就被寫進歷史了。
一句話:實現(xiàn)細(xì)節(jié)的 Bug,因為兼容性永久保留。
這事為什么重要
如果你只靠 typeof 來判斷對象,會得出錯誤結(jié)論:
function isObject(v) {
return typeof v === "object";
}
isObject(null) // true(錯誤期望)于是你以為能安全訪問屬性,結(jié)果:
const v = null;
// v.foo // TypeError: Cannot read properties of null正確寫法(生產(chǎn)可用)
1) 明確檢查 null
value === null最直、最快、最不歧義。
2) 同時判斷“空值”(null 或 undefined)
value == null // 使用寬松等號:null 或 undefined 時為 true這里有意用 ==,因為它只在 null/undefined 上合并判斷,簡潔且慣用。
3) 判斷“可訪問的對象”(非 null 的 object)
value !== null && typeof value === "object"或封裝:
const isObject = v => v !== null && typeof v === "object";4) 判斷“純對象”(plain object,不含數(shù)組/日期等)
function isPlainObject(v) {
return Object.prototype.toString.call(v) === "[object Object]";
// 或:v !== null && typeof v === "object" && !Array.isArray(v)
}Object.prototype.toString 對區(qū)分 Array/Date/RegExp 等很穩(wěn)。
5) 需要深操作時,用現(xiàn)成工具
深拷貝/合并等,不要手搓類型判斷: 用 structuredClone、lodash.isPlainObject、_.cloneDeep、或現(xiàn)代庫的成熟實現(xiàn)。
示例與坑點
數(shù)組等也是“object”
Array.isArray([]) // true
typeof [] // "object"
Object.prototype.toString.call([]) // "[object Array]"
typeof new Date() // "object"
Object.prototype.toString.call(new Date()) // "[object Date]"所以 typeof 把 數(shù)組、日期、正則、null 都?xì)w到 "object" ——你若要具體類型,typeof 不夠用。
函數(shù)有特殊標(biāo)簽
typeof function(){} // "function"
typeof (() => {}) // "function"函數(shù)本質(zhì)是對象,但 typeof 會給出特殊結(jié)果"function"。
推薦實踐模式
- 意圖明確:要找 null → value === null;要找“空值” → value == null。
- 訪問前守衛(wèi):
if (value !== null && typeof value === "object") {
// 再讀屬性;若關(guān)心具體對象種類,再額外判斷(如 Array.isArray)
}(注意 if (obj && obj.prop) 會把 0/''/false 也當(dāng) falsey,可能不符合意圖)
- 抽成工具函數(shù):在代碼庫里統(tǒng)一用 isObject、isPlainObject 等。
- TypeScript:類型系統(tǒng)能避免許多靜態(tài)錯誤;但運行時仍需校驗外部輸入。
- 校驗外部數(shù)據(jù):API/用戶輸入用 zod / ajv / io-ts 做結(jié)構(gòu)校驗,別指望 typeof。
速查表(Cheat Sheet)
// 是 null 嗎?
value === null
// 是 null 或 undefined 嗎?
value == null
// 是對象且不是 null 嗎?
value !== null && typeof value === "object"
// 是數(shù)組嗎?
Array.isArray(value)
// 是“純對象”(非數(shù)組/日期/正則/…)嗎?
Object.prototype.toString.call(value) === "[object Object]"
// 訪問前守衛(wèi)(對對象):
if (value !== null && typeof value === "object") {
// 這里再讀屬性(必要時再區(qū)分具體類型)
}typeof null === "object" 是 JS 里那種學(xué)一次就忘不掉的小地雷。 它存在不是因為“合理”,而是因為“歷史兼容”。
對你來說最重要的:別被它再坑到。 寫明確的判斷、用小工具函數(shù)、對外部數(shù)據(jù)做校驗。 這樣既能避免陰溝里翻船,也能讓代碼意圖清晰、方便新人上手。





























