1. JavaScript类型系统概述JavaScript作为一门动态弱类型语言其类型系统与其他静态类型语言有着本质区别。在JS中类型不是变量本身的属性而是变量当前所持有值的属性。这意味着同一个变量可以在不同时刻持有不同类型的值这种灵活性既是JS的优势也是许多问题的根源。重要提示理解JS类型系统的关键在于区分变量没有类型和值有类型这两个概念。变量只是值的容器而值本身才具有明确的类型特征。2. JavaScript的七种原始类型2.1 基本原始类型JavaScript定义了7种原始数据类型Primitive Typesundefined表示未定义的值是变量声明后未赋值时的默认值null表示空值通常用于显式清空变量引用boolean布尔值包含true和false两个值number数字类型采用IEEE 754标准的双精度浮点数格式bigint大整数类型可以表示任意精度的整数string字符串类型表示文本数据symbol符号类型ES6新增用于创建唯一的标识符// 原始类型示例 let a; // undefined let b null; // null let c true; // boolean let d 42; // number let e 10n; // bigint let f hello;// string let g Symbol();// symbol2.2 原始类型的特性原始类型值具有以下重要特性不可变性原始值本身不能被修改按值比较比较的是实际值而非引用存储在栈内存访问效率高但存储空间有限3. 对象类型引用类型3.1 对象类型概述除了原始类型外JavaScript中的所有其他值都是对象Object或对象子类型。常见的对象类型包括Object基础对象类型Array数组Function函数Date日期RegExp正则表达式Map/SetES6新增的集合类型// 对象类型示例 const obj {}; // 普通对象 const arr [1, 2, 3]; // 数组 function fn() {} // 函数 const date new Date(); // 日期对象 const regex /abc/; // 正则表达式 const map new Map(); // Map集合3.2 对象类型的特性对象类型与原始类型有显著差异可变性对象内容可以被修改按引用比较比较的是内存地址而非内容存储在堆内存访问效率较低但存储空间大4. 类型检测方法4.1 typeof运算符typeof是检测值类型的最基本方法但有其局限性typeof undefined; // undefined typeof true; // boolean typeof 42; // number typeof 10n; // bigint typeof hello; // string typeof Symbol(); // symbol typeof {}; // object typeof []; // object (注意数组也被识别为object) typeof null; // object (历史遗留问题) typeof function(){}; // function实际经验typeof null返回object是JavaScript著名的设计缺陷在代码中检测null时应使用严格等于value null4.2 instanceof运算符instanceof用于检测对象是否属于某个构造函数的实例[] instanceof Array; // true {} instanceof Object; // true new Date() instanceof Date; // true function Person() {} const p new Person(); p instanceof Person; // true4.3 Object.prototype.toString方法这是最可靠的类型检测方法可以准确识别所有内置类型Object.prototype.toString.call(undefined); // [object Undefined] Object.prototype.toString.call(null); // [object Null] Object.prototype.toString.call(true); // [object Boolean] Object.prototype.toString.call(42); // [object Number] Object.prototype.toString.call(10n); // [object BigInt] Object.prototype.toString.call(hello); // [object String] Object.prototype.toString.call(Symbol()); // [object Symbol] Object.prototype.toString.call({}); // [object Object] Object.prototype.toString.call([]); // [object Array] Object.prototype.toString.call(function(){}); // [object Function]5. 类型转换机制5.1 显式类型转换JavaScript提供了多种显式类型转换方法// 转换为数字 Number(123); // 123 parseInt(123); // 123 parseFloat(1.23); // 1.23 // 转换为字符串 String(123); // 123 (123).toString(); // 123 // 转换为布尔值 Boolean(1); // true Boolean(0); // false !!hello; // true (双取反技巧)5.2 隐式类型转换JavaScript在特定操作下会自动进行类型转换// 字符串拼接 1 2; // 12 // 数学运算 10 - 5; // 5 10 * 2; // 20 10 / 2; // 5 // 布尔上下文 if (hello) {} // 字符串转换为true if (0) {} // 0转换为false5.3 相等性比较中的类型转换JavaScript有两种相等性比较运算符宽松相等会进行类型转换严格相等不进行类型转换1 1; // true (类型转换后相等) 1 1; // false (类型不同) 0 false; // true 0 false; // false null undefined; // true null undefined; // false最佳实践在大多数情况下应该使用和!来避免意外的类型转换问题。6. 特殊类型行为与陷阱6.1 NaN的特殊性NaNNot a Number是数字类型的特殊值表示无效的数学运算结果typeof NaN; // number NaN NaN; // false (唯一不等于自身的值) isNaN(NaN); // true isNaN(hello); // true (会先尝试转换为数字) Number.isNaN(NaN); // true (ES6新增不会强制转换)6.2 null与undefined的区别虽然null和undefined都表示无但使用场景不同undefined变量已声明但未赋值null开发者主动赋值的空值let a; console.log(a); // undefined let b null; console.log(b); // null6.3 原始类型与包装对象JavaScript为原始类型提供了对应的包装对象使得原始值可以调用方法const str hello; str.length; // 5 (自动转换为String对象) const num 1.234; num.toFixed(2); // 1.23 (自动转换为Number对象)这种自动装箱机制可能导致一些混淆const str hello; str.test world; // 临时创建包装对象并添加属性 console.log(str.test); // undefined (包装对象被丢弃)7. ES6新增类型特性7.1 Symbol类型Symbol是ES6引入的新原始类型用于创建唯一标识符const sym1 Symbol(); const sym2 Symbol(description); const sym3 Symbol(description); sym2 sym3; // false (每个Symbol都是唯一的) // 常用作对象属性的键 const obj { [sym1]: value };7.2 BigInt类型BigInt用于表示任意精度的整数const big 1234567890123456789012345678901234567890n; typeof big; // bigint // 与普通number不能混合运算 1n 2n; // 3n 1n 2; // TypeError7.3 可选链操作符(?.)与空值合并操作符(??)ES2020新增的两个实用操作符// 可选链避免访问深层属性时的报错 const value obj?.prop?.nestedProp; // 空值合并仅在左侧为null/undefined时使用右侧值 const result input ?? defaultValue;8. 类型系统的最佳实践8.1 防御性类型检查编写健壮的JavaScript代码需要进行充分的类型检查function safeDivide(a, b) { if (typeof a ! number || typeof b ! number) { throw new TypeError(Arguments must be numbers); } if (b 0) { throw new Error(Cannot divide by zero); } return a / b; }8.2 合理使用TypeScript对于大型项目考虑使用TypeScript获得静态类型检查interface User { id: number; name: string; email?: string; // 可选属性 } function getUser(id: number): User { // 实现代码 }8.3 性能考虑类型相关的操作可能影响性能避免频繁的类型转换谨慎使用instanceof在跨frame场景可能不可靠对于高性能代码保持变量类型稳定9. 常见类型相关问题排查9.1 数组类型检测由于typeof数组返回object检测数组的可靠方法Array.isArray([]); // true Object.prototype.toString.call([]) [object Array]; // true9.2 函数参数类型验证确保函数参数符合预期类型function sum(a, b) { a Number(a); b Number(b); if (isNaN(a) || isNaN(b)) { throw new TypeError(Arguments must be coercible to numbers); } return a b; }9.3 JSON序列化中的类型丢失JSON.stringify会丢失特定类型信息const data { date: new Date(), regex: /abc/, fn: function() {}, undef: undefined }; const json JSON.stringify(data); // {date:2023-07-20T12:00:00.000Z,regex:{}}10. 类型系统的进阶话题10.1 原型链与类型关系JavaScript使用原型链实现继承关系function Person(name) { this.name name; } const p new Person(Alice); p instanceof Person; // true p instanceof Object; // true Person.prototype instanceof Object; // true10.2 鸭子类型检查JavaScript常使用鸭子类型关注行为而非具体类型function isThenable(obj) { return obj typeof obj.then function; } isThenable(Promise.resolve()); // true10.3 自定义类型检查可以创建自定义的类型检查函数function isPlainObject(obj) { return typeof obj object obj ! null Object.getPrototypeOf(obj) Object.prototype; }
阅读完成 · 觉得有帮助?