ECMAScript 2017(ES8)
Object.values() / Object.entries()
与 Object.keys()
配套的方法,分别返回对象的值数组和键值对数组。
javascript
{
const obj = { a: 1, b: 2, c: 3 }
// Object.keys() - 返回键数组
console.log(Object.keys(obj)) // ['a', 'b', 'c']
// Object.values() - 返回值数组
console.log(Object.values(obj)) // [1, 2, 3]
// Object.entries() - 返回键值对数组
console.log(Object.entries(obj)) // [['a', 1], ['b', 2], ['c', 3]]
// 传统遍历方式
Object.keys(obj).forEach((key, index) => {
console.log(key, obj[key])
})
// 使用 Object.values() 遍历值
Object.values(obj).forEach(value => {
console.log(value) // 1, 2, 3
})
// 使用 Object.entries() 遍历键值对
Object.entries(obj).forEach(([key, value]) => {
console.log(`${key} is ${value}`)
})
// a is 1, b is 2, c is 3
// 结合 for...of 使用
for (const value of Object.values(obj)) {
console.log(value)
}
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`)
}
// 实际应用:数据过滤和转换
const grades = {
alice: 96,
bob: 84,
charlie: 92,
diana: 88
}
// 获取所有分数
const scores = Object.values(grades)
console.log(scores) // [96, 84, 92, 88]
// 过滤高分学生
const highScorers = Object.entries(grades)
.filter(([name, score]) => score > 90)
.map(([name, score]) => name)
console.log(highScorers) // ['alice', 'charlie']
// 计算平均分
const average = Object.values(grades).reduce((sum, score) => sum + score, 0) / Object.values(grades).length
console.log(average) // 90
// 对象转换
const cityTemperatures = {
'New York': '78/50',
'London': '64/45',
'Tokyo': '82/55'
}
const temperatureData = Object.entries(cityTemperatures).map(([city, temp]) => {
const [high, low] = temp.split('/').map(Number)
return { city, high, low, average: (high + low) / 2 }
})
console.log(temperatureData)
// [
// { city: 'New York', high: 78, low: 50, average: 64 },
// { city: 'London', high: 64, low: 45, average: 54.5 },
// { city: 'Tokyo', high: 82, low: 55, average: 68.5 }
// ]
}
String.prototype.padStart() / padEnd()
字符串填充方法,在字符串的开头或结尾填充指定字符到目标长度。
javascript
{
// padStart() - 在开头填充
console.log('5'.padStart(3, '0')) // '005'
console.log('react'.padStart(10)) // ' react'
console.log('react'.padStart(10, '_')) // '_____react'
// padEnd() - 在结尾填充
console.log('5'.padEnd(3, '0')) // '500'
console.log('react'.padEnd(10)) // 'react '
console.log('react'.padEnd(10, '_')) // 'react_____'
// 实际应用:数字格式化
for (let i = 1; i <= 12; i++) {
const month = i.toString().padStart(2, '0')
console.log(`2023-${month}-01`) // 2023-01-01, 2023-02-01, ...
}
// 财务报表对齐
const amounts = ['0.00', '1,250.50', '15,000.00', '150.75']
amounts.forEach(amount => {
console.log(amount.padStart(12))
})
// 输出:
// 0.00
// 1,250.50
// 15,000.00
// 150.75
// 创建简单的表格
const data = [
['Name', 'Age', 'City'],
['Alice', '25', 'New York'],
['Bob', '30', 'London'],
['Charlie', '35', 'Tokyo']
]
data.forEach(row => {
const formatted = row.map((cell, index) => {
const width = index === 0 ? 10 : index === 1 ? 5 : 12
return cell.padEnd(width)
}).join('|')
console.log(formatted)
})
// 进度条显示
function showProgress(current, total, width = 20) {
const percentage = Math.floor((current / total) * 100)
const filled = Math.floor((current / total) * width)
const bar = '█'.repeat(filled).padEnd(width, '░')
return `[${bar}] ${percentage.toString().padStart(3)}%`
}
console.log(showProgress(7, 10)) // [██████████████░░░░░░] 70%
console.log(showProgress(3, 10)) // [██████░░░░░░░░░░░░░░] 30%
// 日志格式化
function formatLog(level, message) {
const timestamp = new Date().toISOString()
const formattedLevel = level.padEnd(5)
return `${timestamp} [${formattedLevel}] ${message}`
}
console.log(formatLog('INFO', 'Application started'))
console.log(formatLog('ERROR', 'Connection failed'))
// 2023-12-25T10:30:00.000Z [INFO ] Application started
// 2023-12-25T10:30:01.000Z [ERROR] Connection failed
}
Object.getOwnPropertyDescriptors()
返回对象所有自有属性的属性描述符,用于深度克隆和对象合并。
javascript
{
// 基本用法
const obj = {
name: 'Alice',
age: 25,
get info() {
return `${this.name} is ${this.age} years old`
},
set info(value) {
const [name, age] = value.split(' is ')
this.name = name
this.age = parseInt(age)
}
}
const descriptors = Object.getOwnPropertyDescriptors(obj)
console.log(descriptors)
/*
{
name: { value: 'Alice', writable: true, enumerable: true, configurable: true },
age: { value: 25, writable: true, enumerable: true, configurable: true },
info: {
get: [Function: get info],
set: [Function: set info],
enumerable: true,
configurable: true
}
}
*/
// 深度克隆(包含 getter/setter)
function deepClone(obj) {
return Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
}
const cloned = deepClone(obj)
console.log(cloned.info) // 'Alice is 25 years old'
cloned.name = 'Bob'
console.log(obj.name) // 'Alice' (原对象未改变)
console.log(cloned.info) // 'Bob is 25 years old'
// 对象合并(保留属性描述符)
function mergeObjects(target, source) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source))
return target
}
const target = { x: 1 }
const source = {
y: 2,
get z() { return this.y * 2 }
}
const merged = mergeObjects(target, source)
console.log(merged.z) // 4
// 实际应用:配置对象处理
const defaultConfig = {
host: 'localhost',
port: 3000,
get url() {
return `http://${this.host}:${this.port}`
}
}
// 隐藏某些属性
Object.defineProperty(defaultConfig, 'secret', {
value: 'hidden-key',
writable: false,
enumerable: false,
configurable: false
})
console.log(Object.keys(defaultConfig)) // ['host', 'port']
console.log(Object.getOwnPropertyDescriptors(defaultConfig))
// 显示所有属性描述符,包括不可枚举的
// 创建不可变对象
function createImmutable(obj) {
const descriptors = Object.getOwnPropertyDescriptors(obj)
// 将所有属性设为不可写
Object.keys(descriptors).forEach(key => {
if (descriptors[key].value !== undefined) {
descriptors[key].writable = false
}
descriptors[key].configurable = false
})
return Object.create(Object.getPrototypeOf(obj), descriptors)
}
const immutableObj = createImmutable({ a: 1, b: 2 })
// immutableObj.a = 999 // 在严格模式下会报错
console.log(immutableObj.a) // 1
}
Trailing Commas in Function Parameters
函数参数列表和调用中允许尾随逗号。
javascript
{
// ES8 之前 - 语法错误
// function oldStyle(a, b, c,) { } // SyntaxError
// ES8 - 允许尾随逗号
function newStyle(
param1,
param2,
param3, // 尾随逗号 OK
) {
return param1 + param2 + param3
}
// 函数调用中也可以使用
const result = newStyle(
1,
2,
3, // 尾随逗号 OK
)
console.log(result) // 6
// 实际应用:多行参数定义
function createUser(
firstName,
lastName,
email,
age,
address,
phoneNumber,
isActive, // 方便添加新参数
) {
return {
firstName,
lastName,
email,
age,
address,
phoneNumber,
isActive
}
}
// API 调用示例
async function fetchUserData(
userId,
includeProfile,
includeSettings,
includeHistory, // 便于扩展
) {
const params = new URLSearchParams({
userId,
includeProfile,
includeSettings,
includeHistory,
})
return fetch(`/api/users?${params}`)
}
// 数组和对象中早已支持
const array = [
1,
2,
3, // OK since ES3
]
const object = {
a: 1,
b: 2,
c: 3, // OK since ES5
}
// 有助于版本控制
// 添加新参数时,git diff 更清晰
function oldWay(a, b, c) {
// 添加参数 d 时,需要修改 c 行
}
function newWay(
a,
b,
c,
// 添加 d 只需要新增一行,不影响其他行
) {
// 更好的版本控制体验
}
}
Async/Await
基于 Promise 的异步编程语法糖,让异步代码看起来像同步代码。
javascript
{
// Promise 方式
function fetchUserWithPromise(userId) {
return fetch(`/api/users/${userId}`)
.then(response => response.json())
.then(user => {
return fetch(`/api/users/${user.id}/posts`)
})
.then(response => response.json())
.then(posts => {
return { user, posts }
})
.catch(error => {
console.error('Error:', error)
throw error
})
}
// Async/Await 方式
async function fetchUserWithAsync(userId) {
try {
const userResponse = await fetch(`/api/users/${userId}`)
const user = await userResponse.json()
const postsResponse = await fetch(`/api/users/${user.id}/posts`)
const posts = await postsResponse.json()
return { user, posts }
} catch (error) {
console.error('Error:', error)
throw error
}
}
// await 的执行机制
async function demonstrateAwait() {
console.log('1. Start')
// await 非 Promise 值
const value1 = await 42
console.log('2. Non-promise value:', value1) // 42
// await Promise
const value2 = await Promise.resolve('Hello')
console.log('3. Promise value:', value2) // 'Hello'
// await 延迟的 Promise
const value3 = await new Promise(resolve => {
setTimeout(() => resolve('Delayed'), 1000)
})
console.log('4. Delayed value:', value3) // 'Delayed' (1秒后)
return 'Done'
}
// demonstrateAwait().then(result => console.log('5. Final:', result))
// 错误处理
async function handleErrors() {
try {
const result = await Promise.reject(new Error('Something went wrong'))
} catch (error) {
console.log('Caught error:', error.message)
}
// 多个 await 的错误处理
try {
const user = await fetchUser()
const posts = await fetchPosts(user.id)
const comments = await fetchComments(posts)
return { user, posts, comments }
} catch (error) {
// 任何一个 await 失败都会被捕获
console.error('Pipeline failed:', error)
return null
}
}
// 并发执行
async function parallelExecution() {
// 串行执行 - 较慢
const start1 = Date.now()
const result1 = await delay(1000, 'First')
const result2 = await delay(1000, 'Second')
console.log(`Serial: ${Date.now() - start1}ms`) // ~2000ms
// 并行执行 - 较快
const start2 = Date.now()
const [result3, result4] = await Promise.all([
delay(1000, 'Third'),
delay(1000, 'Fourth')
])
console.log(`Parallel: ${Date.now() - start2}ms`) // ~1000ms
}
function delay(ms, value) {
return new Promise(resolve => setTimeout(() => resolve(value), ms))
}
// 实际应用:数据处理管道
async function processUserData(userId) {
try {
// 获取用户基本信息
const user = await fetchUser(userId)
console.log(`Processing user: ${user.name}`)
// 并行获取相关数据
const [profile, settings, activity] = await Promise.all([
fetchUserProfile(userId),
fetchUserSettings(userId),
fetchUserActivity(userId)
])
// 处理数据
const processedData = {
...user,
profile: await processProfile(profile),
settings: normalizeSettings(settings),
activitySummary: summarizeActivity(activity)
}
// 保存结果
await saveProcessedData(processedData)
return processedData
} catch (error) {
console.error(`Failed to process user ${userId}:`, error)
throw error
}
}
// 模拟异步函数
async function fetchUser(id) {
await delay(100)
return { id, name: `User ${id}`, email: `user${id}@example.com` }
}
async function fetchUserProfile(id) {
await delay(150)
return { userId: id, bio: 'A great user', avatar: 'avatar.jpg' }
}
async function fetchUserSettings(id) {
await delay(80)
return { theme: 'dark', notifications: true, language: 'en' }
}
async function fetchUserActivity(id) {
await delay(200)
return [
{ action: 'login', timestamp: Date.now() },
{ action: 'view_page', timestamp: Date.now() - 1000 }
]
}
async function processProfile(profile) {
await delay(50)
return { ...profile, processed: true }
}
function normalizeSettings(settings) {
return { ...settings, normalized: true }
}
function summarizeActivity(activity) {
return { totalActions: activity.length, lastAction: activity[0] }
}
async function saveProcessedData(data) {
await delay(100)
console.log('Data saved successfully')
}
// 使用示例
// processUserData(123).then(data => console.log('Final result:', data))
}