欢迎光临
我们一直在努力

【前端+null+undefined】全面理解 null 与 undefined 的区别,以及如何将属性设为 null

JavaScript null 与 undefined:从引擎内核到实战的完全指南

目录

  • 1. 引言
  • 核心要点速查
  • 2. undefined:引擎赋予的"缺失值"
  • 3. null:程序员显式设置的"空对象引用"
  • 4. null 与 undefined 的全面对比
    • 性能与内存深度解析
  • 5. 如何让一个属性变为 null
  • 6. 实际开发中的典型应用
  • 7. 场景决策流程图
  • 8. 小结
  • 9. 常见面试题解析

1. 引言

在 JavaScript 开发中,null 和 undefined 是最常用也最容易被混淆的两个基本类型。它们都表示“没有值”,但背后的语义、触发场景、类型表现以及内存行为却截然不同。本文将围绕二者的核心区别展开,并结合实际场景演示如何将一个属性显式地变为 null,帮助你彻底消除面试和工作中的模糊地带。

核心要点速查

下表浓缩呈现了 null 与 undefined 在日常开发中最重要的几个差异维度,便于快速回顾与查阅:

维度undefinednull
语义 引擎自动赋予,表示“尚未初始化”或“属性不存在” 开发者显式赋予,表示“有意清空的对象引用”
typeof 'undefined' 'object'(历史遗留 bug)
相等性(==) undefined == null → true null == undefined → true
严格相等(===) 仅等于自身,undefined === null → false 仅等于自身,null === undefined → false
数字转换 Number(undefined) → NaN Number(null) → 0
JSON 序列化 JSON.stringify 会忽略该属性 序列化为 "null",保留该键
布尔值 Boolean(undefined) → false(falsy) Boolean(null) → false(falsy)
典型场景 变量声明未赋值、函数无返回值、访问对象不存在的属性 手动释放引用、清空状态、JSON 保留键位、传递“空”参数

要点记忆:undefined 是引擎说“还没准备好”,null 是开发者说“我明确不要了”。

2. undefined:引擎赋予的“缺失值”

undefined 是 JavaScript 引擎自动赋予的初始状态,它通常出现在以下几种情况:

  • 变量已声明但未赋值;
  • 函数没有显式 return 语句,默认返回 undefined;
  • 访问对象中不存在的属性;
  • 函数定义了形参但调用时未传递对应实参,该形参值为 undefined;
  • 数组越界访问时返回 undefined。

undefined 是全局对象上的一个只读属性(在现代浏览器中不可写),通过 typeof 判断得到 'undefined'。严格相等 undefined === undefined 始终为 true。

let a;
console.log(a); // undefined

function foo(x) {
console.log(x);
}
foo(); // 参数 x 为 undefined

const obj = { name: 'Alice' };
console.log(obj.age); // undefined

在逻辑上,undefined 代表“尚未初始化”或“本应存在但目前缺失”的状态,通常不需要、也不推荐人为地给变量赋值 undefined。让变量回到原始状态应使用默认值或删除属性,而非显式写 = undefined,除非你真的需要覆盖某个已有值。

3. null:程序员显式设置的“空对象引用”

null 则是开发者主动赋予的值,语义是“目前没有对象”或“有意清空的对象引用”。常见场景包括:

  • 手动释放对象引用,帮助垃圾回收;
  • 表示一个预期的对象/属性目前不可用;
  • 作为函数参数传递,表示“不需要对象”;
  • 原型链的终点(Object.prototype.__proto__ === null)。

typeof null 返回 'object',这是一个历史遗留的“bug”:JavaScript 在底层以二进制存储数据类型,对象的前三位标记为 000,而 null 的所有位恰好都是 0,因此被误判。实际上,null 属于类型 Null,在语义上严格独立于 Object。

let user = { name: 'Bob' };
user = null; // 主动释放对象引用

console.log(typeof null); // 'object' ← 历史遗留行为

null == null、null === null 均为 true;null == undefined 为 true(宽松相等下二者互等),但 null === undefined 为 false,因为类型不同。

4. null 与 undefined 的全面对比

维度undefinednull通俗理解检测方法
语义 缺失、未定义、未初始化 空值、空对象引用、人为清空 引擎说“还没准备好” vs 开发者说“我明确不要了” typeof x === 'undefined' 检测 undefined;x === null 检测 null
类型 (typeof) 'undefined' 'object'(历史原因) typeof null 返回 "object" 是历史遗留陷阱 判断 undefined:typeof x === 'undefined';判断 null:x === null(不用 typeof)
谁赋予 JavaScript 引擎自动赋予 程序员显式赋值 出现 undefined 常是代码疏漏,null 则是刻意为之 typeof x === 'undefined';x === null 分别检测
何时使用 检测变量/属性是否存在 清空对象引用、重置状态 用 in 或 !== undefined 判存在,赋 null 表示“清空” 'prop' in obj 或 obj.prop !== undefined;清空后 obj.prop === null
布尔值 Boolean(undefined) → false Boolean(null) → false 都是 falsy,x == null 可同时命中两者 x == null 宽松相等检测 null 和 undefined;严格区分用 ===
数字转换 Number(undefined) → NaN Number(null) → 0 undefined 转 NaN 易引发计算异常,null 转 0 可能隐藏错误 通过 Number(x) 返回值推断,但不作为常规检测手段
JSON 序列化 属性被 JSON.stringify 自动忽略 序列化为 "null" 想让字段在 JSON 中出现但为空用 null,想完全隐藏用 undefined JSON.stringify(obj) 后检查字段缺失或值为 null
内存与 GC 没有特定内存语义 赋 null 后原引用变为游离态,等待 GC 置 null 可帮助回收内存,但不代表立即回收 obj === null 检查引用是否已释放

要点记忆:undefined 是引擎说“你没放东西”,null 是开发者说“我现在不用了,请收回”。

性能与内存深度解析

除了表层的语义和类型差异,null 与 undefined 在 V8 引擎内部表示、垃圾回收(GC)触发时机以及内存占用上也有值得关注的细微差别,这些差别对大规模数据处理和性能敏感型应用尤为重要。

维度undefinednull深层影响
V8 内部表示 特殊 Oddball 单例,引擎可用标记位直接表示,无需解引用堆 也属于 Oddball 单例,但引擎必须存储指针(因为 typeof null === 'object') undefined 在对象属性槽中可能被优化为“未初始化”标记,占用空间极小;null 则需 8 字节指针,大量 null 属性会累积内存开销
GC 触发 赋值为 undefined 同样会断开原引用,对可达性影响相同,但引擎优化不明显 赋值为 null 明确表示“释放引用”,引擎可据此更激进地回收原对象,并能保持隐藏类(Hidden Class)稳定 置 null 比置 undefined 更利于 JIT 内联缓存和隐藏类优化,减少因类型突变导致的去优化(deopt)
内存占用 变量槽可用 hole 标记,不额外占用堆空间;属性值为 undefined 时仍占用属性槽(键名+值槽),但值本身可用标记节省指针 变量槽必须存储指向 null 单例的指针(8 字节);对象属性值为 null 时,属性槽保留,值占用指针 对于大量属性的对象,用 undefined 节省内存有限,若真不需要属性建议用 delete 移除键值对
V8 内部表示对内存的真实影响

V8 通过 标记指针(tagged pointer) 区分小整数(Smi)和堆对象(HeapObject)。null、undefined、true、false 这些唯一值被实现为 Oddball 对象,位于只读堆空间,全局共享单例。

  • 当变量槽(如对象内部属性存储)值为 undefined 时,引擎可直接使用一个特定的标记位(如 0x0 且类型标记为 oddball)来表示,无需额外存储指针;这意味着在对象的隐藏类定型后,大量值为 undefined 的属性不会增加内存寻址开销。
  • 而 null 因为 typeof 契约必须返回 'object',引擎必须存储一个指向 null 单例的完整指针。即使重复出现,每次引用都占 8 字节(64 位系统)。

因此,如果你有一个数据密集的对象,例如包含数千条记录的配置缓存,属性值缺省为 null 会比缺省为 undefined 额外多占用约 8 字节 × 属性数量 的内存。在百万级别的数据处理中,这种差距会很明显。

GC 触发时机与优化差异

虽然从可达性角度看,将变量赋为 undefined 或 null 都会断开对原对象的引用,使原对象变为不可达,从而触发垃圾回收,但 V8 的 隐藏类(Hidden Class) 和 内联缓存(Inline Cache, IC) 机制会区别对待这两种赋值:

  • 隐藏类稳定性:V8 通过对象的属性类型变化来跟踪隐藏类。当属性值从一个具体对象变为 null 时,通常被认为是“空引用”,隐藏类可以不发生改变(若之前已有 null 的反馈);而变为 undefined 可能被当作“类型重置”,促使引擎创建新的隐藏类分支,增加去优化风险。
  • IC 命中率:频繁将属性在对象与 undefined 之间切换,会使类型反馈混乱,导致 IC 未命中,回退到运行时查找;而使用 null 作为“无对象”的哨兵值则更稳定。

因此,对于热点代码(如渲染循环中的状态对象),建议用 null 表示“清空”,避免使用 undefined。

实际编码中的内存优化建议
  • 优先用 null 释放引用 在组件卸载、资源销毁时,使用 obj.ref = null 明确释放大对象引用,既能传达意图,又维持隐藏类稳定。

  • 不用的属性干脆删除 如果某个属性未来不再需要,直接 delete obj.prop 能够彻底移除键和值,减少对象自身内存占用,允许引擎回收隐藏类链。而保留值为 null 的属性仍会占用存储槽。当属性数量庞大时,删除可显著降低内存。

  • 数组清空用 .length = 0 不要逐个将数组元素赋 null 或 undefined,这既繁琐又会产生大量空洞(hole),影响数组内部表示优化。直接设置 arr.length = 0 高效清空。

  • 使用 Map 或弱引用 对于可选的键值对,考虑使用 Map 或 WeakMap 存储,需要时 delete 键,避免对象上挂载大量 null 或不存在的属性。WeakMap 还能在键对象被回收时自动清理。

  • 避免在热路径中频繁切换类型 保持属性类型一致(永远不使用 undefined 作为清空标记),利于 JIT 优化。建议在项目规范中统一使用 null 作为“空值”的代表。

  • 深入理解引擎层面的这些细微差异,能帮助你在高负载的前端应用或 Node.js 后端服务中写出更节省内存、更少 GC 停顿的高性能代码。

    5. 如何让一个属性变为 null

    把属性设置为 null 本质上是显式地将一个对象或变量的值替换为单值 null。下面用不同场景说明具体方式:

    5.1 直接给变量赋值为 null

    最基础的方式,适用于普通变量:

    let data = { id: 1, name: 'task' };
    data = null; // 变量 data 现在为 null,原对象等待 GC 回收

    5.2 将对象的某个属性设为 null

    对于对象中已存在的属性,可以直接重新赋值:

    const config = {
    host: 'localhost',
    apiKey: 'abc123',
    cache: {}
    };

    // 清空某个属性但保留键名
    config.cache = null;

    // 或者新增一个 null 属性
    config.backup = null;

    如果你希望将不存在的属性也初始化为 null(即创建属性并赋 null),上述赋值同样有效。

    5.3 通过解构或三元表达式动态设置

    const response = { user: { name: 'Alice' }, sessionToken: 'xyz' };

    function getSessionToken() {
    // 当未登录时,主动返回 null 表示无对象
    return null;
    }

    const normalizedResponse = {
    response,
    tokenInfo: getSessionToken() // 显式返回 null
    };

    5.4 使用 Object.defineProperty(高级用法)

    当你需要精确控制属性的描述符(如可写、可枚举等)时,可以通过属性定义来设置默认 null:

    const obj = {};
    Object.defineProperty(obj, 'meta', {
    value: null,
    writable: true,
    enumerable: true,
    configurable: true
    });
    console.log(obj.meta); // null

    5.5 在条件重置/清理逻辑中设置 null

    在资源释放、页面销毁、组件卸载等场景中,常见模式是手动将关键对象引用置 null,帮助垃圾回收器更快识别可回收内存:

    class Player {
    constructor() {
    this.videoStream = createStream(); // 假设创建了一个大对象
    }

    destroy() {
    this.videoStream = null; // 释放引用
    console.log('视频流已清空');
    }
    }

    5.6 明确不要做的事:delete 与混淆

    有些开发者可能误用 delete obj.prop 来“清空”属性。delete 会彻底移除属性自身,而不是将其值设为 null。两种行为完全不同:

    const obj = { key: 'data' };

    delete obj.key; // 移除属性,'key' 不再存在于 obj
    console.log(obj.key); // undefined

    obj.key = null; // 保留属性,但值为 null
    console.log(obj.key); // null
    console.log('key' in obj); // true

    如果你希望属性在 JSON 序列化时仍然出现且值为空,请使用 null;若希望属性完全消失,delete 或把属性值设为 undefined 可在序列化时隐藏它(但注意:设为 undefined 不等同于 delete)。

    6. 实际开发中的典型应用

    在前后端数据交互、表格渲染、权限判断等场景中,null 常用于表示“明确为空的对象/状态”,而 undefined 则多用于检测字段是否缺失。例如以下真实代码片段:

    // 表格操作按钮的禁用判断
    disableFunc: row => {
    if (!row['config-status']) {
    return true; // 连 config-status 都没有,禁用按钮
    }
    // 判断同步状态是否无效
    if (['', null, undefined].includes(row['config-status']['sync-status'])) {
    return true;
    }
    // 只有明确同步成功才启用
    return row['config-status']['sync-status'] !== 'SYNC';
    }

    这里同时使用 null 和 undefined 来兜底各种“无效值”情况,其中:

    • '' 空字符串可能由后端传回;
    • null 表示后端明确返回了空对象引用;
    • undefined 表示该字段根本不存在。

    掌握这两者的差异,有助于写出更健壮的逻辑,避免因判断遗漏导致意外行为。

    7. 场景决策流程图

    下面是一张速查流程图,帮助你在常见场景中快速决定应该使用 null 还是 undefined:

    #mermaid-svg-wCiX2pQWq8cqFyz3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wCiX2pQWq8cqFyz3 .error-icon{fill:#552222;}#mermaid-svg-wCiX2pQWq8cqFyz3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wCiX2pQWq8cqFyz3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .marker.cross{stroke:#333333;}#mermaid-svg-wCiX2pQWq8cqFyz3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wCiX2pQWq8cqFyz3 p{margin:0;}#mermaid-svg-wCiX2pQWq8cqFyz3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .cluster-label text{fill:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .cluster-label span{color:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .cluster-label span p{background-color:transparent;}#mermaid-svg-wCiX2pQWq8cqFyz3 .label text,#mermaid-svg-wCiX2pQWq8cqFyz3 span{fill:#333;color:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .node rect,#mermaid-svg-wCiX2pQWq8cqFyz3 .node circle,#mermaid-svg-wCiX2pQWq8cqFyz3 .node ellipse,#mermaid-svg-wCiX2pQWq8cqFyz3 .node polygon,#mermaid-svg-wCiX2pQWq8cqFyz3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .rough-node .label text,#mermaid-svg-wCiX2pQWq8cqFyz3 .node .label text,#mermaid-svg-wCiX2pQWq8cqFyz3 .image-shape .label,#mermaid-svg-wCiX2pQWq8cqFyz3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-wCiX2pQWq8cqFyz3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .rough-node .label,#mermaid-svg-wCiX2pQWq8cqFyz3 .node .label,#mermaid-svg-wCiX2pQWq8cqFyz3 .image-shape .label,#mermaid-svg-wCiX2pQWq8cqFyz3 .icon-shape .label{text-align:center;}#mermaid-svg-wCiX2pQWq8cqFyz3 .node.clickable{cursor:pointer;}#mermaid-svg-wCiX2pQWq8cqFyz3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .arrowheadPath{fill:#333333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wCiX2pQWq8cqFyz3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wCiX2pQWq8cqFyz3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wCiX2pQWq8cqFyz3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wCiX2pQWq8cqFyz3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .cluster text{fill:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 .cluster span{color:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wCiX2pQWq8cqFyz3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wCiX2pQWq8cqFyz3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-wCiX2pQWq8cqFyz3 .icon-shape,#mermaid-svg-wCiX2pQWq8cqFyz3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wCiX2pQWq8cqFyz3 .icon-shape p,#mermaid-svg-wCiX2pQWq8cqFyz3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wCiX2pQWq8cqFyz3 .icon-shape .label rect,#mermaid-svg-wCiX2pQWq8cqFyz3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wCiX2pQWq8cqFyz3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wCiX2pQWq8cqFyz3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wCiX2pQWq8cqFyz3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    是(in / hasOwnProperty)

    是(value == null)

    是(赋值 null)

    字段需存在且为空

    字段需完全消失

    开始:需要表示空值?

    返回具体值(字符串、数字、对象等)

    需要区分属性缺失与属性值为空?

    区分处理:属性不存→ undefined;属性为空→ null

    参数区分场景:未传参→ undefined;明确传空→ null

    需要一次性兜底所有无效值?

    同时命中 null 和 undefined:if (value == null) { … }

    需要主动清空或释放引用?

    赋值为 null:obj = null 或 obj.prop = null// 帮助 GC 回收,维持隐藏类稳定

    JSON 序列化需求?

    使用 null:{ key: null } 序列化后键保留

    使用 delete 或不定义 / 赋 undefined// JSON.stringify 会忽略

    8. 小结

    • undefined 是引擎级“未定义”,表示变量/属性尚未初始化或不存在,不要主动赋值 undefined;
    • null 是开发者级“空引用”,用于主动清空对象、释放内存、表示缺失的预期对象;
    • 想让一个属性变为 null,直接赋值 = null 即可,记住与 delete 的区别;
    • 在相等性判断、JSON 序列化、数字转换等环节,二者行为截然不同,面试和编码中都要重点关注。

    掌握这些细节,你就能在变量声明、接口数据清理、组件卸载等场景中精准使用 null 和 undefined,写出意图更清晰、内存更可控的代码。习的人。

    9. 常见面试题解析

    面试题 1:typeof null 为什么返回 'object'?如何准确判断一个值是 null?

    答案 这是 JavaScript 历史遗留 Bug。早期 JS 使用低位二进制标记类型,对象标记为 000,而 null 恰好全零,导致 typeof null 误判为 'object'。

    准确检测方法:使用严格相等 ===,因为 null 是单值类型:

    const value = null;

    // ✅ 正确:严格相等
    console.log(value === null); // true

    // ❌ 错误:typeof 会得到 'object',无法区分
    console.log(typeof value === 'object'); // true,但 {}、[] 也满足

    如果你确实需要同时判断 null 和 undefined,可用 value == null(宽松相等),它会命中两者,是常见的“无效值”快速检测写法。


    面试题 2:解释 undefined == null 和 undefined === null 的结果,并说明如何同时检测两者

    答案

    • undefined == null 为 true(宽松相等)
    • undefined === null 为 false(严格相等) 原因:宽松相等在比较 null 和 undefined 时,规范规定两者互等(且不与其它值互等);严格相等要求类型相同,而 typeof undefined 为 'undefined',typeof null 为 'object',类型不同,故不等。

    同时检测 null 和 undefined:利用 value == null(宽松相等)可以一次性匹配两者,常用于参数默认值或空值判断:

    function greet(name) {
    if (name == null) {
    console.log('未提供名字或值为空');
    } else {
    console.log(`你好,${name}`);
    }
    }

    greet(); // 未提供名字或值为空
    greet(null); // 未提供名字或值为空
    greet('Alice'); // 你好,Alice

    注意:== null 还会匹配 document.all 等特殊对象,但多数场景下可忽略。


    面试题 3:如何区分对象中某个属性“不存在”与“属性值为 null”?请给出两种检测方法

    答案 若仅用 obj.prop == null 或 obj.prop === undefined,无法区分“属性未定义”和“属性值为 null”(因为前者命中 null,后者命中 undefined,但 null 和 undefined 都可能被误判)。正确方法:

    1️⃣ 使用 in 运算符 'prop' in obj 检查自身及原型链上是否存在该属性,无论值为 null 还是 undefined。

    2️⃣ 使用 Object.hasOwn(obj, 'prop') 或 hasOwnProperty 仅检查对象自有属性,比 in 更严格。

    const obj = { a: null, b: undefined };

    console.log('a' in obj); // true,属性存在
    console.log('b' in obj); // true,属性存在
    console.log('c' in obj); // false,属性不存在

    console.log(Object.hasOwn(obj, 'a')); // true
    console.log(Object.hasOwn(obj, 'c')); // false

    若还想进一步判断“存在且值不是 null 或 undefined”,可结合 in 和 !== null,或直接 obj.prop != null,但需明确语义。


    面试题 4:Number(undefined) 和 Number(null) 的结果分别是什么?在 JSON 序列化时,它们有哪些不同表现?

    答案

    • Number(undefined) → NaN(无法转换为数字)
    • Number(null) → 0(空值转换为零)

    JSON 序列化差异

    • JSON.stringify 遇到 undefined 会忽略该键或转为 null(数组中变成 null)。
    • 遇到 null 会正常序列化为字符串 "null",保留键的存在。

    const data = {
    a: undefined,
    b: null,
    c: [undefined, null, 1]
    };

    console.log(JSON.stringify(data));
    // {"b":null,"c":[null,null,1]}
    // 注意:属性 a 消失;数组中的 undefined 变为 null

    陷阱:将属性设为 undefined 并不能在 JSON 输出中保留占位,若希望后端明确知道“这个字段为空”,应使用 null。此外,NaN 在 JSON 中会被序列化为 null,因此 Number(undefined) 产生的 NaN 在 JSON 中也会变为 null。

    赞(0)
    未经允许不得转载:171主机测评 » 【前端+null+undefined】全面理解 null 与 undefined 的区别,以及如何将属性设为 null
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址