JavaScript进阶篇——第二章 高级特性核心
目录
一、闭包
二、变量提升
三、函数提升
四、函数参数
1. 动态参数 (arguments)
2. 剩余参数 (...rest)
五、展开运算符
六、剩余参数 vs 展开运算符
本文摘要:JavaScript核心概念解析,包含闭包、变量/函数提升、函数参数及展开运算符。闭包由内层函数和外层变量组成,用于数据封装;变量提升仅var存在,let/const有块级作用域;函数声明会整体提升。函数参数处理中,剩余参数(...rest)替代arguments更实用。展开运算符(...)用于数组/对象展开、合并等场景。关键区别:let/const无提升,剩余参数是真数组,展开运算符实现浅拷贝。记忆口诀:"闭包封数据,提升要小心,三点神通广"概括核心要点。
一、闭包
核心概念
闭包 = 内层函数 + 外层函数的变量
内层函数可以访问外层函数作用域中的变量
基本结构
function outer() {// 外层函数变量const a = 1;// 内层函数(闭包)function inner() {console.log(a); // 访问外层变量}return inner;
}const closure = outer();
closure(); // 输出 1
简约写法
function counter() {let count = 0;return function() {count++;console.log(`调用次数: ${count}`);};
}const increment = counter();
increment(); // 调用次数: 1
increment(); // 调用次数: 2
实际应用场景
-
数据私有化
function createBankAccount(initial) {let balance = initial; // 私有变量return {deposit: (amount) => balance += amount,withdraw: (amount) => balance -= amount,getBalance: () => balance}; }const account = createBankAccount(1000); account.deposit(500); console.log(account.getBalance()); // 1500 // 无法直接访问 balance
-
事件处理封装
function setupButton(buttonId) {let clickCount = 0;const btn = document.getElementById(buttonId);btn.addEventListener('click', () => {clickCount++;btn.textContent = `点击了 ${clickCount} 次`;}); }setupButton('myBtn');
⚠️ 注意事项
-
闭包会导致外部函数变量无法被GC回收
-
过度使用可能导致内存泄漏
-
在循环中创建闭包需谨慎
二、变量提升
核心机制
var
声明的变量会被提升到当前作用域顶部
只提升声明,不提升赋值
代码示例
console.log(name); // undefined(不会报错)
var name = '小明';
console.log(name); // '小明'// 实际执行顺序:
// var name; // 声明提升
// console.log(name); // undefined
// name = '小明'; // 赋值
// console.log(name); // '小明'
不同声明方式对比
声明方式 | 变量提升 | 块作用域 | 重复声明 | 初始值 |
---|---|---|---|---|
var | ✅ | ❌ | ✅ | undefined |
let | ❌ | ✅ | ❌ | 未初始化 |
const | ❌ | ✅ | ❌ | 必须赋值 |
❗ 最佳实践
-
始终使用
let
和const
-
避免使用
var
-
声明放在作用域顶部
三、函数提升
函数声明 vs 函数表达式
类型 | 提升 | 调用时机 | 示例 |
---|---|---|---|
函数声明 | ✅ | 声明前后均可调用 | function foo() {} |
函数表达式 | ❌ | 赋值后调用 | const bar = function() {} |
代码示例
// 函数声明(可提升)
sayHello(); // ✅ 正常执行
function sayHello() {console.log('Hello!');
}// 函数表达式(不提升)
try {sayHi(); // ❌ 报错
} catch(e) {console.error(e); // TypeError: sayHi is not a function
}
const sayHi = function() {console.log('Hi!');
}
⚠️ 注意事项
-
函数声明整体提升(包括函数体)
-
函数表达式仅提升变量声明(类似var)
-
避免在块内使用函数声明(行为不一致)
四、函数参数
1. 动态参数 (arguments)
函数内部伪数组,包含所有传入的实参
function sum() {let total = 0;for(let i = 0; i < arguments.length; i++) {total += arguments[i];}return total;
}console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20)); // 30
特点:
-
伪数组(有length,无数组方法)
-
箭头函数不可用
-
不推荐使用(被剩余参数替代)
2. 剩余参数 (...rest)
将多余实参收集到真数组中
function config(baseURL, ...other) {console.log('基础URL:', baseURL);console.log('其他参数:', other);
}config('https://api.com', 'GET', 'json', true);
/* 输出:
基础URL: https://api.com
其他参数: ['GET', 'json', true]
*/
优势:
-
真数组(可使用所有数组方法)
-
清晰区分必需参数和可选参数
-
箭头函数可用
五、展开运算符
核心功能
将数组展开为逗号分隔的元素序列
应用场景
// 1. 求数组最大/最小值
const scores = [95, 89, 72, 100];
console.log(Math.max(...scores)); // 100
console.log(Math.min(...scores)); // 72// 2. 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2, 5];
console.log(combined); // [1, 2, 3, 4, 5]// 3. 复制数组
const original = [10, 20];
const copy = [...original]; // 浅拷贝// 4. 函数参数传递
const points = [5, 10, 15];
function plot(x, y, z) {console.log(`坐标: (${x},${y},${z})`);
}
plot(...points); // 坐标: (5,10,15)// 5. 对象展开(ES2018+)
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a:1, b:2, c:3 }
⚠️ 注意事项
-
展开运算符实现的是浅拷贝
-
对象展开是ES2018新增特性
-
字符串也可展开:
console.log(...'hello') // h e l l o
六、剩余参数 vs 展开运算符
对比解析
特性 | 剩余参数 | 展开运算符 |
---|---|---|
语法位置 | 函数参数列表 | 数组/对象字面量 |
主要功能 | 收集多余参数为数组 | 展开数组/对象为元素序列 |
符号 | ... 在形参前 | ... 在数组/对象前 |
结果类型 | 数组 | 逗号分隔序列 |
典型场景 | 函数定义中处理不定数量参数 | 函数调用、数组合并、对象合并 |
代码示例
// 剩余参数(收集)
function collect(a, b, ...rest) {console.log(rest);
}
collect(1, 2, 3, 4); // [3, 4]// 展开运算符(展开)
const nums = [3, 4];
collect(1, 2, ...nums); // [3, 4] 等价于 collect(1,2,3,4)
✅ 高级特性核心要点总结
📝 高频面试题速答
-
Q:什么是闭包?有什么作用?
A:闭包是内层函数+外层变量,用于数据封装和私有化
-
Q:let/const 和 var 的主要区别?
A:let/const有块作用域且不提升,var有函数作用域且会提升
-
Q:剩余参数和 arguments 的区别?
A:剩余参数是真数组,arguments是伪数组
-
Q:展开运算符的主要用途?
A:展开数组/对象,用于函数传参、数组合并、对象合并等
-
Q:函数声明和函数表达式的区别?
A:函数声明会提升,函数表达式不会提升
🧠 记忆口诀
"闭包封数据,提升要小心,三点神通广"
闭包:封装数据实现私有化
提升:var和函数声明会提升
三点:
...
既是剩余参数也是展开运算符