秋招复习计划-手写代码2

  |  

前言

深拷贝的几种实现,async实现原理,对象的深度优先遍历并执行回调函数


64



深拷贝的实现

JSON对象实现

1
2
3
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}

JSON对象进行深拷贝,代码简单高效,但是也有部分缺陷

  • 如果obj里面有RegExp、Error对象,则序列化的结果将只得到空对象
  • 如果obj里面有时间对象,则JSON。stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象
  • 如果obj里面有函数、undefined,则序列化的结果会把函数或undefiend丢失
  • 如果obj里面有NaNcy、Infinity和-Infinity,则序列化的结果会编程null
  • 无法处理function,无法处理循环引用对象

Object.assign函数实现

Object.assign方法用于将所有可枚举对象的值从一个对象或多个源对象复制到目标对象,并返回目标对象

1
2
3
function deepClone(obj) {
return Object.assign({}, obj);
}

如果对象中只有一级属性,没有二级属性,这个方法可以很好的实现深拷贝,但是如果对象有属性也是对象,那么二级属性以后就是浅拷贝了

递归实现

遍历对象的所有属性,基本类型值直接赋值,如果是引用类型,那么就递归调用,传入引用类型的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function deepClone(obj) {
let res;
if(obj && Array.isArray(obj)) {
res = [];
obj.forEach((item) => {
res.push(deepClone(item));
});
} else if(typeof val === 'object' && val !== null) {
res = {}
for(let key in obj) {
res[key] = deepClone(obj[key]);
}
} else {
res = obj;
}
return res
}

async函数的原理

要实现以此读取两个文件的操作

1
2
3
4
5
6
let asyncReadFile = async function() {
let f1 = await readFile('./exmaple1.txt', 'utf-8');
let f2 = await readFile('./example2.txt', 'utf-8');
console.log(f1);
console.log(f2);
}

async函数本质上就是将生成器函数和自动执行器包装在一个spawn函数里

1
2
3
4
5
async function fn(args) {}
// 等同于
function fn(args) {
return spawn(function *() {})
}

而spawn函数的实现是这样的

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
35
36
37
38
39
40
41
42
43
44
45
46
47
var fs = require('fs');
function spawn(genF) {
// 返回一个Promise
return new Promise(function(resolve, reject) {
// 执行一个生成器,得到迭代器
var gen = genF();
// 自动执行器
function step(nextF) {
try {
// 执行next函数
var next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value)
}
Promise.
resolve(next.value).
then(
function(v) {
step(() => { return gen.next(v); });
},
function(e) {
step(() => { return gen.throw(e); });
}
);
}
step(() => { return gen.next(undefined); });
});
}
var readFile = function (fileName, options) {
return new Promise(function (resolve, reject){
fs.readFile(fileName, options, function(error, data){
if (error) return reject(error);
resolve(data);
});
});
};

var gen = function* () {
var f1 = yield readFile('./apple.txt', 'utf8');
console.log(f1);
var f2 = yield readFile('./orange.txt', 'utf8');
console.log(f2);
};
spawn(gen);

对象的深度优先遍历并执行回调函数

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
35
36
37
38
39
40
41
42
43
44
45
46
47
请使用 JavaScript 编写一个树的深度优先遍历函数(节点最深的最先访问到,依次类推),满足以下测试用例:

// 假设树的结构如下
const tree = [
{
id: 1,
name: '张三',
children: [
{
id: 2,
name: '李四',
children: [
{
id: 5,
name: '张五'
}
]
}
]
},
{
id: 6,
name: '玛丽'
}
]


// 测试用例:

// 1. 生成一颗新树,将所有节点的id,加1
console.log(treeMap(tree, node => {
let newNode = { ...node }
newNode.id = node.id + 1
return newNode
}))
// 打印的新树,应该与tree的结构一致,只是每个id自增1,老的tree,应该没有任何改动

// 2. 打印每个节点的id
treeMap(tree, node => {
console.log(node.id)
return node
});
// 应打印顺序应该是: 5,2,1,6

// 3. 对于非法输入,应直接返回第一个入参
console.log(treeMap(null)) // 输出null
console.log(treeMap(tree, true/*不是函数*/)) //输出tree

是我把这道题想复杂了,并没有抓住,树是一个数组,树的children也是一个数组。我们只要写一个递归函数,遍历数组,数组中都是对象,如果对象有children属性,则递归调用。然后将节点传入函数执行,因为如果对象有children属性,就会优先去遍历children属性,结束以后才会执行函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function treeMap(tree, func) {
if(!Array.isArray(tree) || tree.length === 0) {
return tree;
}
if(Object.prototype.toString.call(func) !== "[object Function]") {
return tree;
}
function dfs(arr) {
let newChild = [];
for(let item of arr) {
let obj = JSON.parse(JSON.stringify(item));
if(item.hasOwnProperty("children")) {
obj.children = dfs(item.children);
}
newChild.push(func(obj));
}
return newChild;
}
return dfs(tree);
}
文章目录
  1. 1. 深拷贝的实现
    1. 1.1. JSON对象实现
    2. 1.2. Object.assign函数实现
    3. 1.3. 递归实现
  2. 2. async函数的原理
  3. 3. 对象的深度优先遍历并执行回调函数