深拷贝

在 JavaScript 中,深拷贝是指创建一个新的对象,并将原始对象的所有属性和嵌套对象的属性都复制到新对象中,确保原始对象和新对象之间没有引用关系。这样做可以避免在修改新对象时影响到原始对象。

以下是几种常见的 JavaScript 深拷贝方法:

手动递归复制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deepCopy(obj) {
if (typeof obj !== "object" || obj === null) {
return obj;
}

let copy = Array.isArray(obj) ? [] : {};

for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}

return copy;
}

使用 JSON 序列化和反序列化:

1
let copy = JSON.parse(JSON.stringify(obj));

这种方法可以将对象转换为字符串,然后再将字符串转换回对象,从而实现深拷贝。但是需要注意的是,它无法复制函数和特殊对象(如正则表达式、Date 对象等),并且会忽略对象的原型链。

使用第三方库(如 Lodash):
第三方库 Lodash 提供了 cloneDeep 方法,可以方便地进行深拷贝:

1
2
const _ = require("lodash");
let copy = _.cloneDeep(obj);

这种方法可以处理各种类型的对象,并且具有良好的性能和兼容性。

需要注意的是,深拷贝可能会导致循环引用的问题,即某个对象的属性引用了自身或者形成了循环引用链。在进行深拷贝时,需要确保处理循环引用的情况,否则可能会导致无限递归的问题。

选择合适的深拷贝方法取决于你的具体需求和使用场景。如果你只需要简单的数据结构拷贝,手动递归复制或使用 JSON 序列化和反序列化可能已经足够。如果需要处理复杂的对象和循环引用,可以考虑使用第三方库来进行深拷贝。

防抖

防抖(debouncing)是一种常用的前端技术,用于限制某个函数在短时间内被频繁触发的情况。当一个事件被触发时,防抖会延迟执行该事件的处理函数,如果在延迟期间内该事件再次被触发,那么延迟时间会被重新计算。这样可以有效地减少函数的执行次数,提升性能。

下面是一个简单的 JavaScript 示例代码,演示了如何实现防抖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function debounce(func, delay) {
let timerId;

return function () {
const context = this;
const args = arguments;

clearTimeout(timerId);
timerId = setTimeout(function () {
func.apply(context, args);
}, delay);
};
}

// 使用示例
function handleInput() {
console.log("处理输入...");
}

const debouncedHandleInput = debounce(handleInput, 300);

// 监听输入事件
document
.getElementById("input")
.addEventListener("input", debouncedHandleInput);

在上面的示例中,debounce 函数接受两个参数:func 是要执行的函数,delay 是延迟的时间间隔(以毫秒为单位)。它返回一个新的函数,这个函数会在延迟期间内被多次调用时,只执行最后一次调用。

你可以根据自己的需求调整延迟时间,以及要执行的函数。在示例中,我们将 handleInput 函数包装在 debounce 中,并将返回的函数 debouncedHandleInput 用作输入事件的处理函数。这样,当用户输入时,handleInput 函数不会立即执行,而是在用户停止输入一段时间后才会执行。

节流

节流(throttling)是另一种常用的前端技术,用于限制某个函数在一定时间间隔内被触发的频率。与防抖不同,节流会在固定的时间间隔内执行函数,而不管该函数是否被频繁触发。

下面是一个简单的 JavaScript 示例代码,演示了如何实现节流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function throttle(func, delay) {
let timerId;
let lastExecutedTime = 0;

return function () {
const context = this;
const args = arguments;
const currentTime = Date.now();

if (currentTime - lastExecutedTime >= delay) {
func.apply(context, args);
lastExecutedTime = currentTime;
} else {
clearTimeout(timerId);
timerId = setTimeout(function () {
func.apply(context, args);
lastExecutedTime = Date.now();
}, delay);
}
};
}

// 使用示例
function handleScroll() {
console.log("处理滚动...");
}

const throttledHandleScroll = throttle(handleScroll, 300);

// 监听滚动事件
window.addEventListener("scroll", throttledHandleScroll);

在上面的示例中,throttle 函数接受两个参数:func 是要执行的函数,delay 是时间间隔(以毫秒为单位)。它返回一个新的函数,这个函数会在固定的时间间隔内被调用,无论被调用的次数。

在示例中,我们将 handleScroll 函数包装在 throttle 中,并将返回的函数 throttledHandleScroll 用作滚动事件的处理函数。这样,无论用户多快地滚动页面,handleScroll 函数都会以固定的时间间隔被执行。

你可以根据自己的需求调整时间间隔,以及要执行的函数。请注意,节流会在固定的时间间隔内执行函数,而不会像防抖那样等待用户停止触发事件。

转置矩阵

要转置一个矩阵,可以将矩阵的行变为列,列变为行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function transposeMatrix(matrix) {
// 获取矩阵的行数和列数
const rows = matrix.length;
const cols = matrix[0].length;
// 创建一个新的矩阵来存储转置后的结果
const transposedMatrix = [];
for (let i = 0; i < cols; i++) {
transposedMatrix[i] = [];
for (let j = 0; j < rows; j++) {
// 将原矩阵的行变为列
transposedMatrix[i][j] = matrix[j][i];
}
}
return transposedMatrix;
}
// 示例矩阵
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// 转置矩阵
const transposed = transposeMatrix(matrix);
console.log(transposed);
[
[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
];

矩阵列匹配

有问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let arr = [
[1, 2], // 匹配选择1
["a", "b"], // 匹配选择2
];
let list = [];
let r_list = [];

const fn = ($arr, $i) => {
for (let i = 0; i < $arr[$i].length; i++) {
r_list[$i] = $arr[$i][i];
if ($i !== $arr.length - 1) {
fn($arr, $i + 1);
} else {
list.push(r_list.slice());
}
}
};

fn(arr, 0);
console.log(list);
// [
// [1, "a"],
// [1, "b"],
// [2, "a"],
// [2, "b"]
// ]

矩阵列生成所有组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function matrixColumnMatch(matrix) {
let results = [];

function backtrack(currentCombination, depth) {
// 如果遍历到矩阵的最后一列,则保存当前组合
if (depth === matrix.length) {
// console.log(currentCombination);
results.push([...currentCombination]); // 深拷贝当前组合
return;
}
// console.log(currentCombination)
for (let i = 0; i < matrix[depth].length; i++) {
currentCombination.push(matrix[depth][i]); // 添加当前列的元素
// console.log(currentCombination);
backtrack(currentCombination, depth + 1); // 递归处理下一列
currentCombination.pop(); // 回溯,撤销当前列的元素,尝试当前列的其他组合
// console.log(currentCombination);
}
}

backtrack([], 0); // 从空的组合开始,深度为 0
return results;
}

// 示例矩阵
const matrix = [
[1, 2], // 第一列
["a", "b"], // 第二列
["X", "Y"], // 第三列
];

// 调用函数
const combinations = matrixColumnMatch(matrix);
console.log(combinations);