学习:ES6(2)

ES6 字符串

ES6 对字符串进行了多项扩展,引入了许多新特性和方法,使字符串处理更加便捷和强大。

1. 模板字符串

基本语法

模板字符串使用反引号( ````` )包围,可以包含多行文本和嵌入表达式。

javascript 复制代码
// 传统字符串拼接
let name = "张三";
let age = 30;
let message = "我的名字是 " + name + ",今年 " + age + " 岁。";

// 使用模板字符串
let templateMessage = `我的名字是 ${name},今年 ${age} 岁。`;

console.log(message);        // "我的名字是 张三,今年 30 岁。"
console.log(templateMessage); // "我的名字是 张三,今年 30 岁。"

多行字符串

javascript 复制代码
// 传统方式
let multiLine1 = "第一行\n第二行\n第三行";

// 模板字符串方式
let multiLine2 = `第一行
第二行
第三行`;

console.log(multiLine2);
// 输出:
// 第一行
// 第二行
// 第三行

表达式嵌入

javascript 复制代码
let x = 10;
let y = 20;

// 可以嵌入任何有效的 JavaScript 表达式
let result = `${x} + ${y} = ${x + y}`;
console.log(result); // "10 + 20 = 30"

// 可以调用函数
function greet(name) {
    return `你好, ${name}!`;
}

let greeting = `${greet("张三")}`;
console.log(greeting); // "你好, 张三!"

嵌套模板字符串

javascript 复制代码
let people = [
    { name: "张三", age: 30 },
    { name: "李四", age: 25 }
];

let html = `
<ul>
    ${people.map(person => `
        <li>${person.name} - ${person.age}岁</li>
    `).join('')}
</ul>
`;

console.log(html);
/*
<ul>
    <li>张三 - 30岁</li>
    <li>李四 - 25岁</li>
</ul>
*/

标签模板

标签模板是一种高级用法,允许你自定义模板字符串的处理方式。

javascript 复制代码
function highlight(strings, ...values) {
    let result = '';
    
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            result += `<strong>${values[i]}</strong>`;
        }
    }
    
    return result;
}

let name = "张三";
let age = 30;

let highlighted = highlight`我的名字是${name},今年${age}岁。`;
console.log(highlighted); // "我的名字是<strong>张三</strong>,今年<strong>30</strong>岁。"

2. 新增字符串方法

includes() - 检查字符串是否包含子串

javascript 复制代码
let str = "Hello, World!";

console.log(str.includes("World")); // true
console.log(str.includes("world")); // false (区分大小写)
console.log(str.includes("Hello", 1)); // false (从索引1开始搜索)

startsWith() - 检查字符串是否以指定子串开头

javascript 复制代码
let str = "Hello, World!";

console.log(str.startsWith("Hello")); // true
console.log(str.startsWith("World")); // false
console.log(str.startsWith("World", 7)); // true (从索引7开始检查)

endsWith() - 检查字符串是否以指定子串结尾

javascript 复制代码
let str = "Hello, World!";

console.log(str.endsWith("World!")); // true
console.log(str.endsWith("Hello")); // false
console.log(str.endsWith("World", 12)); // true (只检查前12个字符)

repeat() - 重复字符串

javascript 复制代码
let str = "abc";

console.log(str.repeat(3)); // "abcabcabc"
console.log("hello".repeat(2)); // "hellohello"

// 参数为小数时会向下取整
console.log("x".repeat(2.9)); // "xx"

// 参数为负数或Infinity会抛出错误
// console.log("x".repeat(-1)); // RangeError
// console.log("x".repeat(Infinity)); // RangeError

padStart() - 字符串开头填充

javascript 复制代码
let str = "5";

// 填充到指定长度
console.log(str.padStart(2, "0")); // "05"

// 填充字符串可以重复使用
console.log("abc".padStart(10, "0123456789")); // "0123456abc"

// 默认使用空格填充
console.log("hello".padStart(10)); // "     hello"

// 如果原字符串长度大于等于指定长度,返回原字符串
console.log("hello".padStart(3)); // "hello"

padEnd() - 字符串结尾填充

javascript 复制代码
let str = "5";

// 填充到指定长度
console.log(str.padEnd(2, "0")); // "50"

// 填充字符串可以重复使用
console.log("abc".padEnd(10, "0123456789")); // "abc0123456"

// 默认使用空格填充
console.log("hello".padEnd(10)); // "hello     "

trimStart() / trimLeft() - 去除开头空白

javascript 复制代码
let str = "  hello world  ";

console.log(str.trimStart()); // "hello world  "
console.log(str.trimLeft());  // "hello world  " (trimLeft是trimStart的别名)

trimEnd() / trimRight() - 去除结尾空白

javascript 复制代码
let str = "  hello world  ";

console.log(str.trimEnd()); // "  hello world"
console.log(str.trimRight()); // "  hello world" (trimRight是trimEnd的别名)

3. Unicode 扩展

codePointAt() - 获取字符的 Unicode 码点

javascript 复制代码
let str = "𠮷a"; // 𠮷是一个4字节的Unicode字符

// charCodeAt只能处理2字节字符
console.log(str.charCodeAt(0)); // 55362 (𠮷的前两个字节)
console.log(str.charCodeAt(1)); // 57271 (𠮷的后两个字节)
console.log(str.charCodeAt(2)); // 97 (a的码点)

// codePointAt可以正确处理4字节字符
console.log(str.codePointAt(0)); // 134071 (𠮷的完整码点)
console.log(str.codePointAt(2)); // 97 (a的码点)

String.fromCodePoint() - 从码点返回字符

javascript 复制代码
// fromCharCode无法正确处理4字节Unicode字符
console.log(String.fromCharCode(0x20BB7)); // "ஷ" (错误的结果)

// fromCodePoint可以正确处理
console.log(String.fromCodePoint(0x20BB7)); // "𠮷" (正确的结果)

// 可以处理多个码点
console.log(String.fromCodePoint(0x20BB7, 0x61)); // "𠮷a"

for...of 遍历字符串

javascript 复制代码
let str = "𠮷a";

// 传统for循环无法正确处理4字节字符
for (let i = 0; i < str.length; i++) {
    console.log(str[i]); // 输出: �, �, a (𠮷被拆分成两个字符)
}

// for...of可以正确处理
for (let char of str) {
    console.log(char); // 输出: 𠮷, a
}

4. 实际应用示例

1. 格式化数字

javascript 复制代码
function formatNumber(num, length = 2) {
    return String(num).padStart(length, '0');
}

console.log(formatNumber(5));      // "05"
console.log(formatNumber(12));     // "12"
console.log(formatNumber(123, 5)); // "00123"

2. 创建日志前缀

javascript 复制代码
function log(level, message) {
    const timestamp = new Date().toISOString();
    const levelStr = String(level).padEnd(5, ' ');
    console.log(`${timestamp} [${levelStr}] ${message}`);
}

log('INFO', '应用程序启动');
log('ERROR', '发生错误');

3. 生成HTML模板

javascript 复制代码
function createUserCard(user) {
    return `
        <div class="user-card">
            <h2>${user.name}</h2>
            <p>年龄: ${user.age}</p>
            <p>邮箱: ${user.email}</p>
            <button onclick="showDetails(${user.id})">查看详情</button>
        </div>
    `;
}

let user = {
    id: 1,
    name: "张三",
    age: 30,
    email: "zhangsan@example.com"
};

document.body.innerHTML = createUserCard(user);

4. SQL查询构建器

javascript 复制代码
class QueryBuilder {
    constructor(table) {
        this.table = table;
        this.whereClauses = [];
        this.orderBy = null;
        this.limit = null;
    }
    
    where(field, operator, value) {
        this.whereClauses.push(`${field} ${operator} '${value}'`);
        return this;
    }
    
    sort(field, direction = 'ASC') {
        this.orderBy = `${field} ${direction}`;
        return this;
    }
    
    take(count) {
        this.limit = count;
        return this;
    }
    
    build() {
        let query = `SELECT * FROM ${this.table}`;
        
        if (this.whereClauses.length > 0) {
            query += ` WHERE ${this.whereClauses.join(' AND ')}`;
        }
        
        if (this.orderBy) {
            query += ` ORDER BY ${this.orderBy}`;
        }
        
        if (this.limit) {
            query += ` LIMIT ${this.limit}`;
        }
        
        return query;
    }
}

// 使用示例
let query = new QueryBuilder('users')
    .where('age', '>', 18)
    .where('status', '=', 'active')
    .sort('name', 'ASC')
    .take(10)
    .build();

console.log(query);
// 输出: SELECT * FROM users WHERE age > '18' AND status = 'active' ORDER BY name ASC LIMIT 10

5. 多语言支持

javascript 复制代码
const translations = {
    en: {
        greeting: "Hello, {name}!",
        farewell: "Goodbye, {name}!"
    },
    zh: {
        greeting: "你好,{name}!",
        farewell: "再见,{name}!"
    }
};

function translate(key, language, params = {}) {
    const template = translations[language][key];
    
    if (!template) return `[${key}]`;
    
    return template.replace(/{(\w+)}/g, (match, param) => {
        return params[param] || match;
    });
}

// 使用示例
console.log(translate('greeting', 'en', { name: '张三' })); // "Hello, 张三!"
console.log(translate('farewell', 'zh', { name: '李四' })); // "再见,李四!"

总结

ES6 对字符串的扩展主要包括:

  1. 模板字符串:提供了更强大的字符串拼接和多行字符串支持
  2. 新增方法 :如 includes()startsWith()endsWith()repeat()padStart()padEnd()
  3. Unicode 支持:更好地处理4字节Unicode字符
  4. 标签模板:提供自定义字符串处理的能力

这些新特性使字符串处理更加简洁、直观和强大,特别是在模板生成、国际化、格式化等场景中非常有用。

ES6 数值

ES6 对 JavaScript 的数值类型进行了多项扩展,引入了新的方法和特性,使数值处理更加便捷和精确。

1. 二进制和八进制表示法

二进制表示法

ES6 引入了二进制表示法,使用 0b0B 前缀:

javascript 复制代码
// 二进制表示
let binary1 = 0b1010; // 等于十进制的 10
let binary2 = 0B1111; // 等于十进制的 15

console.log(binary1); // 10
console.log(binary2); // 15

八进制表示法

ES6 规范了八进制表示法,使用 0o0O 前缀:

javascript 复制代码
// 八进制表示
let octal1 = 0o10; // 等于十进制的 8
let octal2 = 0O17; // 等于十进制的 15

console.log(octal1); // 8
console.log(octal2); // 15

2. Number 对象的新方法

Number.isFinite() - 检查是否为有限数值

javascript 复制代码
// 传统方法
console.log(isFinite(10));      // true
console.log(isFinite(Infinity)); // false
console.log(isFinite("10"));    // true (字符串会被转换)

// Number.isFinite() 不会进行类型转换
console.log(Number.isFinite(10));      // true
console.log(Number.isFinite(Infinity)); // false
console.log(Number.isFinite("10"));    // false (字符串不会被转换)

Number.isNaN() - 检查是否为 NaN

javascript 复制代码
// 传统方法
console.log(isNaN(NaN));      // true
console.log(isNaN("NaN"));    // true (字符串会被转换)
console.log(isNaN(10));       // false

// Number.isNaN() 不会进行类型转换
console.log(Number.isNaN(NaN));      // true
console.log(Number.isNaN("NaN"));    // false (字符串不会被转换)
console.log(Number.isNaN(10));       // false

Number.isInteger() - 检查是否为整数

javascript 复制代码
console.log(Number.isInteger(10));    // true
console.log(Number.isInteger(10.0));  // true (10.0 在数值上等于 10)
console.log(Number.isInteger(10.5));  // false
console.log(Number.isInteger("10"));  // false (字符串不会被转换)
console.log(Number.isInteger(true));  // false
console.log(Number.isInteger(NaN));   // false

Number.isSafeInteger() - 检查是否为安全整数

安全整数是指在 JavaScript 中能够精确表示的整数,范围在 -(2^53 - 1)2^53 - 1 之间。

javascript 复制代码
console.log(Number.isSafeInteger(10));                    // true
console.log(Number.isSafeInteger(Math.pow(2, 53) - 1));    // true
console.log(Number.isSafeInteger(Math.pow(2, 53)));        // false
console.log(Number.isSafeInteger(Math.pow(2, 53) + 1));    // false

// 安全整数范围
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 (2^53 - 1)
console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991 (-(2^53 - 1))

Number.parseFloat() - 解析浮点数

javascript 复制代码
// 与全局 parseFloat 功能相同
console.log(Number.parseFloat("3.14"));      // 3.14
console.log(Number.parseFloat("3.14abc"));  // 3.14
console.log(Number.parseFloat("abc3.14"));  // NaN
console.log(Number.parseFloat("0x10"));     // 0 (0x前缀不会被识别为十六进制)

Number.parseInt() - 解析整数

javascript 复制代码
// 与全局 parseInt 功能相同
console.log(Number.parseInt("10"));        // 10
console.log(Number.parseInt("10.5"));       // 10
console.log(Number.parseInt("10abc"));      // 10
console.log(Number.parseInt("abc10"));      // NaN
console.log(Number.parseInt("0x10"));       // 16 (0x前缀被识别为十六进制)
console.log(Number.parseInt("10", 8));      // 8 (八进制)

3. Math 对象的新方法

Math.trunc() - 去除小数部分

javascript 复制代码
console.log(Math.trunc(4.9));    // 4
console.log(Math.trunc(4.1));    // 4
console.log(Math.trunc(-4.9));   // -4
console.log(Math.trunc(-4.1));   // -4
console.log(Math.trunc("4.9"));  // 4 (字符串会被转换)
console.log(Math.trunc("foo"));  // NaN

Math.sign() - 判断数值的符号

javascript 复制代码
console.log(Math.sign(5));    // 1 (正数)
console.log(Math.sign(-5));   // -1 (负数)
console.log(Math.sign(0));    // 0 (零)
console.log(Math.sign(-0));   // -0 (负零)
console.log(Math.sign("5"));  // 1 (字符串会被转换)
console.log(Math.sign("foo")); // NaN

Math.cbrt() - 计算立方根

javascript 复制代码
console.log(Math.cbrt(8));     // 2 (2的立方是8)
console.log(Math.cbrt(27));    // 3 (3的立方是27)
console.log(Math.cbrt(-8));    // -2 (-2的立方是-8)
console.log(Math.cbrt(0));     // 0

Math.hypot() - 计算平方和的平方根

javascript 复制代码
// 计算直角三角形的斜边长度
console.log(Math.hypot(3, 4));    // 5 (3² + 4² = 25,平方根是5)
console.log(Math.hypot(5, 12));   // 13 (5² + 12² = 169,平方根是13)

// 可以接受多个参数
console.log(Math.hypot(1, 2, 3)); // 3.7416573867739413 (1² + 2² + 3² = 14,平方根是√14)

指数和对数方法

javascript 复制代码
// Math.expm1() - 返回 e^x - 1
console.log(Math.expm1(0));       // 0 (e^0 - 1 = 1 - 1 = 0)
console.log(Math.expm1(1));       // 1.718281828459045 (e^1 - 1)

// Math.log1p() - 返回 ln(1 + x)
console.log(Math.log1p(0));       // 0 (ln(1 + 0) = ln(1) = 0)
console.log(Math.log1p(1));       // 0.6931471805599453 (ln(2))

// Math.log10() - 返回以10为底的对数
console.log(Math.log10(10));      // 1 (10^1 = 10)
console.log(Math.log10(100));     // 2 (10^2 = 100)

// Math.log2() - 返回以2为底的对数
console.log(Math.log2(2));        // 1 (2^1 = 2)
console.log(Math.log2(8));        // 3 (2^3 = 8)

三角函数方法

javascript 复制代码
// 双曲三角函数
console.log(Math.sinh(0));        // 0 (双曲正弦)
console.log(Math.cosh(0));        // 1 (双曲余弦)
console.log(Math.tanh(0));        // 0 (双曲正切)

// 反双曲三角函数
console.log(Math.asinh(0));       // 0 (反双曲正弦)
console.log(Math.acosh(1));       // 0 (反双曲余弦)
console.log(Math.atanh(0));       // 0 (反双曲正切)

4. 指数运算符 (**)

ES6 引入了指数运算符 ** ,相当于 Math.pow()

javascript 复制代码
// 基本用法
console.log(2 ** 3);      // 8 (2的3次方)
console.log(3 ** 2);      // 9 (3的2次方)

// 结合赋值运算符
let x = 2;
x **= 3;
console.log(x);           // 8

// 与 Math.pow() 的区别
console.log((-5) ** 2);   // 25
console.log(Math.pow(-5, 2)); // 25

// 指数运算符右结合
console.log(2 ** 3 ** 2); // 512 (等同于 2 ** (3 ** 2))

5. 实际应用示例

1. 数值验证

javascript 复制代码
// 验证输入是否为有效整数
function validateInteger(input) {
    if (typeof input !== 'number' || !Number.isInteger(input)) {
        throw new Error('输入必须是整数');
    }
    return input;
}

// 验证输入是否为安全整数
function validateSafeInteger(input) {
    if (!Number.isSafeInteger(input)) {
        throw new Error('输入必须在安全整数范围内');
    }
    return input;
}

// 使用示例
try {
    let num = validateInteger(10.5); // 抛出错误
} catch (e) {
    console.error(e.message);
}

try {
    let bigNum = validateSafeInteger(Math.pow(2, 53)); // 抛出错误
} catch (e) {
    console.error(e.message);
}

2. 数值格式化

javascript 复制代码
// 格式化数字为指定小数位数
function formatDecimal(num, decimals = 2) {
    const factor = 10 ** decimals;
    return Math.trunc(num * factor) / factor;
}

// 格式化数字为指定有效数字
function formatSignificant(num, digits) {
    if (num === 0) return 0;
    
    const magnitude = Math.floor(Math.log10(Math.abs(num)));
    const factor = 10 ** (digits - magnitude - 1);
    return Math.trunc(num * factor) / factor;
}

// 使用示例
console.log(formatDecimal(3.14159, 2));    // 3.14
console.log(formatDecimal(3.145, 2));      // 3.14 (不是四舍五入)
console.log(formatSignificant(1234.567, 3)); // 1230
console.log(formatSignificant(0.00123456, 3)); // 0.00123

3. 数值范围检查

javascript 复制代码
// 检查数值是否在指定范围内
function isInRange(num, min, max) {
    return num >= min && num <= max;
}

// 检查数值是否在安全整数范围内
function isSafeRange(num) {
    return Number.isSafeInteger(num) && 
           num >= Number.MIN_SAFE_INTEGER && 
           num <= Number.MAX_SAFE_INTEGER;
}

// 使用示例
console.log(isInRange(5, 1, 10));    // true
console.log(isInRange(0, 1, 10));    // false
console.log(isSafeRange(9007199254740991)); // true
console.log(isSafeRange(9007199254740992)); // false

4. 数值转换工具

javascript 复制代码
// 二进制转十进制
function binaryToDecimal(binary) {
    return Number.parseInt(binary, 2);
}

// 八进制转十进制
function octalToDecimal(octal) {
    return Number.parseInt(octal, 8);
}

// 十进制转二进制
function decimalToBinary(decimal) {
    return decimal.toString(2);
}

// 十进制转八进制
function decimalToOctal(decimal) {
    return decimal.toString(8);
}

// 使用示例
console.log(binaryToDecimal('1010'));    // 10
console.log(octalToDecimal('10'));       // 8
console.log(decimalToBinary(10));        // '1010'
console.log(decimalToOctal(8));          // '10'

5. 数学计算工具

javascript 复制代码
// 计算两点之间的距离
function distance(x1, y1, x2, y2) {
    return Math.hypot(x2 - x1, y2 - y1);
}

// 计算向量的长度
function vectorLength(x, y, z = 0) {
    return Math.hypot(x, y, z);
}

// 角度转弧度
function degreesToRadians(degrees) {
    return degrees * (Math.PI / 180);
}

// 弧度转角度
function radiansToDegrees(radians) {
    return radians * (180 / Math.PI);
}

// 使用示例
console.log(distance(0, 0, 3, 4));      // 5
console.log(vectorLength(3, 4));        // 5
console.log(vectorLength(1, 2, 2));     // 3
console.log(degreesToRadians(180));      // 3.141592653589793
console.log(radiansToDegrees(Math.PI));  // 180

总结

ES6 对数值的扩展主要包括:

  1. 新的表示法 :二进制 ( 0b ) 和八进制 ( 0o ) 表示法
  2. Number 对象新方法 :如 isFinite()isNaN()isInteger()isSafeInteger()
  3. Math 对象新方法 :如 trunc()sign()cbrt()hypot()
  4. 指数运算符** 运算符,简化幂运算

这些新特性使数值处理更加精确和便捷,特别是在科学计算、数据验证和格式化等场景中非常有用。

ES6 对象

ES6 对 JavaScript 的对象进行了多项重要扩展,引入了更简洁的语法和强大的功能,使对象操作更加便捷和灵活。

1. 对象属性简写

属性值简写

当对象属性名和变量名相同时,可以简写:

javascript 复制代码
// ES5 写法
let name = "张三";
let age = 30;
let person = {
    name: name,
    age: age
};

// ES6 简写
let name = "张三";
let age = 30;
let person = {
    name,
    age
};

console.log(person); // { name: "张三", age: 30 }

方法简写

对象方法的定义可以省略 function 关键字:

javascript 复制代码
// ES5 写法
let calculator = {
    add: function(a, b) {
        return a + b;
    },
    subtract: function(a, b) {
        return a - b;
    }
};

// ES6 简写
let calculator = {
    add(a, b) {
        return a + b;
    },
    subtract(a, b) {
        return a - b;
    }
};

console.log(calculator.add(1, 2)); // 3
console.log(calculator.subtract(5, 2)); // 3

2. 属性名表达式

ES6 允许使用表达式作为对象的属性名:

javascript 复制代码
let prop = "name";
let id = 1;

// 使用表达式作为属性名
let person = {
    [prop]: "张三",
    ["user" + id]: "用户1",
    ["age_" + (id + 1)]: 25
};

console.log(person); // { name: "张三", user1: "用户1", age_2: 25 }

// 动态添加属性
let key = "dynamic";
let obj = {};
obj[key] = "动态值";
console.log(obj); // { dynamic: "动态值" }

3. 方法的 name 属性

函数的 name 属性返回函数名:

javascript 复制代码
let person = {
    sayName() {
        return "张三";
    },
    [Symbol("id")]() {
        return 123;
    }
};

console.log(person.sayName.name); // "sayName"
console.log(person[Symbol("id")].name); // "" (Symbol 方法名返回空字符串)

// 特殊情况
let obj = {
    get value() {},
    set value(v) {}
};

let descriptor = Object.getOwnPropertyDescriptor(obj, "value");
console.log(descriptor.get.name); // "get value"
console.log(descriptor.set.name); // "set value"

4. Object.is() - 比较两个值是否相等

Object.is() 方法比较两个值是否相等,类似于 === ,但有一些区别:

javascript 复制代码
// 与 === 相同的情况
console.log(Object.is(1, 1)); // true
console.log(Object.is("foo", "foo")); // true
console.log(Object.is(true, true)); // true
console.log(Object.is(null, null)); // true
console.log(Object.is(undefined, undefined)); // true

// 与 === 不同的情况
console.log(Object.is(+0, -0)); // false (=== 返回 true)
console.log(Object.is(NaN, NaN)); // true (=== 返回 false)

// 对象比较
console.log(Object.is({}, {})); // false (不同对象引用)
let obj = {};
console.log(Object.is(obj, obj)); // true (同一对象引用)

5. Object.assign() - 对象合并

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象:

javascript 复制代码
// 基本用法
let target = { a: 1, b: 2 };
let source1 = { b: 3, c: 4 };
let source2 = { d: 5 };

Object.assign(target, source1, source2);
console.log(target); // { a: 1, b: 3, c: 4, d: 5 }

// 返回目标对象
let result = Object.assign({ a: 1 }, { b: 2 }, { c: 3 });
console.log(result); // { a: 1, b: 2, c: 3 }

// 浅拷贝
let original = { a: 1, b: { c: 2 } };
let copy = Object.assign({}, original);
console.log(copy); // { a: 1, b: { c: 2 } }

// 修改嵌套对象
copy.b.c = 3;
console.log(original.b.c); // 3 (原始对象也被修改,因为是浅拷贝)

// 合并同名属性,后面的会覆盖前面的
let obj1 = { a: 1, b: 2 };
let obj2 = { b: 3, c: 4 };
let merged = Object.assign({}, obj1, obj2);
console.log(merged); // { a: 1, b: 3, c: 4 }

6. Object.getOwnPropertyDescriptors() - 获取属性描述符

该方法返回指定对象所有自身属性(非继承属性)的描述对象:

javascript 复制代码
let obj = {
    a: 1,
    get b() { return 2; },
    set c(value) { this._c = value; }
};

Object.defineProperty(obj, 'd', {
    value: 4,
    writable: false,
    enumerable: false,
    configurable: true
});

let descriptors = Object.getOwnPropertyDescriptors(obj);
console.log(descriptors);
/*
{
  a: {
    value: 1,
    writable: true,
    enumerable: true,
    configurable: true
  },
  b: {
    get: [Function: get b],
    set: undefined,
    enumerable: true,
    configurable: true
  },
  c: {
    get: undefined,
    set: [Function: set c],
    enumerable: true,
    configurable: true
  },
  d: {
    value: 4,
    writable: false,
    enumerable: false,
    configurable: true
  }
}
*/

7. __proto__属性、Object.setPrototypeOf()、Object.getPrototypeOf()

proto 属性

__proto__ 属性(前后各两个下划线)用于读取或设置当前对象的prototype对象:

javascript 复制代码
let person = {
    greet() {
        return "Hello!";
    }
};

let student = {
    study() {
        return "Studying...";
    }
};

// 设置原型
student.__proto__ = person;
console.log(student.greet()); // "Hello!" (继承自 person)
console.log(student.study()); // "Studying..." (自身方法)

Object.setPrototypeOf()

Object.setPrototypeOf() 方法的作用与 __proto__ 相同,用于设置一个对象的prototype对象:

javascript 复制代码
let person = {
    greet() {
        return "Hello!";
    }
};

let student = {
    study() {
        return "Studying...";
    }
};

// 设置原型
Object.setPrototypeOf(student, person);
console.log(student.greet()); // "Hello!" (继承自 person)
console.log(student.study()); // "Studying..." (自身方法)

Object.getPrototypeOf()

Object.getPrototypeOf() 方法用于读取一个对象的prototype对象:

javascript 复制代码
let person = {
    greet() {
        return "Hello!";
    }
};

let student = {
    study() {
        return "Studying...";
    }
};

Object.setPrototypeOf(student, person);
console.log(Object.getPrototypeOf(student) === person); // true

8. super 关键字

super 关键字用于指向当前对象的原型对象:

javascript 复制代码
let person = {
    greet() {
        return "Hello!";
    }
};

let student = {
    study() {
        return "Studying...";
    },
    greet() {
        // 调用原型对象的 greet 方法
        return super.greet() + " I'm a student.";
    }
};

Object.setPrototypeOf(student, person);
console.log(student.greet()); // "Hello! I'm a student."

9. 对象的扩展运算符 (...)

ES6 引入了扩展运算符 ... ,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:

javascript 复制代码
// 对象拷贝
let person = { name: "张三", age: 30 };
let copy = { ...person };
console.log(copy); // { name: "张三", age: 30 }

// 对象合并
let obj1 = { a: 1, b: 2 };
let obj2 = { b: 3, c: 4 };
let merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 3, c: 4 }

// 添加新属性
let personWithJob = { ...person, job: "工程师" };
console.log(personWithJob); // { name: "张三", age: 30, job: "工程师" }

// 覆盖属性
let renamedPerson = { ...person, name: "李四" };
console.log(renamedPerson); // { name: "李四", age: 30 }

// 与解构赋值结合使用
let { name, ...rest } = person;
console.log(name); // "张三"
console.log(rest); // { age: 30 }

10. 实际应用示例

1. 对象工厂函数

javascript 复制代码
function createUser(name, age, email) {
    return {
        name,
        age,
        email,
        // 方法简写
        getInfo() {
            return `${this.name} (${this.age}) - ${this.email}`;
        },
        // 计算属性
        ['is' + (age >= 18 ? 'Adult' : 'Minor')]: true
    };
}

let user = createUser("张三", 30, "zhangsan@example.com");
console.log(user.getInfo()); // "张三 (30) - zhangsan@example.com"
console.log(user.isAdult); // true

2. 对象合并工具

javascript 复制代码
// 深度合并对象
function deepMerge(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();

    if (isObject(target) && isObject(source)) {
        for (const key in source) {
            if (isObject(source[key])) {
                if (!target[key]) Object.assign(target, { [key]: {} });
                deepMerge(target[key], source[key]);
            } else {
                Object.assign(target, { [key]: source[key] });
            }
        }
    }

    return deepMerge(target, ...sources);
}

function isObject(item) {
    return item && typeof item === 'object' && !Array.isArray(item);
}

// 使用示例
let obj1 = {
    a: 1,
    b: {
        c: 2,
        d: 3
    }
};

let obj2 = {
    b: {
        d: 4,
        e: 5
    },
    f: 6
};

let result = deepMerge({}, obj1, obj2);
console.log(result);
/*
{
    a: 1,
    b: {
        c: 2,
        d: 4,
        e: 5
    },
    f: 6
}
*/

3. 对象验证工具

javascript 复制代码
// 验证对象是否包含所需属性
function validateObject(obj, requiredProps) {
    const missingProps = requiredProps.filter(prop => !(prop in obj));
    
    if (missingProps.length > 0) {
        throw new Error(`缺少必需属性: ${missingProps.join(', ')}`);
    }
    
    return true;
}

// 验证对象属性类型
function validateTypes(obj, schema) {
    for (const [prop, type] of Object.entries(schema)) {
        if (prop in obj && typeof obj[prop] !== type) {
            throw new Error(`属性 ${prop} 应为 ${type} 类型,实际为 ${typeof obj[prop]}`);
        }
    }
    
    return true;
}

// 使用示例
let user = {
    name: "张三",
    age: 30,
    email: "zhangsan@example.com"
};

try {
    validateObject(user, ['name', 'age', 'email']);
    validateTypes(user, { name: 'string', age: 'number', email: 'string' });
    console.log("验证通过");
} catch (error) {
    console.error(error.message);
}

4. 对象转换工具

javascript 复制代码
// 将对象转换为查询字符串
function objectToQueryString(obj) {
    return Object.entries(obj)
        .filter(([_, value]) => value !== undefined && value !== null)
        .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
        .join('&');
}

// 将查询字符串转换为对象
function queryStringToObject(queryString) {
    const params = new URLSearchParams(queryString);
    const obj = {};
    
    for (const [key, value] of params) {
        obj[key] = value;
    }
    
    return obj;
}

// 使用示例
let params = {
    name: "张三",
    age: 30,
    page: 1,
    limit: 10
};

let queryString = objectToQueryString(params);
console.log(queryString); // "name=%E5%BC%A0%E4%B8%89&age=30&page=1&limit=10"

let parsedObj = queryStringToObject(queryString);
console.log(parsedObj); // { name: "张三", age: "30", page: "1", limit: "10" }

总结

ES6 对对象的扩展主要包括:

  1. 属性简写:当属性名和变量名相同时可以简写
  2. 方法简写 :对象方法可以省略 function 关键字
  3. 属性名表达式:可以使用表达式作为对象的属性名
  4. 新增方法 :如 Object.is()Object.assign()Object.getOwnPropertyDescriptors()
  5. 原型操作__proto__Object.setPrototypeOf()Object.getPrototypeOf()
  6. super 关键字:用于指向当前对象的原型对象
  7. 扩展运算符... 用于对象拷贝和合并

这些新特性使对象操作更加简洁和强大,特别是在对象创建、合并、验证和转换等场景中非常有用。

ES6 数组

ES6 对 JavaScript 的数组进行了多项重要扩展,引入了许多新方法和特性,使数组操作更加便捷和强大。

1. 扩展运算符 (...)

扩展运算符(spread)是三个点( ... ),它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

javascript 复制代码
// 基本用法
console.log(...[1, 2, 3]); // 1 2 3

// 替代 apply 方法
// ES5
Math.max.apply(null, [14, 3, 77]);
// ES6
Math.max(...[14, 3, 77]);

// 数组合并
let arr1 = [1, 2];
let arr2 = [3, 4];
let merged = [...arr1, ...arr2];
console.log(merged); // [1, 2, 3, 4]

// 数组克隆
let original = [1, 2, 3];
let clone = [...original];
console.log(clone); // [1, 2, 3]

// 将字符串转为数组
let str = "hello";
let chars = [...str];
console.log(chars); // ["h", "e", "l", "l", "o"]

2. Array.from() - 将类数组对象转为数组

Array.from() 方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

javascript 复制代码
// 类数组对象转数组
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

let arr = Array.from(arrayLike);
console.log(arr); // ["a", "b", "c"]

// NodeList 转数组
let divs = document.querySelectorAll('div');
let divArray = Array.from(divs);

// 字符串转数组
let str = "hello";
let strArray = Array.from(str);
console.log(strArray); // ["h", "e", "l", "l", "o"]

// Set 转数组
let set = new Set([1, 2, 3]);
let setArray = Array.from(set);
console.log(setArray); // [1, 2, 3]

// 带映射函数
let numbers = [1, 2, 3];
let doubled = Array.from(numbers, x => x * 2);
console.log(doubled); // [2, 4, 6]

3. Array.of() - 创建数组

Array.of() 方法用于将一组值,转换为数组。

javascript 复制代码
// Array.of() 总是返回参数值组成的数组
console.log(Array.of()); // []
console.log(Array.of(1)); // [1]
console.log(Array.of(1, 2)); // [1, 2]

// 与 Array() 构造函数的区别
console.log(Array()); // []
console.log(Array(3)); // [empty × 3] (3个空位)
console.log(Array(1, 2)); // [1, 2]

console.log(Array.of(3)); // [3]
console.log(Array.of(1, 2)); // [1, 2]

4. find() 和 findIndex() - 查找数组元素

find()

find() 方法用于找出第一个符合条件的数组成员。

javascript 复制代码
let arr = [1, 5, 10, 15];

// 找出第一个大于 10 的元素
let result = arr.find((value, index, arr) => {
    return value > 10;
});
console.log(result); // 15

// 找出第一个偶数
let even = arr.find(value => value % 2 === 0);
console.log(even); // 10

// 找不到返回 undefined
let notFound = arr.find(value => value > 20);
console.log(notFound); // undefined

findIndex()

findIndex() 方法返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

javascript 复制代码
let arr = [1, 5, 10, 15];

// 找出第一个大于 10 的元素的索引
let index = arr.findIndex((value, index, arr) => {
    return value > 10;
});
console.log(index); // 3

// 找出第一个偶数的索引
let evenIndex = arr.findIndex(value => value % 2 === 0);
console.log(evenIndex); // 2

// 找不到返回 -1
let notFoundIndex = arr.findIndex(value => value > 20);
console.log(notFoundIndex); // -1

5. fill() - 填充数组

fill() 方法使用给定值,填充一个数组。

javascript 复制代码
let arr = [1, 2, 3, 4, 5];

// 填充整个数组
arr.fill(0);
console.log(arr); // [0, 0, 0, 0, 0]

// 指定填充范围
arr = [1, 2, 3, 4, 5];
arr.fill(0, 2); // 从索引 2 开始填充到末尾
console.log(arr); // [1, 2, 0, 0, 0]

// 指定填充范围(包含开始索引,不包含结束索引)
arr = [1, 2, 3, 4, 5];
arr.fill(0, 1, 3); // 从索引 1 开始,到索引 3 结束(不包含 3)
console.log(arr); // [1, 0, 0, 4, 5]

// 初始化空数组
let emptyArr = new Array(5).fill(0);
console.log(emptyArr); // [0, 0, 0, 0, 0]

6. entries()、keys() 和 values() - 遍历数组

entries()

entries() 方法返回一个遍历器对象,用于遍历数组的键值对。

javascript 复制代码
let arr = ['a', 'b', 'c'];
let iterator = arr.entries();

for (let [index, value] of iterator) {
    console.log(index, value);
}
// 0 "a"
// 1 "b"
// 2 "c"

keys()

keys() 方法返回一个遍历器对象,用于遍历数组的键名。

javascript 复制代码
let arr = ['a', 'b', 'c'];
let iterator = arr.keys();

for (let index of iterator) {
    console.log(index);
}
// 0
// 1
// 2

values()

values() 方法返回一个遍历器对象,用于遍历数组的键值。

javascript 复制代码
let arr = ['a', 'b', 'c'];
let iterator = arr.values();

for (let value of iterator) {
    console.log(value);
}
// "a"
// "b"
// "c"

7. includes() - 检查数组是否包含某个值

includes() 方法返回一个布尔值,表示某个数组是否包含给定的值。

javascript 复制代码
let arr = [1, 2, 3, NaN];

// 基本用法
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false

// 可以检测 NaN
console.log(arr.includes(NaN)); // true
// 而 indexOf 无法检测 NaN
console.log(arr.indexOf(NaN)); // -1

// 指定开始搜索的位置
console.log(arr.includes(2, 1)); // true (从索引 1 开始搜索)
console.log(arr.includes(2, 2)); // false (从索引 2 开始搜索)

8. flat() 和 flatMap() - 数组扁平化

flat()

flat() 方法将嵌套的数组"拉平",变成一维数组。

javascript 复制代码
// 基本用法
let arr = [1, [2, 3]];
console.log(arr.flat()); // [1, 2, 3]

// 多层嵌套
let arr2 = [1, [2, [3, 4]]];
console.log(arr2.flat()); // [1, 2, [3, 4]] (默认只拉平一层)

// 指定拉平层数
console.log(arr2.flat(2)); // [1, 2, 3, 4] (拉平两层)

// 不管有多少层嵌套,都要转成一维数组
let arr3 = [1, [2, [3, [4, 5]]]];
console.log(arr3.flat(Infinity)); // [1, 2, 3, 4, 5]

// 自动跳过空位
let arr4 = [1, , 2, , 3];
console.log(arr4.flat()); // [1, 2, 3]

flatMap()

flatMap() 方法对原数组的每个成员执行一个函数,然后对返回值组成的数组执行 flat() 方法。

javascript 复制代码
// 基本用法
let arr = [1, 2, 3];
let result = arr.flatMap(x => [x * 2]);
console.log(result); // [2, 4, 6]

// 相当于
let result2 = arr.map(x => [x * 2]).flat();
console.log(result2); // [2, 4, 6]

// 只能展开一层
let arr2 = [1, 2, 3];
let result3 = arr2.flatMap(x => [[x * 2]]);
console.log(result3); // [[2], [4], [6]] (只展开一层)

// 实际应用:提取句子中的单词
let sentences = [
    "hello world",
    "javascript is awesome",
    "es6 features"
];

let words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ["hello", "world", "javascript", "is", "awesome", "es6", "features"]

9. 实际应用示例

1. 数组去重

javascript 复制代码
// 使用 Set 去重
function uniqueArray(arr) {
    return [...new Set(arr)];
}

// 使用 filter 和 indexOf 去重
function uniqueArray2(arr) {
    return arr.filter((item, index) => arr.indexOf(item) === index);
}

// 使用 reduce 去重
function uniqueArray3(arr) {
    return arr.reduce((unique, item) => {
        return unique.includes(item) ? unique : [...unique, item];
    }, []);
}

// 使用示例
let arr = [1, 2, 2, 3, 4, 4, 5];
console.log(uniqueArray(arr));   // [1, 2, 3, 4, 5]
console.log(uniqueArray2(arr));  // [1, 2, 3, 4, 5]
console.log(uniqueArray3(arr));  // [1, 2, 3, 4, 5]

2. 数组分组

javascript 复制代码
// 按条件分组
function groupBy(arr, key) {
    return arr.reduce((groups, item) => {
        const group = typeof key === 'function' ? key(item) : item[key];
        groups[group] = groups[group] || [];
        groups[group].push(item);
        return groups;
    }, {});
}

// 使用示例
let people = [
    { name: "张三", age: 30, gender: "男" },
    { name: "李四", age: 25, gender: "女" },
    { name: "王五", age: 35, gender: "男" },
    { name: "赵六", age: 28, gender: "女" }
];

// 按性别分组
let byGender = groupBy(people, 'gender');
console.log(byGender);
/*
{
    "男": [
        { name: "张三", age: 30, gender: "男" },
        { name: "王五", age: 35, gender: "男" }
    ],
    "女": [
        { name: "李四", age: 25, gender: "女" },
        { name: "赵六", age: 28, gender: "女" }
    ]
}
*/

// 按年龄范围分组
let byAgeRange = groupBy(people, person => {
    if (person.age < 30) return "青年";
    else return "中年";
});
console.log(byAgeRange);
/*
{
    "青年": [
        { name: "李四", age: 25, gender: "女" },
        { name: "赵六", age: 28, gender: "女" }
    ],
    "中年": [
        { name: "张三", age: 30, gender: "男" },
        { name: "王五", age: 35, gender: "男" }
    ]
}
*/

3. 数组排序工具

javascript 复制代码
// 多字段排序
function multiSort(arr, fields) {
    return arr.sort((a, b) => {
        for (let field of fields) {
            const { key, order = 'asc' } = typeof field === 'string' ? { key: field } : field;
            
            if (a[key] < b[key]) return order === 'asc' ? -1 : 1;
            if (a[key] > b[key]) return order === 'asc' ? 1 : -1;
        }
        return 0;
    });
}

// 使用示例
let products = [
    { name: "手机", price: 5000, category: "电子产品" },
    { name: "电脑", price: 8000, category: "电子产品" },
    { name: "衣服", price: 200, category: "服装" },
    { name: "鞋子", price: 300, category: "服装" }
];

// 先按类别升序,再按价格降序
let sorted = multiSort(products, [
    { key: 'category', order: 'asc' },
    { key: 'price', order: 'desc' }
]);

console.log(sorted);
/*
[
    { name: "电脑", price: 8000, category: "电子产品" },
    { name: "手机", price: 5000, category: "电子产品" },
    { name: "鞋子", price: 300, category: "服装" },
    { name: "衣服", price: 200, category: "服装" }
]
*/

4. 数组分页

javascript 复制代码
// 数组分页
function paginate(arr, page = 1, pageSize = 10) {
    const startIndex = (page - 1) * pageSize;
    const endIndex = startIndex + pageSize;
    
    return {
        data: arr.slice(startIndex, endIndex),
        pagination: {
            page,
            pageSize,
            total: arr.length,
            totalPages: Math.ceil(arr.length / pageSize)
        }
    };
}

// 使用示例
let items = Array.from({ length: 25 }, (_, i) => i + 1); // [1, 2, ..., 25]

let page1 = paginate(items, 1, 10);
console.log(page1);
/*
{
    data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
    pagination: {
        page: 1,
        pageSize: 10,
        total: 25,
        totalPages: 3
    }
}
*/

let page3 = paginate(items, 3, 10);
console.log(page3);
/*
{
    data: [21, 22, 23, 24, 25],
    pagination: {
        page: 3,
        pageSize: 10,
        total: 25,
        totalPages: 3
    }
}
*/

5. 数组转换工具

javascript 复制代码
// 树形数组转平铺数组
function flattenTree(tree, childrenKey = 'children') {
    return tree.reduce((flat, item) => {
        const { [childrenKey]: children, ...rest } = item;
        return [
            ...flat,
            rest,
            ...(children ? flattenTree(children, childrenKey) : [])
        ];
    }, []);
}

// 平铺数组转树形数组
function arrayToTree(arr, idKey = 'id', parentKey = 'parentId', childrenKey = 'children') {
    const tree = [];
    const map = {};
    
    // 创建映射
    arr.forEach(item => {
        map[item[idKey]] = { ...item, [childrenKey]: [] };
    });
    
    // 构建树
    arr.forEach(item => {
        const node = map[item[idKey]];
        if (item[parentKey] === null || item[parentKey] === undefined) {
            tree.push(node);
        } else {
            const parent = map[item[parentKey]];
            if (parent) {
                parent[childrenKey].push(node);
            }
        }
    });
    
    return tree;
}

// 使用示例
let flatArray = [
    { id: 1, name: "根节点", parentId: null },
    { id: 2, name: "子节点1", parentId: 1 },
    { id: 3, name: "子节点2", parentId: 1 },
    { id: 4, name: "子节点1-1", parentId: 2 }
];

let tree = arrayToTree(flatArray);
console.log(tree);
/*
[
    {
        id: 1,
        name: "根节点",
        parentId: null,
        children: [
            {
                id: 2,
                name: "子节点1",
                parentId: 1,
                children: [
                    {
                        id: 4,
                        name: "子节点1-1",
                        parentId: 2,
                        children: []
                    }
                ]
            },
            {
                id: 3,
                name: "子节点2",
                parentId: 1,
                children: []
            }
        ]
    }
]
*/

let flattened = flattenTree(tree);
console.log(flattened);
/*
[
    { id: 1, name: "根节点", parentId: null },
    { id: 2, name: "子节点1", parentId: 1 },
    { id: 4, name: "子节点1-1", parentId: 2 },
    { id: 3, name: "子节点2", parentId: 1 }
]
*/

总结

ES6 对数组的扩展主要包括:

  1. 扩展运算符... 用于数组展开、合并等
  2. Array.from():将类数组对象转为数组
  3. Array.of():创建数组的新方法
  4. find() 和 findIndex():查找数组元素
  5. fill():填充数组
  6. entries()、keys() 和 values():遍历数组的新方法
  7. includes():检查数组是否包含某个值
  8. flat() 和 flatMap():数组扁平化

这些新特性使数组操作更加简洁和强大,特别是在数据处理、转换和搜索等场景中非常有用。

相关推荐
河铃旅鹿3 小时前
Android开发-java版:Framgent
android·java·笔记·学习
自动化代码美学5 小时前
【Python3.13】官网学习之控制流
开发语言·windows·python·学习
命运之光6 小时前
【最新】ChromeDriver最新版本下载安装教程,ChromeDriver版本与Chrome不匹配问题
前端·chrome
AA陈超6 小时前
ASC学习笔记0020:用于定义角色或Actor的默认属性值
c++·笔记·学习·ue5·虚幻引擎
星离~7 小时前
Vue响应式原理详解:从零实现一个迷你Vue
前端·javascript·vue.js
梦6508 小时前
React 简介
前端·react.js·前端框架
一只小阿乐8 小时前
react 中的判断显示
前端·javascript·vue.js·react.js·react
光影少年8 小时前
useMemo 和 React.memo区别
前端·react.js·前端框架
小沐°8 小时前
React-页码组件
前端·javascript·react.js