随便记点考到的东西
null 和 undefined 的区别
null 和 undefined 是 JavaScript 中表示"空值"的两种类型,但它们在语义和使用上有本质区别。
| 对比维度 | undefined |
null |
|---|---|---|
| 类型 | "undefined" |
"object"(JS 历史遗留 Bug) |
| 语义(最主要) | "未定义"/"未赋值" | "空值"/"主动清空" |
| 来源 | JS 引擎自动产生 | 开发者手动赋值 |
| 转为数值 | NaN |
0 |
instanceof 和 typeof 判断数据类型的原理
typeof 和 instanceof 是 JavaScript 中两种常用的类型判断方法,但它们的判断原理完全不同。
一、typeof 的原理
1. 基本原理
typeof 通过检查 JavaScript 内部存储的"类型标记" 来判断数据类型。
在 JavaScript 引擎中,每个值都有一个内部类型标记(type tag):
- 在底层(C++ 实现),每个 JS 值都会存储一个表示类型的标志位
javascript
arduino
// JavaScript 值在引擎内部的简化表示
typedef union {
struct {
uint64_t typeTag; // 类型标记(1 = 对象,2 = 字符串,3 = 数字...)
uint64_t value; // 实际值
};
double numberValue; // 数字直接存储
} JSValue;
2. null 的特殊情况(历史 Bug)
javascript
csharp
typeof null; // "object"(这是 JS 的著名 Bug)
为什么 :在 JS 早期实现中,null 被表示为 空对象指针 ,类型标记被设为 0(代表对象),这个 Bug 一直保留至今。
javascript
javascript
// 准确判断 null 的方法
Object.prototype.toString.call(null); // "[object Null]"
3. typeof 的局限性
javascript
csharp
// ❌ 无法区分对象的具体类型
typeof {}; // "object"
typeof []; // "object"(数组也是对象)
typeof new Date(); // "object"
typeof /regex/; // "object"
// ✅ 只有函数能特殊识别
typeof function(){}; // "function"
二、instanceof 的原理
1. 基本原理
instanceof 通过检查构造函数的 prototype 属性是否出现在对象的原型链上。
javascript
javascript
// 基本语法
object instanceof Constructor
// 判断逻辑(简化版的手写实现)
function instanceOf(obj, Constructor) {
// 1. 获取构造函数的 prototype 对象
const prototype = Constructor.prototype;
// 2. 获取对象的原型(__proto__)
let proto = Object.getPrototypeOf(obj);
// 3. 沿着原型链向上查找
while (proto !== null) {
if (proto === prototype) {
return true; // 找到了,返回 true
}
proto = Object.getPrototypeOf(proto);
}
return false; // 到 null 都没找到,返回 false
}
2. 原型链查找示例
javascript
javascript
function Parent() {}
function Child() {}
Child.prototype = Object.create(Parent.prototype);
const child = new Child();
// 判断过程
child instanceof Child; // true
child instanceof Parent; // true
child instanceof Object; // true
// 原型链:
// child → Child.prototype → Parent.prototype → Object.prototype → null
// ↑ ↑ ↑
// Child.prototype Parent.prototype Object.prototype
3. instanceof 的特性
特性1:只能判断对象
javascript
javascript
// 基本类型返回 false
1 instanceof Number; // false
'string' instanceof String; // false
true instanceof Boolean; // false
// 需要用包装对象才能判断!!!
new Number(1) instanceof Number; // true
特性2:instanceof 可被修改
javascript
javascript
function MyClass() {}
const obj = new MyClass();
obj instanceof MyClass; // true
// 修改原型链后失效
MyClass.prototype = {};
obj instanceof MyClass; // false(原型链断了)
三、两者的对比总结
| 维度 | typeof |
instanceof |
|---|---|---|
| 判断原理 | 检查内部类型标记 | 检查原型链 |
| 适用类型 | 基本类型 + 函数 | 对象类型 |
能否判断 null |
❌ 返回 "object"(Bug) |
❌ 始终 false |
| 能否判断数组 | ❌ 返回 "object" |
✅ arr instanceof Array |
| 能否判断自定义类 | ❌ 返回 "object" |
✅ obj instanceof MyClass |
| 可被修改 | ❌ 不可修改 | ✅ prototype 可被修改 |
四、更准确的类型判断方法
1. Object.prototype.toString.call()
这是最准确的类型判断方法,能区分所有内置类型。
javascript
scss
function getType(value) {
return Object.prototype.toString.call(value);
}
getType(123); // "[object Number]"
getType("hello"); // "[object String]"
getType(true); // "[object Boolean]"
getType(null); // "[object Null]"
getType(undefined); // "[object Undefined]"
getType({}); // "[object Object]"
getType([]); // "[object Array]"
getType(function(){}); // "[object Function]"
getType(new Date()); // "[object Date]"
getType(/regex/); // "[object RegExp]"
getType(Symbol()); // "[object Symbol]"
getType(123n); // "[object BigInt]"
forEach 和 for...in 的区别
forEach 和 for...in 都是用于遍历的语法,但它们的设计目的、适用场景、遍历范围完全不同。
一、核心区别速览
| 维度 | forEach |
for...in |
|---|---|---|
| 适用类型 | 数组(及类数组) | 对象(可枚举属性) |
| 遍历内容 | 数组索引对应的值 | 对象可枚举属性名(包括原型链) |
| 遍历原型链 | ❌ 只遍历自身属性 | ✅ 会遍历原型链上的属性 |
| 返回结果 | undefined |
遍历出的结果是 key(属性名) |
| 能否中断break | ❌ 不能(除非抛异常) | ✅ 可以用 break/return 中断 |
| 使用场景 | 数组遍历 | 对象属性遍历 |
二、详细对比
1. 适用类型不同
javascript
// forEach:专门为数组设计
const arr = [1, 2, 3];
arr.forEach((item, index) => {
console.log(item); // 1, 2, 3
});
// for...in:为对象设计
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
console.log(key); // 'a', 'b', 'c'
console.log(obj[key]); // 1, 2, 3
}
// ❌ forEach 不能直接用于普通对象
// obj.forEach is not a function
2. 原型链遍历问题(重要!)
javascript
// 给数组原型添加属性
Array.prototype.customProp = 'hello';
const arr = [1, 2, 3];
// forEach:只遍历数组自身的元素
arr.forEach(item => {
console.log(item); // 1, 2, 3(不会输出 'hello')
});
// for...in:会遍历原型链上的属性!
for (let key in arr) {
console.log(key); // '0', '1', '2', 'customProp'(多了原型属性)
}
// 解决方案:使用 hasOwnProperty 过滤
for (let key in arr) {
if (arr.hasOwnProperty(key)) {
console.log(key); // 只输出 '0', '1', '2'
}
}
4. 能否中断
javascript
// forEach:不能中途中断
const arr = [1, 2, 3, 4, 5];
arr.forEach(item => {
if (item === 3) {
// ❌ 这里不能 break 或 return 终止循环
// return 只会退出当前回调,不影响下一次迭代
}
console.log(item); // 1, 2, 3, 4, 5(全部输出)
});
// for...in:可以用 break 中断
for (let key in arr) {
if (Number(key) === 2) {
break; // ✅ 退出循环
}
console.log(arr[key]); // 只输出 1, 2
}
三、替代方案对比
1. for...of(ES6 推荐)
for...of 结合了两者的优点:遍历数组值、可中断、不遍历原型链。
javascript
const arr = [1, 2, 3];
// for...of:直接遍历值
for (let value of arr) {
console.log(value); // 1, 2, 3
if (value === 2) break; // ✅ 可以中断
}
// 不会遍历原型链属性
Array.prototype.custom = 'test';
for (let value of arr) {
console.log(value); // 1, 2, 3(不输出 'test')
}