Appearance
console.log 是同步还是异步? 如何实现一个 console.log?
console.log 内部实现是 process.stdout,将输入的内容打印到 stdout,异步同步取决于 stdout 连接的数据流的类型(需要写入的位置)以及不同的操作系统。
- 文件:在 Windows 和 POSIX 上是同步的;
- TTY(终端):在 Windows 上是异步的,在 POSIX 上是同步;
- 管道(和 socket):在 Windows 上是同步的,在 POSIX 上是异步的;
造成这种差异的原因是因为一些历史遗留问题,不过这个问题并不会影响正常的输出结果。
localStorage 和 sessionStorage 的区别
localStorage 和 sessionStorage 是浏览器提供的两种前端存储机制,用于在客户端保存数据。它们的主要区别如下:
1. 生命周期
localStorage
- 数据持久化:除非主动删除(如调用
localStorage.removeItem()或localStorage.clear()),否则数据不会过期。 - 场景:适合长期保存的数据,如用户偏好设置(主题、语言)、缓存的内容等。
- 数据持久化:除非主动删除(如调用
sessionStorage
- 会话期间有效:数据仅在当前窗口(或标签页)有效。关闭窗口后,数据自动清除。
- 特殊情况:
- 刷新页面不会清除数据(会话未结束)。
- 打开新窗口或标签页会创建新的
sessionStorage实例。
- 场景:适合临时保存的数据,如表单填写进度、一次性验证码等。
2. 作用域
localStorage
- 同源共享:同一域名下的所有窗口(无论是否同源)共享相同的
localStorage数据。 - 示例:javascript
// 在窗口A中设置 localStorage.setItem('theme', 'dark'); // 在窗口B(同源)中可以读取 console.log(localStorage.getItem('theme')); // 输出: dark
- 同源共享:同一域名下的所有窗口(无论是否同源)共享相同的
sessionStorage
- 窗口隔离:每个窗口(或标签页)的
sessionStorage独立,互不影响。 - 示例:javascript
// 在窗口A中设置 sessionStorage.setItem('formData', 'xxx'); // 在窗口B(即使同源)中无法读取 console.log(sessionStorage.getItem('formData')); // 输出: null
- 窗口隔离:每个窗口(或标签页)的
3. 存储容量
- 两者一致:通常为 5MB(不同浏览器可能略有差异)。
- 注意:超过容量会触发
QuotaExceededError,需提前处理。
4. 使用场景
| 场景 | localStorage | sessionStorage |
|---|---|---|
| 用户偏好设置 | ✅ 主题、字体大小、语言选择等 | ❌ 窗口关闭后需重置 |
| 临时表单数据 | ❌ 长期保存可能导致数据过时 | ✅ 表单未提交时保存进度 |
| 缓存频繁访问的数据 | ✅ 减少API请求(如用户信息、配置项) | ❌ 会话结束后需重新获取 |
| 一次性验证信息 | ❌ 数据可能被长期保留 | ✅ 验证码、临时令牌等 |
| 多窗口数据共享 | ✅ 所有窗口可见 | ❌ 仅当前窗口可见 |
5. 代码示例对比
javascript
// localStorage 示例
localStorage.setItem('username', 'doubao');
console.log(localStorage.getItem('username')); // 输出: doubao
localStorage.removeItem('username');
// sessionStorage 示例
sessionStorage.setItem('token', '123456');
console.log(sessionStorage.getItem('token')); // 输出: 123456(当前窗口有效)总结
选
localStorage:需要长期保存、多窗口共享的数据。选
sessionStorage:临时性、窗口隔离的数据,如会话状态、表单进度。
for...of 循环和 for...in 循环的区别
for...of 循环的使用案例
for...of 用于遍历可迭代对象(数组、字符串、Map、Set、NodeList等),直接获取元素的值,适合“需要元素值,不需要索引”的场景。
1. 遍历数组(获取元素值)
javascript
const fruits = ['apple', 'banana', 'cherry'];
for (const fruit of fruits) {
console.log(fruit); // 输出:apple、banana、cherry
}2. 遍历字符串(获取字符)
javascript
const str = 'hello';
for (const char of str) {
console.log(char); // 输出:h、e、l、l、o
}3. 遍历Map(获取键值对)
javascript
const map = new Map([['name', 'Alice'], ['age', 20]]);
for (const [key, value] of map) {
console.log(`${key}: ${value}`); // 输出:name: Alice、age: 20
}4. 遍历Set(获取唯一值)
javascript
const set = new Set([1, 2, 2, 3]);
for (const num of set) {
console.log(num); // 输出:1、2、3
}5. 遍历DOM集合(如NodeList)
javascript
const lis = document.querySelectorAll('li');
for (const li of lis) {
li.classList.add('active'); // 给每个li添加样式
}for...in的使用案例
for...in 用于遍历对象的可枚举属性(包括继承的 enumerable 属性),获取的是属性名,适合“需要对象属性名”的场景。注意:禁止用for...in遍历数组(会遍历非数字索引的属性,且顺序不确定)。
1. 遍历对象的自有属性
javascript
const person = { name: 'Bob', age: 30, city: 'Beijing' };
for (const key in person) {
// 通常用hasOwnProperty过滤继承属性
if (person.hasOwnProperty(key)) {
console.log(`${key}: ${person[key]}`); // 输出:name: Bob、age: 30、city: Beijing
}
}2. 检查对象是否有某个属性
javascript
function hasProperty(obj, prop) {
for (const key in obj) {
if (key === prop) return true;
}
return false;
}
const obj = { a: 1, b: 2 };
console.log(hasProperty(obj, 'a')); // true3. 遍历对象的继承属性(谨慎使用)
javascript
// 父对象
const parent = { x: 10 };
// 子对象继承父对象
const child = Object.create(parent);
child.y = 20;
// 遍历子对象的所有可枚举属性(包括继承的x)
for (const key in child) {
console.log(key); // 输出:y、x
}四、核心区别总结
| 循环类型 | 适用场景 | 遍历目标 | 性能 | 注意事项 |
|---|---|---|---|---|
for (let i = 0; ...) | 数组、类数组 | 索引 → 元素值 | 最快(无额外开销) | 需要手动维护索引 |
for...of | 可迭代对象(数组、Map等) | 直接获取元素值 | 较快(迭代器开销) | 不能直接获取索引(需配合entries()) |
for...in | 对象(非数组) | 属性名 | 较慢(需检查继承) | 禁止遍历数组,需过滤继承属性 |
一句话总结:
- 遍历数组且需要性能 → 传统for循环;
- 遍历可迭代对象且想要简洁 → for...of;
- 遍历对象属性 → for...in(记得用
hasOwnProperty)。