null 与 undefined 的区别

nullundefined 的区别

本篇全文由DeepSeek生成, 觉得写得不错, 就发出来了

首先要明确的核心概念是:TypeScript 是 JavaScript 的超集 ,因此 nullundefined 的行为和区别首先源于 JavaScript,TypeScript 的类型系统在此基础上为我们提供了更强大的控制和安全性。


1. 含义与产生场景

这是理解两者区别最直观的方式。

undefined (未定义)
  • 含义 :表示一个变量已被声明,但尚未被赋值。它是所有未初始化变量的默认值
  • 常见产生场景
    1. 声明了变量但没有赋值。

      typescript 复制代码
      let a: number;
      console.log(a); // 输出: undefined
    2. 访问对象不存在的属性。

      typescript 复制代码
      const obj = { name: 'Alice' };
      console.log(obj.age); // 输出: undefined
    3. 函数被调用时,未提供的参数。

      typescript 复制代码
      function greet(name: string) { return `Hello ${name}`; }
      greet(); // TypeScript 会报错,但在 JS 运行时 name 参数是 undefined
    4. 函数没有显式返回任何值(即没有 return 语句或 return;),默认返回 undefined

      typescript 复制代码
      function doNothing() {}
      console.log(doNothing()); // 输出: undefined
null (空值)
  • 含义 :表示一个**"空"的 "不存在"的对象的引用。它是一个显式赋值的值**,用来表示"这里应该有值,但现在是空的"。
  • 常见产生场景
    1. 由程序员主动显式地 赋给一个变量,以清空该变量或表示其无值。

      typescript 复制代码
      let user = { name: 'Bob' };
      user = null; // 明确表示 user 不再指向任何对象
    2. 作为函数的参数,表示应该传入一个空值。

    3. 一些 DOM API 方法在找不到元素时会返回 null

      typescript 复制代码
      const element = document.getElementById('non-existent-id');
      console.log(element); // 输出: null

2. TypeScript 类型系统中的区别

TypeScript 有各自独立的类型:

  • undefined 类型只有一个值:undefined
  • null 类型只有一个值:null

默认情况下,它们是所有其他类型的子类型。这意味着在 strictNullChecks 选项关闭 时(不推荐),你可以把 nullundefined 赋值给任意类型的变量(如 string, number)。

typescript 复制代码
// 假设 strictNullChecks: false (旧版TS或宽松配置)
let title: string;
title = 'Hello';   // OK
title = null;      // OK
title = undefined; // OK

let age: number = null; // OK

然而,最佳实践是始终开启 strictNullChecks (在 tsconfig.json 中设置)。开启后:

  • nullundefined 不能再赋值给其他类型。
  • 它们只能赋值给自身类型和 any 类型。
  • 这能有效避免运行时"Cannot read property 'X' of null/undefined"这类常见错误。
typescript 复制代码
// 假设 strictNullChecks: true (推荐配置)
let title: string;
title = 'Hello';   // OK
title = null;      // ❌ 错误: Type 'null' is not assignable to type 'string'.
title = undefined; // ❌ 错误: Type 'undefined' is not assignable to type 'string'.

// 如果一个变量确实可能为 null 或 undefined,必须明确声明!
let maybeName: string | null = null;
maybeName = 'Alice'; // OK
maybeName = null;    // OK
maybeName = undefined; // ❌ 错误: Type 'undefined' is not assignable to type 'string | null'.

3. 相等性检查 (== vs ===)

这是一个非常重要的行为差异。

  • 非严格相等 (==)nullundefined 在非严格相等比较时被认为是相等的。

    typescript 复制代码
    console.log(null == undefined); // true
  • 严格相等 (===)nullundefined 是不同类型的值,严格比较时不相等。

    typescript 复制代码
    console.log(null === undefined); // false

建议 :在 TypeScript 和现代 JavaScript 开发中,始终使用 === 以避免隐式类型转换带来的意外行为。


4. JSON 序列化

在将数据转换为 JSON 字符串时,两者的行为也不同:

  • undefined 的属性会被完全忽略,不会出现在 JSON 字符串中。
  • null 的属性会被保留 ,其值为 null
typescript 复制代码
const data = { name: 'Alice', age: undefined, address: null };
const jsonString = JSON.stringify(data);
console.log(jsonString); // 输出: '{"name":"Alice","address":null}'
// 注意 `age` 属性消失了

总结与对比表格

特性 undefined null
含义 变量已声明但未赋值 表示一个空的对象引用(主动赋值)
类型 undefined null
产生方式 由 JavaScript 引擎自动分配 由程序员显式分配
typeof 运算 "undefined" "object" (这是JS的历史遗留bug)
== 比较 null == undefinedtrue undefined == nulltrue
=== 比较 null === undefinedfalse undefined === nullfalse
JSON 序列化 属性被忽略 属性值变为 null

实践建议

  1. 开启 strictNullChecks:这是避免空值错误的最重要手段。

  2. 使用联合类型 :如果一个值确实可能为空,明确使用 string | nullstring | undefined 甚至 string | null | undefined 来声明其类型。

  3. 优先使用 undefined :在很多场景下(如可选参数、可选属性),TypeScript 默认使用 undefined。保持一致性可以使代码更清晰。你可以将 undefined 视为"系统级"的缺失,而 null 视为"程序级"的空白赋值。

  4. 明确检查 :在使用一个可能为 nullundefined 的值前,一定要进行检查。

    typescript 复制代码
    function doSomething(str: string | null) {
      if (str === null) {
        // 处理 null 的情况
        return;
      }
      // 现在 TypeScript 知道这里的 str 一定是 string 类型
      console.log(str.length);
    }

    也可以使用可选链 (?.)空值合并 (??) 运算符 来简化操作:

    typescript 复制代码
    const length = maybeName?.length ?? 0; // 如果 maybeName 为 null/undefined,则返回 0
相关推荐
小时前端7 小时前
谁说 AI 历史会话必须存后端?IndexedDB方案完美翻盘
前端·agent·indexeddb
wordbaby7 小时前
TanStack Router 基于文件的路由
前端
wordbaby7 小时前
TanStack Router 路由概念
前端
wordbaby7 小时前
TanStack Router 路由匹配
前端
cc蒲公英7 小时前
vue nextTick和setTimeout区别
前端·javascript·vue.js
程序员刘禹锡7 小时前
Html中常用的块标签!!!12.16日
前端·html
我血条子呢7 小时前
【CSS】类似渐变色弯曲border
前端·css
DanyHope7 小时前
LeetCode 两数之和:从 O (n²) 到 O (n),空间换时间的经典实践
前端·javascript·算法·leetcode·职场和发展
hgz07107 小时前
企业级多项目部署与Tomcat运维实战
前端·firefox
用户1887871069847 小时前
基于vant3的搜索选择组件
前端