介绍ES6的功能
- let和const:增加了块级作用域变量
- 箭头函数:使用箭头 => 定义函数,方便简洁
- 模板字符串:使用反引号 `` 和 ${} 实现字符串插值
- 解构赋值:从对象或数组中提取值,赋给不同的变量
- 参数默认值:设置函数参数的默认值,未传入时使用默认值
- 扩展运算符:快速遍历数组,合并数组等,用…表示
- Promise:用同步的方式写异步代码,避免回调地狱
- class类: 实现面向对象编程
- 模块化:import和export模块化语法
- 遍历数据结构的新方法:for…of, Array.from(), Map/Set等
- 异步函数async/await: 同步方式编写异步代码
- 新数据结构Map/Set等
let、const以及var的区别
- 作用域不同
- var 声明的变量只有全局作用域和函数作用域
- let 和 const 声明的变量有块级作用域
- 变量提升不同
- var 声明的变量会变量提升到作用域顶部
- let 和 const 不会变量提升
- 暂时性死区
- let 和 const 声明的变量会有一个暂时性死区,访问会报错
- var 不会有这个问题
- 重复声明
- var 可以无限次重复声明同一个变量
- let 和 const 不允许在同一作用域内重复声明
- 是否常量
- const 声明的变量是一个常量,初始化后不能修改
- var 和 let 声明的变量值可以修改
总结:
在全局作用域中优先使用 const,块级作用域优先考虑 let
需要修改的变量用 let 声明
全局常量使用 const 声明
避免使用 var
浅拷⻉和深拷⻉的区别
-
拷贝层级不同
- 浅拷贝只拷贝一层对象的属性, deeper level 的对象还是共享的。
- 深拷贝将对象完全拷贝一份出来,每一级别的数据都会拷贝。
-
共享引用情况不同
- 浅拷贝会共享子对象的引用,源对象和拷贝对象指向同一个子对象。
- 深拷贝不会共享任何引用,源对象和拷贝对象中的子对象都是完全独立的。
-
修改源对象是否会影响拷贝对象
- 浅拷贝会被影响,因为子对象是共享的。
- 深拷贝不会被影响,因为所有层级数据都是完全独立的。
-
实现方式不同
- 浅拷贝可以使用 Object.assign() 或展开语法 {…obj} 实现。
- 深拷贝可以通过递归、JSON.parse(JSON.stringify())等方式实现。
所以在需要完全独立的对象拷贝时要用深拷贝,而如果只需要一层数据,可以用浅拷贝来优化性能。需要根据具体需求选择合适的拷贝类型。
介绍箭头函数的this
箭头函数中的this与普通函数不同,它的this是词法作用域中的this,由上下文确定。
主要的区别有:
- 普通函数的this是调用的时候确定的,箭头函数的this是在定义时绑定的,后面无法修改。
- 普通函数作为对象的方法调用,this指向对象,箭头函数作为方法调用,this还是指向上下文。
- 普通函数调用call/apply/bind可以修改this,箭头函数不可以。
- 普通函数的this在严格模式和非严格模式下有区别,箭头函数this在两种模式下都是一样的。
- 普通函数的this在事件回调中指向触发事件的元素,箭头函数的this在回调中继承外层this。
let obj = {
name: 'Jack',
normal() {
console.log(this.name)
},
arrow: () => {
console.log(this.name)
}
}
obj.normal() // Jack
obj.arrow() // undefined
所以箭头函数可以更好地保持this绑定,避免由this引起的一些问题。但作为对象方法时要注意this不再绑定对象,需要根据情况而定是否要使用。
介绍Promise和then
Promise和then都是用于异步编程
Promise是一种异步编程的解决方案,有以下几个特点:
- 对象的状态不受外界影响,Promise对象代表一个异步操作,一旦状态改变,就不会再变化。
有三种状态:Pending(进行中)、Resolved(已完成)和 Rejected(已失败)。 - Promise对象用then方法分别指定Resolved和Reject状态的回调函数。then方法返回的是一个新的Promise实例。
then方法是Promise最基础的方法,作用是为Promise注册回调
- then方法接收两个参数,onFulfilled和onRejected,分别对应Promise成功或失败的回调。
- then方法返回一个新的Promise,可以链式调用。后面then根据前面then的返回值来确定自身的状态。
- 如果then中的回调函数返回的是一个Promise,那后面的then会等待它的状态改变;如果返回的是一个普通值,那下一个then会在下一轮事件循环中执行。
示例代码:
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
p.then(result => {
console.log(result) // 'success'
return new Promise((resolve, reject) => {
// do something
})
}).then(result2 => {
console.log(result2)
})
介绍快速排序
快速排序是一种非常高效的排序算法,利用了分治策略,可以实现在O(nlogn)的时间复杂度内对数组进行排序。
其基本思想是:
- 选择一个基准值(pivot),通常选择数组的第一个元素。
- 对数组进行分割,将小于基准值的元素放到基准值的左边,大于基准值的元素放到基准值的右边。称为分区(partition)操作。
- 对左右两个分区递归进行快速排序。
代码实现:
function quickSort(arr) {
if (arr.length <= 1) {
return arr;
}
let pivot = arr[0];
let left = [];
let right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat(pivot, quickSort(right));
}
快速排序通过设定基准值分割数组,可以很好地利用分治策略将计算复杂度降低到O(nlogn),是最快的排序算法之一。但它是不稳定的排序算法。
算法:前K个最⼤的元素
对一个无序数组寻找前K个最大的元素,可以使用如下算法:
建立一个大小为K的大根堆(最大堆)
遍历数组,依次将每个元素和堆顶元素(当前堆中最大值)比较:
如果大于堆顶,删除堆顶,将该元素插入堆中
如果小于等于堆顶,不做处理
遍历完成后,堆中剩余的K个元素就是数组中前K大的元素
// 考虑时间复杂度:快速选择算法,平均时间复杂度为O(n)
// 快速选择算法是基于快速排序的分治方法。它将数组分为两部分
// 一部分包含比选定的枢纽元素小的所有元素
// 另一部分包含比枢纽元素大的所有元素。
function partition(nums, left, right) {
let pivot = nums[Math.floor((right + left) / 2)], i = left, j = right;
while (i <= j) {
while (nums[i] > pivot) i++;
while (nums[j] < pivot) j--;
if (i <= j) {
[nums[i], nums[j]] = [nums[j], nums[i]];
i++;
j--;
}
}
return i;
}
function quickSelect(nums, left, right, k) {
if (nums.length > 1) {
let index = partition(nums, left, right);
if (left < index - 1 && k <= index) {
return quickSelect(nums, left, index - 1, k);
}
if (index < right && k >= index) {
return quickSelect(nums, index, right, k);
}
}
return nums.slice(0, k);
}
function findKthLargest(nums, k) {
return quickSelect(nums, 0, nums.length - 1, k);
}
// 示例
const nums = [3, 2, 1, 5, 6, 4];
const k = 2;
console.log(findKthLargest(nums, k));
// 数据量大的话不可行, O(nlogn)
function findKthLargest(nums, k) {
// 对数组进行降序排序
nums.sort((a, b) => b - a);
// 选取前K个元素
return nums.slice(0, k);
}
// 示例
const nums = [3, 2, 1, 5, 6, 4];
const k = 2;
console.log(findKthLargest(nums, k));