%% 变量声明特性对比
flowchart TD
subgraph 变量声明方式
A[var]
C[let]
E[const]
end
A --> B[函数作用域
可重复声明
变量提升] C --> D[块级作用域
不可重复声明
暂时性死区] E --> F[块级作用域
必须初始化
引用不可变] classDef keyword fill:#ffeaa7,stroke:#d63031 class A,C,E keyword
可重复声明
变量提升] C --> D[块级作用域
不可重复声明
暂时性死区] E --> F[块级作用域
必须初始化
引用不可变] classDef keyword fill:#ffeaa7,stroke:#d63031 class A,C,E keyword
ES6核心功能概览
ES6(ECMAScript 2015)是JavaScript发展史上的重要里程碑,带来了众多革命性特性:
🎯主要功能列表
let/const
变量声明- 箭头函数
=>
- 模板字符串
Hello ${name}
- 解构赋值
[a, b] = [1, 2]
- 默认参数
function(a = 1)
- 扩展运算符
...array
- Promise异步处理
- Class类语法
- Module模块系统
- Set/Map数据结构
🆚 let、const 与 var 的区别
📊详细对比表格
特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | 是(undefined) | 是(暂时性死区) | 是(暂时性死区) |
重复声明 | 允许 | ❌禁止 | ❌禁止 |
重新赋值 | ✅允许 | ✅允许 | ❌禁止 |
初始化 | 可选 | 可选 | 必须 |
💥实际代码演示
javascript
function scopeTest() {
// var 的函数作用域
if (true) {
var a = 1;
let b = 2;
const c = 3;
}
console.log(a); // 1 - 可访问
// console.log(b); // ReferenceError
// console.log(c); // ReferenceError
// 重复声明问题
var name = 'Jake';
var name = 'Tom'; // ✅ 允许
// let age = 20;
// let age = 25; // ❌ SyntaxError
}
// 暂时性死区示例
function temporalDeadZone() {
// console.log(x); // ReferenceError
let x = 1;
}
📋浅拷贝 vs 深拷贝
🎯核心区别
浅拷贝 :只复制对象的第一层属性,嵌套对象仍指向同一内存地址
深拷贝:递归复制对象的所有层级,完全独立的新对象
💻实现代码对比
javascript
// 浅拷贝实现
function shallowCopy(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
const result = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = obj[key]; // 直接赋值引用
}
}
return result;
}
// 深拷贝实现(简化版)
function deepCopy(obj, map = new WeakMap()) {
if (typeof obj !== 'object' || obj === null) return obj;
if (map.has(obj)) return map.get(obj); // 处理循环引用
const result = Array.isArray(obj) ? [] : {};
map.set(obj, result); // 记录已拷贝对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepCopy(obj[key], map); // 递归拷贝
}
}
return result;
}
// 测试用例
const original = {
name: 'Jake',
skills: ['JS', 'CSS'],
address: { city: 'Beijing' }
};
const shallow = shallowCopy(original);
const deep = deepCopy(original);
original.skills.push('React');
console.log(shallow.skills); // ['JS', 'CSS', 'React'] - 被影响
console.log(deep.skills); // ['JS', 'CSS'] - 不受影响
⚡快速排序算法详解
🎯算法原理
快速排序采用"分治法"思想:
- 选择一个基准元素(pivot)
- 将数组分为两部分:小于基准的放左边,大于基准的放右边
- 递归处理左右两部分
💻核心实现
javascript
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivotIndex = Math.floor(arr.length / 2);
const pivot = arr[pivotIndex];
const left = [];
const right = [];
for (let i = 0; i < arr.length; i++) {
if (i === pivotIndex) continue; // 跳过基准元素
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 递归排序并合并结果
return [...quickSort(left), pivot, ...quickSort(right)];
}
// 测试
const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(quickSort(arr)); // [11, 12, 22, 25, 34, 64, 90]
📊时间复杂度分析
- 最好情况:O(n log n) - 每次都能均匀分割
- 最坏情况:O(n²) - 每次选到最大或最小元素作为基准
- 平均情况:O(n log n)
🏆前K个最大元素算法
🎯问题描述
给定一个数组,找出其中前K个最大的元素
💻多种解法对比
javascript
// 方法1: 排序后取前K个 - 时间复杂度 O(n log n)
function topKBySort(nums, k) {
return nums.sort((a, b) => b - a).slice(0, k);
}
// 方法2: 最小堆 - 时间复杂度 O(n log k)
class MinHeap {
constructor() {
this.heap = [];
}
push(val) {
this.heap.push(val);
this.up(this.heap.length - 1);
}
pop() {
if (this.heap.length === 1) return this.heap.pop();
const top = this.heap[0];
this.heap[0] = this.heap.pop();
this.down(0);
return top;
}
up(index) {
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
if (this.heap[parentIndex] <= this.heap[index]) break;
[this.heap[parentIndex], this.heap[index]] =
[this.heap[index], this.heap[parentIndex]];
index = parentIndex;
}
}
down(index) {
const lastIndex = this.heap.length - 1;
while (true) {
let minIndex = index;
const leftIndex = index * 2 + 1;
const rightIndex = index * 2 + 2;
if (leftIndex <= lastIndex &&
this.heap[leftIndex] < this.heap[minIndex]) {
minIndex = leftIndex;
}
if (rightIndex <= lastIndex &&
this.heap[rightIndex] < this.heap[minIndex]) {
minIndex = rightIndex;
}
if (minIndex === index) break;
[this.heap[index], this.heap[minIndex]] =
[this.heap[minIndex], this.heap[index]];
index = minIndex;
}
}
size() { return this.heap.length; }
top() { return this.heap[0]; }
}
function topKByHeap(nums, k) {
const heap = new MinHeap();
for (const num of nums) {
heap.push(num);
if (heap.size() > k) {
heap.pop(); // 弹出最小值,保持堆大小为k
}
}
return heap.heap.sort((a, b) => b - a); // 降序返回
}
// 测试用例
const nums = [3, 2, 1, 5, 6, 4];
const k = 2;
console.log(topKBySort(nums, k)); // [6, 5]
console.log(topKByHeap(nums, k)); // [6, 5]
Q1: 为什么说const更安全?
A1: const声明的变量不能被重新赋值,避免了意外修改,特别是在复杂对象中保护引用不被改变 😱
Q2: 深拷贝如何处理循环引用?
A2: 使用WeakMap记录已拷贝的对象,再次遇到时直接返回引用,避免无限递归 💥
Q3: 快排的基准元素选择有什么讲究?
A3: 随机选择或三数取中法可以避免最坏情况,提升平均性能 🎯
Q4: 堆排序和快排哪个更适合找TopK?
A4: 堆排序更适合,时间复杂度稳定为O(n log k),而快排平均O(n)但最坏O(n²)
Q5: ES6的解构赋值在实际项目中有哪些妙用?
A5: 函数参数解构、数组交换、对象属性提取等,代码更简洁易读 🚀