ES6
Set与Map以及WeakSet与WeakMap
参考文章:# ES6数据结构深度解析:Set, Map, WeakSet 和 WeakMap # 参考文章:js 中 Map 和 Set 的用法以及区别
Set与Map的区别(标准答案)
1.定义与用途
- Map:是一个存储键值对的数据结构,每个键值都是唯一的,用于映射到一个值。适合用于需要通过唯一标识符快速查找数据的场景
- Set:是一个不包含重复元素的集合。主要用于存储唯一项,确保集合中的每个元素都是独一无二的。
2.存储方式
- Map: 存储的是键值对,即每条记录都包含一个键和一个对应的值。
- Set: 只存储单个值,没有键值对的概念。
3.访问元素
- Map: 通过键来访问值,例如
map.get(key)。 - Set: 不能通过键访问,只能遍历整个集合来访问元素。
4.插入元素
- Map: 插入键值对,如果键已存在,则更新其对应的值。
- Set: 插入单个值,如果值已存在,则不会插入新值。
5.删除元素
- Map: 通过键删除键值对,例如
map.delete(key)。 - Set: 通过值删除元素,例如
set.delete(value)。
6.应用场景
- Map: 适用于需要通过唯一键快速查找对应值的场景,如缓存、配置管理等。
- Set: 适用于需要确保集合中元素唯一性的场景,如去重、集合运算等。
数据类型判断
如何准确判断数组数据类型
<body>
<script>
// 1.isArray
let a = [1, 2, 3]
let b = '1111'
let c = {}
console.log(Array.isArray(a));
// 2.instanceof
console.log(a instanceof Array);
// 3.Object.prototype.toString.call()
console.log(Object.prototype.toString.call(a) === '[object Array]');
</script>
</body>
对象浅拷贝、深拷贝
- 浅拷贝:只复制一层对象或者数组,对于嵌套的对象或者数组仅仅复制其引用
- 深拷贝:复制整个对象,确保新旧对象之间没有任何共享的引用数据类型
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>深拷贝与浅拷贝</title>
</head>
<body>
<script>
let obj1 = {
a: 1,
b: {
name: '张三'
}
}
// 1.浅拷贝
let obj2 = Object.assign({}, obj1)
obj2.b.name = '李四'
console.log('obj1', obj1);
console.log('obj2', obj2);
// 2.深拷贝
let obj3 = JSON.parse(JSON.stringify(obj1))
obj3.b.name = '李虎'
console.log('obj3', obj3);
</script>
</body>
</html>
深拷贝注意事项:
-
使用
JSON.parse(JSON.stringify(...))进行深拷贝有一些限制:- 它不能处理循环引用的对象。
- 它会忽略函数和原型链上的属性。
- 它无法正确拷贝
Date、RegExp等特殊类型的对象。
-
对于更复杂的情况,可以使用第三方库如 Lodash (
_.cloneDeep) 或者自己实现一个递归拷贝的函数。
this指向与改变this指向
为什么要有this指向
在this之前,js当中没有一个机制可以让对象中的函数去访问对象自己的属性,如下面这段代码所示:调用对象obj中的方法bar访问obj内部属性,直接打印myname会报错,倘若打印this.myname就不会。所以,为了让对象中的函数有能力访问对象自己的属性,我们需要this这样一个机制。
let obj = {
myname: 'qing',
bar: function(){ //在对象内部方法使用对象内部的属性
console.log(myname);
}
}
obj.bar()
this绑定规则
- 默认绑定:当一个函数独立调用,即不带任何修饰符的时候,函数在哪个词法作用域下生效,函数中的
this就指向哪里。通俗点说,只要是默认绑定,this就指向window(因为一个函数不带任何前缀被独立调用的时候,就说明它被声明在了全局中) - 隐式绑定:当函数的引用有上下文对象的时候(当函数被某个对象所拥有时),函数的
this指向引用它的对象 - 隐式丢失:当一个函数被多个对象链式调用时,函数的
this指向就近的那个对象 - 显式绑定:
callbindapply - new绑定:当使用构造函数创建对象时,构造函数内部的
this会绑定到新创建的对象实例上 - 箭头函数:使用箭头函数会让this指向其父级作用域
函数柯里化
什么是函数柯里化
所谓柯里化,简单来说就是把一个多参数的函数,转化为单参数的函数。
// 柯里化之前
function add(a, b, c) {
return a + b + c
}
add(1, 2, 3) // 6
// 柯里化之后
let addCurry = curry(add)
addCurry(1)(2)(3) // 6
示例
下面是一个使用 JavaScript 函数柯里化的示例代码:
// 柯里化函数写法
function add(x) {
return function(y) {
return x + y;
};
}
// 调用柯里化函数
const add5 = add(5);
console.log(add5(3)); // 8
console.log(add5(7)); // 12
在上面的示例代码中,add() 函数被柯里化为两个函数:一个是接收一个参数 y 的函数,返回 x + y;另一个是接收两个参数 x 和 y 的函数,返回 x + y。
柯里化函数的写法可以根据需要进行修改,比如可以定义多个参数的柯里化函数,也可以将参数进行优化等等。
优缺点分析
JavaScript 函数柯里化的优点是:
- 函数更加灵活和可重用。通过柯里化,可以将一个多参数的函数转换为一系列单参数的函数,使函数更加灵活和可重用。
- 可以避免重复的代码。通过柯里化,可以避免在调用函数时重复地传递参数,从而避免了重复的代码。
JavaScript 函数柯里化的缺点是:
- 可能会降低性能。通过柯里化,函数的性能可能会降低,因为需要额外的内存来存储函数的返回值和参数。
- 可能会增加代码复杂度。通过柯里化,可能会增加代码的复杂度,因为需要处理额外的参数和函数返回值。
手写实现
// 原始函数
function Fn_init (a, b, c) {
console.log('最终的结果:', a * b * c)
}
// 柯里化函数
function curryization (fn, params) {
// 获取函数参数长度
const lth = fn.length
params = params || []
console.log('params', params);
return function (...args) {
// 收集fn函数的参数
// newArgs = params.concat(Array.prototype.slice.call(arguments))
newArgs = params.concat(args)
console.log('newArgs', newArgs);
if (newArgs.length < lth) {
// 继续执行curryization柯里化函数,继续收集参数,this指向window
return curryization.call(this, fn, newArgs)
} else {
// 所有参数收集完毕,整体执行源函数,this指向window
return fn.apply(this, newArgs)
}
}
}
const curryFunc = curryization(Fn_init)
curryFunc(2)(3, 4) // 24
// curryFunc(2, 3)(4) // 24
// curryFunc(2, 3, 4) // 24
// curryFunc(2)(3)(4) // 24