函数or块级作用域

块级作用域:变量在块外不可访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 1. if 块
if (true) {
let x = 1;
const y = 2;
// x 和 y 只在这个块内可用
}
// console.log(x); // 错误:x 在块外不可访问

// 2. for 循环块
for (let i = 0; i < 3; i++) {
let value = i * 2;
// i 和 value 只在循环内可用
}
// console.log(i); // 错误:i 在循环外不可访问

// 3. 简单的块
{
let x = 1;
const y = 2;
// x 和 y 只在这个块内可用
}
// console.log(x); // 错误

函数作用域:变量在函数外部不可访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function compare() {
// 函数作用域的 var
var x = 1;

if (true) {
var x = 2; // 修改了上面的 x
let y = 3; // 块级作用域的变量
console.log(x); // 2
console.log(y); // 3
}

console.log(x); // 2
// console.log(y); // 错误:y 不可访问
}
// console.log(x); // 错误:x 不可访问

对比下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
// 错误示例
for(var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 3, 3, 3
}, 1000);
}
// 正确示例
for(let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 0, 1, 2
}, 1000);
}

避免闭包陷阱

setTimeout(callback,delay):执行的函数,延迟时间(毫秒)

使用 var

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
console.log('循环开始');

for(var i = 0; i < 3; i++) {
// 每次循环都设置一个定时器
setTimeout(() => {
console.log(i);
}, 1000);
console.log('当前i:', i);
}

console.log('循环结束,此时i:', i);

// 输出顺序:
// "循环开始"
// "当前i: 0"
// "当前i: 1"
// "当前i: 2"
// "循环结束,此时i: 3"
// 1秒后同时输出:
// 3
// 3
// 3

相当于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// var 的情况相当于:
var i; // i 被提升到顶部

// 循环设置三个定时器
i = 0;
setTimeout(() => { console.log(i); }, 1000);

i = 1;
setTimeout(() => { console.log(i); }, 1000);

i = 2;
setTimeout(() => { console.log(i); }, 1000);

i = 3; // 循环结束

// 1秒后,三个定时器同时执行
// 此时 i 已经是 3
// 所以都输出 3

使用 let

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
console.log('循环开始');

for(let i = 0; i < 3; i++) {
// 每次循环都创建新的 i
setTimeout(() => {
console.log(i);
}, 1000);
console.log('当前i:', i);
}

// 输出顺序:
// "循环开始"
// "当前i: 0"
// "当前i: 1"
// "当前i: 2"
// 1秒后同时输出:
// 0
// 1
// 2

相当于

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// let 的情况相当于:
{
let i = 0;
setTimeout(() => { console.log(i); }, 1000);
}

{
let i = 1;
setTimeout(() => { console.log(i); }, 1000);
}

{
let i = 2;
setTimeout(() => { console.log(i); }, 1000);
}

// 每个块都有自己的 i
// 1秒后,定时器分别输出各自的 i

其他解决 var 的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 方法1:使用立即执行函数保存当前的 i
for(var i = 0; i < 3; i++) {
(function(j) {
setTimeout(() => {
console.log(j); // 0, 1, 2
}, 1000);
})(i);
}

// 方法2:将 i 作为参数传给 setTimeout
for(var i = 0; i < 3; i++) {
setTimeout((j) => {
console.log(j); // 0, 1, 2
}, 1000, i);
}

React 状态更新方式对比

1
2
3
4
5
6
7
8
9
10
11
const handleDirectUpdate = () => {
setDirectCount(directCount + 1);
setDirectCount(directCount + 1);
setDirectCount(directCount + 1);
};

const handleFunctionalUpdate = () => {
setFuncCount(prev => prev + 1);
setFuncCount(prev => prev + 1);
setFuncCount(prev => prev + 1);
};

下面是上述代码演示,可以点击按钮来体验两种不同的状态更新方式

JS对象迭代特性

for…in的陷阱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建一个父对象
const parent = {
parentMethod: function() {}
};

// 创建一个继承自parent的对象
const child = Object.create(parent);
child.name = '张三';
child.age = 25;

// 使用for...in遍历
console.log('===for...in遍历===');
for(let key in child) {
console.log(key); // 输出: name, age, parentMethod
}

// 正确的做法:使用hasOwnProperty过滤
console.log('===使用hasOwnProperty===');
for(let key in child) {
if(child.hasOwnProperty(key)) {
console.log(key); // 输出: name, age
}
}

Object.keys()和Object.entries()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const person = {
name: '张三',
age: 25
};

// 定义一个不可枚举的属性
Object.defineProperty(person, 'password', {
value: '123456',
enumerable: false
});

// 只获取可枚举的键
console.log('===Object.keys()===');
console.log(Object.keys(person)); // ['name', 'age']

// 获取键值对数组
console.log('===Object.entries()===');
Object.entries(person).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
// name: 张三
// age: 25
});

Object.getOwnPropertyDescriptor()查看属性特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const obj = {};

// 添加属性并设置特性
Object.defineProperty(obj, 'name', {
value: '张三',
writable: true, // 可写
enumerable: true, // 可枚举
configurable: true // 可配置
});

// 查看属性描述符
console.log('===属性描述符===');
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
// {
// value: '张三',
// writable: true,
// enumerable: true,
// configurable: true
// }

迭代器与生成器的高级用法

自定义可迭代对象

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
// 创建一个自定义的课程列表对象
const courses = {
_list: ['JavaScript', 'Python', 'Java'],

// 实现迭代器
[Symbol.iterator]() {
let index = 0;
const self = this;

return {
next() {
if (index < self._list.length) {
return {
value: self._list[index++],
done: false
};
}
return { done: true };
}
};
}
};

// 现在可以使用for...of遍历了
console.log('===课程列表遍历===');
for(let course of courses) {
console.log(course);
}

生成器函数的实际应用

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
47
48
49
50
51
52
53
// 1. 分页数据生成器
function* createPageGenerator(totalItems, pageSize) {
const totalPages = Math.ceil(totalItems / pageSize);

for(let page = 1; page <= totalPages; page++) {
const start = (page - 1) * pageSize;
const end = Math.min(start + pageSize, totalItems);
yield {
page,
start,
end,
isLastPage: page === totalPages
};
}
}

// 使用分页生成器
const pageGen = createPageGenerator(95, 10);
console.log('===分页示例===');
for(let pageInfo of pageGen) {
console.log(`第${pageInfo.page}页: ${pageInfo.start}-${pageInfo.end}`);
}

// 2. 树形结构遍历生成器
function* traverseTree(node) {
yield node.value;

if(node.children) {
for(let child of node.children) {
yield* traverseTree(child);
}
}
}

// 使用树形遍历
const tree = {
value: 'root',
children: [
{
value: 'A',
children: [
{ value: 'A1' },
{ value: 'A2' }
]
},
{ value: 'B' }
]
};

console.log('===树形遍历===');
for(let value of traverseTree(tree)) {
console.log(value); // root, A, A1, A2, B
}

async迭代器的实战应用

异步数据流处理

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
47
48
49
50
51
52
53
// 模拟异步数据源
async function* asyncDataSource() {
const data = [
{ id: 1, name: '用户1' },
{ id: 2, name: '用户2' },
{ id: 3, name: '用户3' }
];

for(let item of data) {
// 模拟网络延迟
await new Promise(resolve => setTimeout(resolve, 1000));
yield item;
}
}

// 使用异步迭代器处理数据
async function processUsers() {
console.log('===开始处理用户数据===');
for await (let user of asyncDataSource()) {
console.log(`处理用户: ${user.name}`);
// 这里可以添加实际的数据处理逻辑
}
console.log('===用户数据处理完成===');
}

// 使用异步迭代器处理API请求
async function* fetchPaginatedData(url, pageSize = 10) {
let page = 1;
let hasMore = true;

while(hasMore) {
const response = await fetch(
`${url}?page=${page}&pageSize=${pageSize}`
);
const data = await response.json();

yield data.items;

hasMore = data.hasMore;
page++;
}
}

// 使用示例
async function downloadAllData() {
try {
for await (let pageData of fetchPaginatedData('/api/data')) {
console.log('获取到一页数据:', pageData);
}
} catch(error) {
console.error('数据获取失败:', error);
}
}

实用的迭代器工具函数

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
47
48
// 1. 合并多个异步迭代器
async function* mergeAsyncIterators(...iterators) {
const promises = iterators.map(async iterator => {
for await (let item of iterator) {
yield item;
}
});

await Promise.all(promises);
}

// 2. 异步迭代器转换函数
async function* mapAsyncIterator(iterator, mapFn) {
for await (let item of iterator) {
yield mapFn(item);
}
}

// 3. 异步迭代器过滤函数
async function* filterAsyncIterator(iterator, filterFn) {
for await (let item of iterator) {
if (await filterFn(item)) {
yield item;
}
}
}

// 使用示例
async function demo() {
const asyncData = asyncDataSource();

// 转换数据
const transformedData = mapAsyncIterator(
asyncData,
item => ({ ...item, processed: true })
);

// 过滤数据
const filteredData = filterAsyncIterator(
transformedData,
async item => item.id > 1
);

// 处理结果
for await (let item of filteredData) {
console.log('处理后的数据:', item);
}
}

for循环和forEach的性能对比

执行速度对比

1
2
3
4
5
6
7
8
9
10
11
12
13
const largeArray = Array(1000000).fill(1);

console.time('for');
for(let i = 0; i < largeArray.length; i++) {
const item = largeArray[i];
}
console.timeEnd('for'); // for: ~2ms

console.time('forEach');
largeArray.forEach(item => {
const temp = item;
});
console.timeEnd('forEach'); // forEach: ~5ms

中断执行的差异

1
2
3
4
5
6
7
8
9
10
11
// for循环可以中断
for(let i = 0; i < 5; i++) {
if(i === 3) break;
console.log(i); // 0, 1, 2
}

// forEach不能中断
[1,2,3,4,5].forEach(num => {
if(num === 3) return; // 只能跳过当前项
console.log(num); // 1, 2, 4, 5
});

map配合Promise.all的异步处理

并行请求处理

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
async function fetchUserData(users) {
try {
// 并行发起所有请求
const requests = users.map(user =>
fetch(`/api/users/${user.id}`)
);

// 等待所有请求完成
const responses = await Promise.all(requests);

// 解析所有响应
const data = await Promise.all(
responses.map(res => res.json())
);

return data;
} catch (error) {
console.error('数据获取失败:', error);
return [];
}
}

// 使用示例
const users = [
{ id: 1 },
{ id: 2 },
{ id: 3 }
];

fetchUserData(users).then(data => {
console.log('获取的用户数据:', data);
});

数据清洗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const rawData = [
{ id: 1, name: ' John ', age: '20' },
{ id: 2, name: 'Jane ', age: '25' },
{ id: 3, name: ' Bob', age: '30' }
];

// 清洗数据
const cleanData = rawData.map(item => ({
id: item.id,
name: item.name.trim(),
age: parseInt(item.age),
createdAt: new Date()
}));

console.log('清洗后的数据:', cleanData);

filter和map链式调用

数据筛选和转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const products = [
{ id: 1, name: '手机', price: 2999, stock: 0 },
{ id: 2, name: '电脑', price: 5999, stock: 10 },
{ id: 3, name: '耳机', price: 999, stock: 5 },
{ id: 4, name: '平板', price: 3999, stock: 3 }
];

// 链式调用:筛选有库存的商品并格式化价格
const availableProducts = products
.filter(product => product.stock > 0)
.map(product => ({
id: product.id,
name: product.name,
price: `¥${product.price.toFixed(2)}`,
inStock: true
}));

console.log('可购买商品:', availableProducts);

条件过滤和数据转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const messages = [
{ type: 'error', content: '操作失败', code: 500 },
{ type: 'success', content: '保存成功', code: 200 },
{ type: 'warning', content: '即将超时', code: 0 },
{ type: 'error', content: '网络错误', code: 503 }
];

// 筛选错误信息并格式化
const errorLogs = messages
.filter(msg => msg.type === 'error')
.map(error => ({
message: error.content,
time: new Date().toISOString(),
code: error.code
}));

console.log('错误日志:', errorLogs);

reduce方法的高级应用

数组转对象

1
2
3
4
5
6
7
8
9
10
11
12
13
const users = [
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 25 },
{ id: 3, name: '王五', age: 22 }
];

// 转换为以id为键的对象
const userMap = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {});

console.log('用户映射:', userMap);

分组统计

1
2
3
4
5
6
7
8
9
10
11
12
13
const orders = [
{ date: '2024-01-01', amount: 100 },
{ date: '2024-01-01', amount: 200 },
{ date: '2024-01-02', amount: 300 }
];

// 按日期分组并计算总金额
const dailyTotal = orders.reduce((acc, order) => {
acc[order.date] = (acc[order.date] || 0) + order.amount;
return acc;
}, {});

console.log('每日订单总额:', dailyTotal);

every结合Object.values的应用

表单验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const formData = {
username: 'john_doe',
email: 'john@example.com',
age: '25'
};

const validations = {
username: value => value.length >= 3,
email: value => value.includes('@'),
age: value => !isNaN(value) && parseInt(value) > 0
};

const isValid = Object.entries(validations)
.every(([field, validate]) => {
const value = formData[field];
return validate(value);
});

console.log('表单是否有效:', isValid);

some和every的对比使用

权限检查

1
2
3
4
5
6
7
8
9
10
11
12
const userRoles = ['editor', 'viewer'];

// 检查是否有任一所需权限
const hasAnyRequiredRole = userRoles
.some(role => ['admin', 'editor'].includes(role));

// 检查是否具有所有所需权限
const hasAllRequiredRoles = ['viewer', 'editor']
.every(role => userRoles.includes(role));

console.log('有任一所需权限:', hasAnyRequiredRole);
console.log('有所有所需权限:', hasAllRequiredRoles);

includes的组合应用

标签匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const posts = [
{ id: 1, tags: ['javascript', 'tutorial'] },
{ id: 2, tags: ['python', 'data-science'] },
{ id: 3, tags: ['javascript', 'react'] }
];

// 查找包含特定标签的文章
const requiredTags = ['javascript', 'tutorial'];

const matchedPosts = posts.filter(post =>
requiredTags.every(tag => post.tags.includes(tag))
);

console.log('匹配的文章:', matchedPosts);

高级应用场景

组合使用多种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const orders = [
{ id: 1, items: ['apple', 'banana'], total: 50 },
{ id: 2, items: ['orange'], total: 30 },
{ id: 3, items: ['banana', 'grape'], total: 45 }
];

// 复杂数据处理
const result = orders
.filter(order => order.total > 40) // 筛选大额订单
.map(order => ({ // 转换数据结构
id: order.id,
itemCount: order.items.length,
hasSpecialItem: order.items.includes('banana')
}))
.reduce((acc, order) => { // 统计信息
acc.totalOrders++;
acc.specialOrders += order.hasSpecialItem ? 1 : 0;
return acc;
}, { totalOrders: 0, specialOrders: 0 });

console.log('订单统计:', result);