002-JavaScript 类型判断方法详解

JavaScript 类型判断方法详解

1. 判断数据类型的常用方法

方法 说明 示例
typeof 返回基本类型字符串,对引用类型(除函数)返回 "object" typeof "str""string"
instanceof 检测构造函数的原型是否在对象原型链上(用于引用类型) [] instanceof Arraytrue
Object.prototype.toString.call() 最准确的类型判断方法,返回 [object Type] 格式字符串 toString.call(null)[object Null]
Array.isArray() ES5 新增的数组检测方法 Array.isArray([])true
=== 特殊值直接比较(null/undefined value === null

2. instanceof 原理

实现机制
沿着对象的原型链向上查找,检查构造函数的 prototype 属性是否出现在原型链中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function myInstanceof(obj, constructor) {
// 获取对象的原型
let proto = Object.getPrototypeOf(obj);

while (proto) {
// 找到原型匹配
if (proto === constructor.prototype) return true;
// 继续向上查找
proto = Object.getPrototypeOf(proto);
}
return false;
}

// 测试
console.log(myInstanceof([], Array)); // true
console.log(myInstanceof({}, Object)); // true
console.log(myInstanceof([], Object)); // true(Array继承自Object)


**注意事项**:

* 对基本类型无效:`1 instanceof Number``false`
* 跨框架对象检测可能失效(不同执行环境有不同原型)
* 修改 `prototype` 可能导致意外行为

### 3. 判断空对象的 4 种方法

```javascript
const obj = {};

// 方法1:Object.keys(推荐)
Object.keys(obj).length === 0; // true

// 方法2:JSON.stringify
JSON.stringify(obj) === '{}'; // true

// 方法3:for...in 遍历
function isEmpty(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) return false;
}
return true;
}

// 方法4:Object.getOwnPropertyNames
Object.getOwnPropertyNames(obj).length === 0; // true

特殊案例处理

1
2
3
4
5
6
7
8
9
// Symbol 属性需单独检测
const symObj = { [Symbol('key')]: 'value' };
Object.getOwnPropertySymbols(symObj).length === 0; // false

// 不可枚举属性
const nonEnum = Object.create({}, {
hidden: { value: 'secret', enumerable: false }
});
Object.keys(nonEnum).length === 0; // true(需注意)

4. typeof null 的特殊性

1
typeof null; // "object"(历史遗留问题)

原因
JavaScript 早期设计时用二进制前三位标识类型:

  • 000:对象类型
  • 1:整型
  • 010:双精度浮点型
  • 100:字符串
  • 110:布尔型
    null 的二进制全为 0,被错误识别为对象类型

正确检测 null

1
const isNull = (value) => value === null;  // 直接全等比较

5. typeof NaN 的特殊性

1
typeof NaN; // "number"(符合IEEE 754标准)

NaN 的特性

  • 全称:Not-A-Number(非数字)
  • 是特殊的 Number 类型值
  • 由无效数学运算产生:0/0, Math.sqrt(-1)
  • 不等于任何值(包括自身):NaN === NaNfalse

检测 NaN 的方法

1
2
3
4
5
6
7
8
9
10
11
// ES6 推荐方法
Number.isNaN(NaN); // true

// 兼容旧环境方法
function isNaN(value) {
return value !== value; // NaN是唯一不自等的值
}

// 注意全局isNaN的缺陷(会先尝试类型转换)
isNaN("hello"); // true(字符串被转成NaN)
Number.isNaN("hello"); // false(严格检测)

类型判断总结表

表达式 结果 说明
typeof "hello" "string" 基本类型准确检测
typeof null "object" 历史遗留问题
typeof NaN "number" IEEE 754 标准定义
typeof function() {} "function" 函数的特殊处理
[] instanceof Array true 原型链检测
Object.prototype.toString.call({}) "[object Object]" 最可靠的类型判断

003-var-let-const 全面对比

var / let / const 全面对比

核心区别总结

特性 var let const
作用域 函数作用域 块级作用域 块级作用域
变量提升 提升并初始化undefined 提升但不初始化(TDZ) 提升但不初始化(TDZ)
重复声明 允许 禁止 禁止
全局绑定 成为window属性 不成为window属性 不成为window属性
初始值 可不初始化 可不初始化 必须初始化
循环中的表现 每次循环共享同一变量 每次迭代创建新绑定 每次迭代创建新绑定

关键概念详解

1. 块级作用域(Block Scope)

letconst声明的变量只在代码块{}内有效:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
var a = 1;
let b = 2;
const c = 3;
}
console.log(a); // 1(var穿透块作用域)
console.log(b); // ReferenceError
console.log(c); // ReferenceError


**典型应用**:

```javascript
// 解决循环变量泄露
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0,1,2(正确)
}
for (var j = 0; j < 3; j++) {
setTimeout(() => console.log(j)); // 3,3,3(泄露到全局)
}

2. 暂时性死区(TDZ - Temporal Dead Zone)

在声明前访问let/const变量会触发错误(而var返回undefined):

1
2
3
4
5
console.log(a); // undefined(变量提升)
var a = 10;

console.log(b); // ReferenceError(TDZ区域)
let b = 20;

TDZ 本质
从进入作用域到变量声明之间的区域,禁止访问变量

3. const 的特殊行为

1
2
3
4
5
6
const a = {};
a.x = 1; // ✅ 允许(修改对象内部属性)
a = {}; // ❌ TypeError(禁止重新赋值)

const b = Object.freeze({});
b.x = 1; // ❌ 静默失败(严格模式报错)

const 本质

  • 保证变量指向的内存地址不变(对于对象是堆地址,对于基本类型是栈值)
  • 对象属性修改不受限制(除非使用Object.freeze

最佳实践指南

  1. **默认使用 const**
    除非需要重新赋值,否则优先用const(减少意外修改)

    1
    2
    const API_URL = "https://api.example.com";
    const config = { timeout: 5000 };
  2. **需要重新赋值时用 let**
    替代var的所有场景

    1
    2
    let count = 0;
    count = processItems(items);
  3. **避免使用 var**
    除特殊兼容场景外不应使用

  4. const 对象保护技巧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 深度冻结对象
    const deepFreeze = obj => {
    Object.freeze(obj);
    Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') deepFreeze(obj[key]);
    });
    };

    const safeObj = deepFreeze({ nested: { value: 1 } });
    safeObj.nested.value = 2; // ❌ 严格模式报错

常见误区

1
2
3
4
5
6
7
8
9
10
11
12
13
// 误区1:const不能修改对象
const user = { name: "Alice" };
user.name = "Bob"; // ✅ 允许

// 误区2:循环中的const
for (const i = 0; i < 3; i++) { // ❌ i++导致重新赋值报错
console.log(i);
}

// 正确用法
for (const item of [1,2,3]) { // ✅ 每次迭代创建新绑定
console.log(item);
}

浏览器兼容性

  • let/const:ES6+(现代浏览器全支持,IE11部分支持)
  • 旧环境需通过Babel转译为ES5
1