当前位置: 首页 > java >正文

ES6手录02-字符串与函数的扩展

画板

一、变量的扩展

(一)字符串的扩展

1. 模板字符串

模板字符串是ES6中对字符串功能的重要增强,解决了传统字符串在多换行、变量拼接等场景下的繁琐问题,极大提升了代码可读性和编写效率。

(1)语法特点
  • 界定符:使用反引号(```,位于键盘ESC键下方)包裹字符串内容,而非传统的单引号(’ ')或双引号(" ")。
  • 多换行支持:字符串可直接在代码中换行,无需使用\n转义字符或+进行字符串拼接。例如:
// 传统字符串(需拼接或转义)
const oldStr = "第一行\n第二行\n第三行";
const oldStr2 = "第一行" +"第二行" +"第三行";// 模板字符串(直接换行)
const newStr = `第一行
第二行
第三行`;
console.log(newStr); // 输出时保留换行格式
  • 变量与表达式嵌入:通过${ }语法在字符串中嵌入变量、表达式,甚至调用函数,嵌入内容会自动计算并转换为字符串。例如:
const name = "Alice";
const age = 25;
const score = 92;// 嵌入变量
const str1 = `我的名字是${name}`; 
// 嵌入表达式
const str2 = `明年我就${age + 1}岁了`;
const str3 = `成绩等级:${score >= 90 ? "优秀" : "良好"}`;
// 嵌入函数调用
const getTime = () => new Date().getFullYear();
const str4 = `当前年份:${getTime()}`;console.log(str1); // 我的名字是Alice
console.log(str2); // 明年我就26岁了
console.log(str3); // 成绩等级:优秀
console.log(str4); // 当前年份:2024(根据实际年份变化)
(2)${ }语法规则
  • 支持内容
    • 变量(如${userName})、常量(如${PI});
    • 算术、逻辑、三元等表达式(如${a * b}${isValid ? "通过" : "失败"});
    • 函数调用(如${formatDate(date)})。
  • 不支持内容
    • 控制语句(如ifforwhile),若需条件判断需使用三元表达式替代;
    • 多行代码块,嵌入内容需是单个表达式或语句。
  • 作用范围:仅在模板字符串中生效,普通字符串中${ }会被视为普通文本。例如:
const num = 10;
console.log("数值:${num}"); // 输出:数值:${num}(普通字符串不解析)
console.log(`数值:${num}`); // 输出:数值:10(模板字符串解析)
(3)核心应用场景
  • DOM操作中的HTML构建:传统方式需拼接大量字符串和变量,易出错且可读性差;模板字符串可直接嵌入变量和多换行HTML结构,简化代码。例如:
const user = { name: "Bob", age: 30, isVip: true };// 传统方式(繁琐且易出错)
const oldHtml = '<div class="user-card">' +'<h3>' + user.name + '</h3>' +'<p>年龄:' + user.age + '</p>' +'<p>会员:' + (user.isVip ? '是' : '否') + '</p>' +'</div>';// 模板字符串(简洁清晰)
const newHtml = `<div class="user-card"><h3>${user.name}</h3><p>年龄:${user.age}</p><p>会员:${user.isVip ? '是' : '否'}</p>
</div>`;document.body.innerHTML = newHtml;
  • 复杂字符串拼接:如日志输出、动态URL构建等场景。例如:
const logType = "error";
const message = "数据库连接失败";
const time = new Date().toLocaleString();// 动态日志
const log = `[${time}] [${logType.toUpperCase()}]:${message}`;
console.log(log); // 输出:[2024/5/20 14:30:00] [ERROR]:数据库连接失败// 动态URL
const apiUrl = `https://api.example.com/users?name=${encodeURIComponent(name)}&age=${age}`;
(4)注意事项
  • 反引号转义:若字符串中需包含反引号,需使用\转义(如const str = 他说:Hello ``)。
  • 嵌套使用:支持模板字符串嵌套,需注意{ }匹配,避免语法错误。例如:
const arr = [1, 2, 3];
const nestedStr = `数组内容:${arr.map(item => `[${item}]`).join(", ")}`;
console.log(nestedStr); // 输出:数组内容:[1], [2], [3]
  • 性能考量:模板字符串的解析效率与传统字符串拼接相近,无需担心性能问题;但频繁拼接极长字符串时,建议结合StringBuilder类或数组join方法优化(虽现代JS引擎已优化此场景)。

二、函数的扩展

ES6对函数的语法和功能进行了多维度增强,包括默认参数、rest参数、箭头函数等,解决了传统函数的诸多痛点,提升了代码简洁性和逻辑性。

(一)函数默认参数

1. 传统实现的缺陷(ES5及之前)

在ES6之前,函数默认参数需通过逻辑或运算符(||)实现,但存在明显缺陷:当传入0falsenull""等“假值”时,会被误判为“未传参”,从而使用默认值,不符合预期。例如:

// ES5 实现默认参数(有缺陷)
function getScore(score) {// 若传入0,会被||判定为假值,返回默认值60(错误)const finalScore = score || 60;return finalScore;
}console.log(getScore()); // 60(正确,未传参)
console.log(getScore(85)); // 85(正确)
console.log(getScore(0)); // 60(错误,预期输出0)
console.log(getScore(false)); // 60(错误,预期输出false)
2. ES6默认参数语法

ES6允许直接在函数形参位置赋值,实现默认参数,且能精确判断“是否传参”,而非依赖“真值判断”。

(1)基本用法
  • 语法格式function 函数名(形参1=默认值1, 形参2=默认值2, ...) { }
  • 执行逻辑:调用函数时,若某参数未传递(或传递undefined),则使用默认值;若传递有效值(包括0false等),则使用实参值。例如:
// ES6 默认参数(无缺陷)
function getScore(score = 60) {return score;
}console.log(getScore()); // 60(未传参,用默认值)
console.log(getScore(undefined)); // 60(传undefined,用默认值)
console.log(getScore(0)); // 0(传0,用实参值)
console.log(getScore(false)); // false(传false,用实参值)
(2)高级特性
  • 默认值支持表达式:默认值可是变量、函数调用或复杂表达式,表达式在函数调用时(而非定义时)计算。例如:
// 默认值为变量
const defaultAge = 18;
function getUserAge(age = defaultAge) {return age;
}// 默认值为函数调用
function getDefaultName() {return "匿名用户";
}
function getUserName(name = getDefaultName()) {return name;
}// 默认值为复杂表达式
function calculate(a, b = a * 2) {return a + b;
}console.log(getUserAge()); // 18
console.log(getUserName()); // 匿名用户
console.log(calculate(3)); // 3 + 6 = 9
  • 部分参数设默认值:可仅对部分参数设置默认值,但建议将带默认值的参数放在参数列表末尾,避免传参顺序混乱。例如:
// 推荐:带默认值的参数放末尾
function fetchData(url, method = "GET", timeout = 5000) {console.log(`URL: ${url}, 方法: ${method}, 超时: ${timeout}`);
}fetchData("https://api.example.com"); // URL: ..., 方法: GET, 超时: 5000
fetchData("https://api.example.com", "POST"); // 方法: POST, 超时: 5000
fetchData("https://api.example.com", "PUT", 10000); // 超时: 10000// 不推荐:带默认值的参数放中间(传参需跳过默认值时需传undefined)
function fn(a = 1, b, c = 3) {console.log(a, b, c);
}
fn(undefined, 2); // 1, 2, 3(需传undefined跳过a的默认值,易出错)
(3)注意事项
  • 参数作用域:默认参数形成独立作用域,与函数体作用域隔离,参数间可相互引用,但不能引用函数体内的变量。例如:
// 正确:参数b引用参数a
function fn(a = 1, b = a + 1) {return b;
}
console.log(fn()); // 2// 错误:参数a引用函数体内的变量d(d未定义)
function fn2(a = d, b) {const d = 10;return a;
}
fn2(); // ReferenceError: d is not defined
  • 默认值不可重复声明:若函数参数与默认值中的变量同名,会抛出语法错误。例如:
// 错误:参数name与默认值中的name重复
function fn(name = name) {return name;
}
fn(); // SyntaxError: Identifier 'name' has already been declared

(二)rest参数

1. 传统方案的不足(arguments对象)

ES5中,若需获取函数的“剩余参数”(不确定数量的参数),需使用arguments对象,但arguments类数组对象(有索引和length,但无数组方法如mapfilter),需先通过Array.from(arguments)[...arguments]转换为数组,操作繁琐。例如:

// ES5:用arguments获取剩余参数(需转换)
function sum() {// 将arguments转为数组const args = Array.from(arguments);return args.reduce((total, curr) => total + curr, 0);
}console.log(sum(1, 2, 3)); // 6
console.log(sum(4, 5, 6, 7)); // 22
2. ES6 rest参数语法

rest参数(形式为...变量名)用于收集函数的“剩余参数”,并直接返回一个真正的数组,无需转换即可使用数组方法,简化代码。

(1)基本用法
  • 语法格式function 函数名(形参1, 形参2, ...rest) { },其中rest为自定义变量名(通常用restargs)。
  • 执行逻辑rest收集“形参列表之后的所有实参”,若没有剩余参数,rest为空数组(而非undefined)。例如:
// ES6:用rest参数获取剩余参数(直接是数组)
function sum(...rest) {return rest.reduce((total, curr) => total + curr, 0);
}console.log(sum(1, 2, 3)); // 6(rest = [1,2,3])
console.log(sum(4, 5)); // 9(rest = [4,5])
console.log(sum()); // 0(rest = [])// 结合固定参数使用
function printUser(name, age, ...hobbies) {console.log(`姓名:${name},年龄:${age}`);console.log(`爱好:${hobbies.join(", ")}`);
}printUser("Alice", 25, "阅读", "跑步", "编程"); 
// 姓名:Alice,年龄:25
// 爱好:阅读, 跑步, 编程
(2)与arguments对象的区别
特性rest参数arguments对象
数据类型真正的数组(可直接用mapfilter等方法)类数组对象(需转换为数组才能用数组方法)
收集范围仅收集“形参之后的剩余参数”收集函数的所有实参(包括固定形参)
作用域仅在函数体内可见,与普通变量一致函数体内自动生成,不可删除(非严格模式)
可读性明确声明“剩余参数”,代码更清晰需阅读函数体才能确定是否用于收集剩余参数
(3)注意事项
  • 位置限制:rest参数必须是函数的最后一个形参,后面不能有其他参数,否则会抛出语法错误。例如:
// 错误:rest参数后有其他参数
function fn(...rest, a) { } 
// SyntaxError: Rest parameter must be last formal parameter// 正确:rest参数在最后
function fn(a, ...rest) { }
  • 与默认参数结合:rest参数可与默认参数共存,但需确保rest在最后。例如:
function fn(a = 1, ...rest) {console.log(a, rest);
}fn(); // 1, []
fn(2, 3, 4); // 2, [3,4]
  • 箭头函数中的使用:箭头函数不支持arguments对象,若需收集剩余参数,必须使用rest参数。例如:
// 箭头函数用rest参数收集剩余参数
const multiply = (...rest) => rest.reduce((total, curr) => total * curr, 1);
console.log(multiply(2, 3, 4)); // 24

(三)箭头函数

箭头函数(() => {})是ES6中最具代表性的语法之一,它简化了函数声明,同时改变了this的绑定规则,解决了传统函数中this指向混乱的问题。

1. 基本语法

箭头函数的语法可根据“参数数量”和“函数体复杂度”进行简化,核心结构为“参数列表 + => + 函数体”。

场景传统函数语法箭头函数语法(简化后)
无参数function() { ... }() => { ... }
单个参数function(a) { ... }a => { ... }(可省略小括号)
多个参数function(a, b) { ... }(a, b) => { ... }
函数体仅返回一个值function(a) { return a * 2; }a => a * 2(省略大括号和return
函数体有多行代码function(a, b) { const c = a + b; return c; }(a, b) => { const c = a + b; return c; }(需保留大括号和return

示例:

// 1. 无参数,返回固定值
const getDefault = () => "默认值";
console.log(getDefault()); // 默认值// 2. 单个参数,返回计算结果
const double = num => num * 2;
console.log(double(5)); // 10// 3. 多个参数,多行函数体
const calculate = (a, b) =>

三、扩展运算符(…)

扩展运算符(...)是ES6中极具灵活性的语法特性,其核心功能是“拆分数据结构”,与rest参数(用于“收集参数”)形成“逆运算”关系。它支持数组、字符串、类数组、对象等多种数据类型,在函数调用、数据复制、结构合并等场景中应用广泛。

(一)基本概念与核心特性

1. 定义

扩展运算符由三个连续的点(...)组成,作用是将可迭代对象(数组、字符串、Set、Map、arguments等)或对象拆分为“独立的参数序列”或“键值对集合”,本质是“解构”的一种简化形式。

2. 核心特性
  • 方向性:仅用于“拆分”,不改变原数据结构(原数据不会被修改);
  • 灵活性:可与普通参数/属性混合使用,支持部分拆分;
  • 局限性:对普通对象(无Iterator接口)的拆分需在对象字面量内进行,不能直接单独使用(如...{a:1}会报错)。

(二)扩展运算符的应用场景

1. 函数调用:拆分数组为参数序列

这是扩展运算符最基础的应用场景,可将数组元素直接作为函数的独立参数传递,替代传统的apply()方法,代码更简洁。

(1)基础用法
// 传统方式:用apply()将数组转为参数
function add(a, b, c) {return a + b + c;
}
const numbers = [1, 2, 3];
console.log(add.apply(null, numbers)); // 6(apply第二个参数需是数组)// ES6方式:用扩展运算符拆分数组
console.log(add(...numbers)); // 6(...numbers直接拆分为1,2,3)
(2)混合普通参数

扩展运算符可与普通参数结合,灵活控制参数位置:

function multiply(first, ...rest) {return first * rest.reduce((total, curr) => total * curr, 1);
}
const values = [2, 3, 4];
// 普通参数(5) + 扩展运算符拆分的参数(2,3,4)
console.log(multiply(5, ...values)); // 5*2*3*4 = 120
(3)控制台打印数组元素

console.log()接收多个参数,扩展运算符可直接拆分数组元素并逐个打印:

const arr = ["a", "b", "c"];
console.log(...arr); // a b c(等价于console.log("a", "b", "c"))
2. 数组操作:复制与合并

扩展运算符在数组复制和合并场景中,比传统方法(concat()slice())更简洁,但需注意“深浅拷贝”的区别。

(1)数组复制
  • 一维数组深拷贝:扩展运算符可实现一维数组的“深拷贝”(复制元素值,而非指针),修改新数组不会影响原数组;
  • 多维数组浅拷贝:对嵌套数组(多维数组),扩展运算符仅复制外层数组的指针,内层数组仍共享内存,修改内层元素会同步影响原数组。

示例:

// 1. 一维数组复制(深拷贝)
const arr1 = [1, 2, 3];
const arr2 = [...arr1]; // 等价于arr1.slice()或[].concat(arr1)
arr2.push(4);
console.log(arr1); // [1,2,3](原数组不变)
console.log(arr2); // [1,2,3,4](新数组修改)// 2. 多维数组复制(浅拷贝)
const arr3 = [1, [2, 3], 4];
const arr4 = [...arr3];
arr4[1][0] = 9; // 修改内层数组元素
console.log(arr3); // [1, [9,3], 4](原数组内层元素同步变化)
console.log(arr4); // [1, [9,3], 4](新数组变化)

深拷贝解决方案:若需完全深拷贝多维数组,需使用JSON.parse(JSON.stringify(arr))(适用于无函数/正则的数组)或递归拷贝函数:

// 递归深拷贝函数
function deepClone(obj) {if (typeof obj !== "object" || obj === null) return obj;const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {newObj[key] = deepClone(obj[key]);}return newObj;
}const arr5 = [1, [2, 3], 4];
const arr6 = deepClone(arr5);
arr6[1][0] = 9;
console.log(arr5); // [1, [2,3], 4](原数组不变)
(2)数组合并

扩展运算符可快速合并多个数组,支持“部分合并”和“多数组合并”,比concat()更直观:

const arrA = [1, 2];
const arrB = [3, 4];
const arrC = [5];// 1. 合并多个数组
const mergedArr1 = [...arrA, ...arrB, ...arrC];
console.log(mergedArr1); // [1,2,3,4,5]// 2. 合并时添加新元素
const mergedArr2 = [0, ...arrA, 2.5, ...arrB];
console.log(mergedArr2); // [0,1,2,2.5,3,4]
3. 对象操作:复制与合并

扩展运算符可用于对象的“浅复制”和“合并”,需在对象字面量({})内使用,语法为{...obj}

(1)对象复制(浅拷贝)
  • 复制对象的所有可枚举属性到新对象,修改新对象的顶层属性不会影响原对象;
  • 若对象包含嵌套对象(深层属性),则嵌套对象仍为浅拷贝(共享指针)。

示例:

const obj1 = { name: "Alice", age: 25 };
const obj2 = { ...obj1 }; // 浅复制obj1的顶层属性obj2.age = 26;
console.log(obj1); // { name: "Alice", age: 25 }(原对象不变)
console.log(obj2); // { name: "Alice", age: 26 }(新对象修改)// 嵌套对象浅拷贝
const obj3 = { name: "Bob", info: { city: "Beijing" } };
const obj4 = { ...obj3 };
obj4.info.city = "Shanghai";
console.log(obj3); // { name: "Bob", info: { city: "Shanghai" } }(原对象嵌套属性变化)
(2)对象合并

合并多个对象时,后出现的对象属性会覆盖同名的前对象属性,支持“合并+新增属性”的组合操作。

示例:

const objA = { a: 1, b: 2 };
const objB = { b: 3, c: 4 };
const objC = { d: 5 };// 1. 合并objA和objB(objB的b覆盖objA的b)
const mergedObj1 = { ...objA, ...objB };
console.log(mergedObj1); // { a:1, b:3, c:4 }// 2. 合并+新增属性(新增e:6)
const mergedObj2 = { ...objA, ...objB, ...objC, e: 6 };
console.log(mergedObj2); // { a:1, b:3, c:4, d:5, e:6 }

Object.assign()的对比

  • 功能一致:{...obj1, ...obj2}等价于Object.assign({}, obj1, obj2)
  • 区别:扩展运算符更简洁,且支持“部分属性合并”(如{ ...obj1, b: 10 }),而Object.assign()需额外处理。
4. 类数组与可迭代对象转换

扩展运算符可将“类数组对象”(如arguments、DOM节点集合)或“可迭代对象”(如Set、Map、字符串)转为真实数组,替代Array.from()

(1)类数组对象转换

类数组对象(有length属性和索引,但无数组方法)可通过[...类数组]转为真实数组:

// 1. arguments转换
function fn() {const args = [...arguments]; // 转为真实数组console.log(args.map(item => item * 2)); // 支持数组方法map
}
fn(1, 2, 3); // [2,4,6]// 2. DOM节点集合转换
const divs = document.querySelectorAll("div"); // NodeList(类数组)
const divArr = [...divs]; // 转为真实数组
divArr.forEach(div => div.style.color = "red"); // 支持forEach
(2)可迭代对象转换
  • Set:拆分Set元素并转为数组(自动去重);
  • Map:拆分Map为“键值对数组”([key, value]);
  • 字符串:拆分字符串为字符数组(支持Unicode字符,比split("")更可靠)。

示例:

// 1. Set转换(去重)
const set = new Set([1, 2, 2, 3]);
const setArr = [...set];
console.log(setArr); // [1,2,3]// 2. Map转换
const map = new Map([["name", "Charlie"], ["age", 30]]);
const mapArr = [...map];
console.log(mapArr); // [ ["name", "Charlie"], ["age", 30] ]// 3. 字符串转换(支持Unicode)
const str = "hello😀"; // 😀是Unicode字符(占2个字节)
const strArr1 = [...str]; // 正确拆分:["h","e","l","l","o","😀"]
const strArr2 = str.split(""); // 错误拆分:["h","e","l","l","o","�","�"]
console.log(strArr1); // 正确
console.log(strArr2); // 错误

(三)注意事项

  1. 普通对象不能直接拆分...{a:1}单独使用会报错,必须在对象字面量内使用(如const newObj = { ...{a:1} });
  2. 浅拷贝风险:对嵌套数组/对象,扩展运算符仅复制指针,修改深层数据会影响原数据,需根据需求选择深拷贝方案;
  3. 可迭代对象限制:仅支持有Iterator接口的对象(如数组、Set、Map),普通对象(无Iterator)需通过Object.entries(obj)转为可迭代的键值对数组后再拆分。
http://www.xdnf.cn/news/19562.html

相关文章:

  • Kotlin 协程异步任务工具类:高效处理异步操作与超时控制
  • UE5 为啥原生的NotifyState写逻辑会有问题
  • 开源低代码平台(NocoBase)
  • 20250828的学习笔记
  • 9.1日IO作业
  • 2025年09月01日Github流行趋势
  • 99、23种设计模式之组合模式(8/23)
  • 09.《路由基础知识解析和实践》
  • 基于外部对照数据借用的临床试验统计分析方案设计与仿真研究
  • PitVis-2023挑战赛:内镜下垂体瘤手术视频中的手术流程识别|文献速递-深度学习人工智能医疗图像
  • 如何把指定阿里云文件夹下的所有文件移动到另一个文件夹下,移动文件时把文件名称(不包括文件后缀)进行md5编码
  • 从理论到实践,深入剖析数据库水平拆分的安全平滑落地
  • Spark自定义累加器实现高效WordCount
  • Spark和Spring整合处理离线数据
  • promptoMANIA-AI绘画提示词生成器
  • Electron使用WebAssembly实现CRC-16 CCITT校验
  • macOS中Homebrew安装PHP的详细步骤(五)
  • 深入了解Flink核心:Slot资源管理机制
  • PostgreSQL 索引大全
  • 深入理解Docker容器技术:原理与实践
  • 如何安装CUDA????
  • three.js+WebGL踩坑经验合集(10.1):镜像问题又一坑——THREE.InstancedMesh的正反面显示问题
  • 机器学习-时序预测2
  • 基于FPGA+DSP数据采集处理平台的搭建
  • 【Vue2 ✨】Vue2 入门之旅(四):生命周期钩子
  • Unity核心概念③:Inspector窗口可编辑变量
  • C++/QT day3(9.1)
  • 深度学习中常用的激活函数
  • 关系型数据库——GaussDB的简单学习
  • Spring Boot 和 Spring Cloud 的原理和区别