Js常见手写题总结

间隔时间打印

0-9每间隔1s打印一个数字。

async/await

1
2
3
4
5
6
7
8
(async () => {
for (let i = 0; i < 10; ++i) {
await new Promise(resolve => {
console.log(i);
setTimeout(resolve, 1000);
});
}
})();

模拟递归调用

1
2
3
4
5
6
7
8
9
10
11
const print = (start, end) => {
if(start >= end) return void 0;
new Promise(resolve => {
setTimeout(resolve, 1000);
}).then(() => {
console.log(start);
print(start + 1, end);
})
}

print(0, 10);

构造链式调用

1
2
3
4
5
6
7
8
9
10
let promiseChain = Promise.resolve();

(() => {
for (let i = 0; i < 10; ++i) {
promiseChain = promiseChain.then(() => {
console.log(i);
return new Promise(resolve => setTimeout(resolve, 1000));
});
}
})();

不使用let

1
2
3
4
5
6
7
8
9
10
11
12
let promiseChain = Promise.resolve();

(() => {
for (var i = 0; i < 10; ++i) {
(i => {
promiseChain = promiseChain.then(() => {
console.log(i);
return new Promise(resolve => setTimeout(resolve, 1000));
});
})(i);
}
})();

Promise

实现Promise与then

实现Promise与then

实现Promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const promiseFactory = delay => new Promise(r => setTimeout(() => r(delay), delay));

const promiseAll = promises => {
return new Promise((resolve, reject) => {
const promiseStatus = new Array(promises.length).fill(false);
const result = new Array(promises.length).fill(null);
promises.forEach((item, index) => {
item.then(res => {
result[index] = res;
promiseStatus[index] = true;
if(promiseStatus.every(it => it)) resolve(result);
}).catch(err => {
result[index] = err;
reject(result);
})
})
})
}

promiseAll([
promiseFactory(1000),
promiseFactory(2000),
promiseFactory(3000),
]).then(console.log); // 三秒后输出 [1000, 2000, 3000]

排序

常见排序与复杂度

常见排序与复杂度

字典序排序

1
2
3
const arr = [123, 356, 2, 6, 999, 311];
arr.sort();
console.log(arr); // [123, 2, 311, 356, 6, 999]

最大的第K个数

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
const adjust = (index, arr) => {
let changeIndex = -1;
const n = arr.length;
if(index < n && arr[index] < arr[index * 2 + 1]){
changeIndex = index * 2 + 1;
[arr[index], arr[index * 2 + 1]] = [arr[index * 2 + 1], arr[index]];
}
if(index < n && arr[index] < arr[index * 2 + 2]){
changeIndex = index * 2 + 2;
[arr[index], arr[index * 2 + 2]] = [arr[index * 2 + 2], arr[index]];
}
if(changeIndex !== -1) adjust(changeIndex, arr);
}

const find = (arr, k) => {
for(let n = arr.length, i = n-1; i>=0; --i) adjust(i, arr);
for(let i=0; i<k-1; ++i){
arr.shift();
[arr[0], arr[arr.length - 1]] = [arr[arr.length - 1], arr[0]];
adjust(0, arr);
}
console.log(arr.shift());
}

const k = 3;
const arr = [4, 3, 5, 1, 6, 2, 7, 8];
find(arr, k);

防抖和节流

1
2
3
4
5
6
7
8
9
10
// 防抖
const d = function(time, funct, ...args){
let timer = null;
return () => {
clearTimeout(timer);
timer = null;
timer = setTimeout(() => funct(...args), time);
}
}
window.onscroll = d(1000, (a) => console.log(a), 1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 节流
const t = function(time, funct, ...args){
let timer = null;
return () => {
if(!timer){
funct(...args);
timer = setTimeout(() => {
clearTimeout(timer);
timer = null;
}, time);
}
}
}
window.onscroll = t(1000, (a) => console.log(a), 1);

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 寄生组合继承
function Parent(from){
this.name = "parent";
this.say = function(){
console.log(this.name);
}
this.from = from;
}
function Child(from){
Parent.call(this, from);
this.name = "child";
}
let f = function(){};
f.prototype = Parent.prototype;
Child.prototype = new f();
Child.prototype.construce = Child;

let child = new Child("child");
child.say(); // child
console.log(child.from); // child

十进制转二进制

计算

1
2
3
4
5
6
7
8
9
(function(num){
num = num >> 0;
const target = [];
while(num){
target.unshift(num % 2);
num = (num / 2) >> 0;
}
console.log(target.join(""));
})(10);

API

1
console.log(Number(10).toString(2));

带并发限制的异步调度器

Js实现一个带并发限制的异步调度器Scheduler,保证同时运行的任务最多有两个。

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
48
49
50
51
52
53
// Start
class Scheduler {
constructor() {
this.max = 2;
this.task = [];
this.pending = [];
}
add(fn){
const dispatch = () => {
for(let i=0; i<this.max; ++i){
if(!this.task[i] && this.pending.length){
const runFn = this.pending.shift();
this.task[i] = true;
runFn().then(() => {
this.task[i] = false;
runFn.resolve();
dispatch();
})
}
}
}
const pr = new Promise(resolve => {
fn.resolve = resolve;
this.pending.push(fn);
});
dispatch();
return pr;
}
}
// End

const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time);
})


const scheduler = new Scheduler();
const addTask = (time, order) => {
scheduler
.add(() => timeout(time))
.then(() => console.log(order));
}


addTask(2000, "1");
addTask(1000, "2");
addTask(3000, "3");
addTask(2000, "4");

// 2 第一秒
// 1 第二秒
// 3 第四秒
// 4 第四秒
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
48
49
50
51
52
// Start
class Scheduler {
constructor() {
this.max = 2;
this.task = [];
this.pending = [];
}
add(fn){
return new Promise(resolve => {
fn.resolve = resolve;
if(this.task.length < this.max) {
this.dispatch(fn);
} else {
this.pending.push(fn);
}
})
}
dispatch(pr){
this.task.push(pr);
pr().then(() => {
pr.resolve();
this.task.splice(this.task.indexOf(pr), 1);
if(this.pending.length) {
this.dispatch(this.pending.shift());
}
})
}
}
// End

const timeout = (time) => new Promise(resolve => {
setTimeout(resolve, time);
})


const scheduler = new Scheduler();
const addTask = (time, order) => {
scheduler
.add(() => timeout(time))
.then(() => console.log(order));
}


addTask(2000, "1");
addTask(1000, "2");
addTask(3000, "3");
addTask(2000, "4");

// 2 第一秒
// 1 第二秒
// 3 第四秒
// 4 第四秒

拍平数组

迭代式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const arr = [1, [2, 3], [[[4, 5, 6]], [7, 8, 9]]];

const flat = (arr) => {
let i = 0;
const newArray = [...arr];
while(i < newArray.length){
if(Array.isArray(newArray[i])){
const tmp = newArray[i];
newArray.splice(i, 1, ...tmp);
}else{
++i;
}
}
return newArray;
}

console.log(flat(arr));

递归式

1
2
3
4
5
6
7
8
9
10
11
12
const arr = [1, [2, 3], [[[4, 5, 6]], [7, 8, 9]]];

const flat = (arr) => {
const newArray = [];
arr.forEach(item => {
if(Array.isArray(item)) newArray.push(...flat(item));
else newArray.push(item);
})
return newArray;
}

console.log(flat(arr));

手写apply、call、bind

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
Function.prototype._apply = function(base, args){
base.fn = this;
const result = base.fn(...args);
delete base.fn;
return result;
}

Function.prototype._call = function(base, ...args){
return this._apply(base, args);
}

Function.prototype._bind = function(base, ...args){
const functionArgsLength = this.length;
const curArgsLength = args.length;
if(curArgsLength >= functionArgsLength){
return this._apply(base, args);
}else{
return (...newArgs) => {
return this._bind(base, ...args, ...newArgs);
}
}
}

function fn(num1, num2){
console.log(this.num, num1, num2);
}

const obj = { num: 1 }

fn._apply(obj, [2, 3]);
fn._call(obj, 4, 5);
fn._bind(obj)(6)(7);

new操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Student(name){
this.name = name;
}

Student.prototype.say = function(){
console.log("I'm", this.name);
}

const _new = (fn, ...args) => {
const obj = Object.create(fn.prototype);
const res = fn.apply(obj, args);

if ((typeof res === "object" && res !== null) || typeof res === "function") {
return res;
}

return obj;
}

const student = _new(Student, "A");
console.log(student);
student.say();

数组去重

Set

1
2
3
const arr = [1, 2, 3, 1, 1, 1, 3, 5, 3];
const newArr = Array.from(new Set(arr)); // [...(new Set(arr))]
console.log(newArr); // [1, 2, 3, 5]

indexOf

1
2
3
4
5
6
7
// 使用find、findIndex、includes思路相同
const arr = [1, 2, 3, 1, 1, 1, 3, 5, 3];
const newArr = [];
arr.forEach((v) => {
if(newArr.indexOf(v) === -1) newArr.push(v);
})
console.log(newArr); // [1, 2, 3, 5]

利用对象

1
2
3
4
5
6
const arr = [1, 2, 3, 1, 1, 1, 3, 5, 3];
const obj = {};
arr.forEach((v) => {
if(!obj[v]) obj[v] = 1;
})
console.log(Object.keys(obj).map(item => Number(item))); // [1, 2, 3, 5]

串行执行Promise

reduce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const arr = [
() => Promise.resolve(1),
res => Promise.resolve(res * 2),
res => Promise.resolve(res * 2),
res => Promise.resolve(res * 3),
res => new Promise(r => setTimeout(() => r(res), 1000))
];


arr.reduce((pre, cur) => {
return pre.then(res => cur(res));
}, Promise.resolve()).then(res => {
console.log(res);
});

async/await

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const arr = [
() => Promise.resolve(1),
res => Promise.resolve(res * 2),
res => Promise.resolve(res * 2),
res => Promise.resolve(res * 3),
res => new Promise(r => setTimeout(() => r(res), 1000))
];

(async () => {
let preResult = null;
for (let i = 0; i < arr.length; i++) {
const fn = arr[i];
preResult = await fn(preResult);
console.log(preResult);
}
})();

远程相加

假如现在本地无法实现加法功能,现有其他团队提供的远程Api可以实现两个数相加的功能,现需要改进Api使其能够实现多个数相加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const addRemote = (a, b) => {
return new Promise(r => setTimeout(r, 1000, a + b));
}

const add = (...args) => {
const cur = [].concat(args);
return new Promise(r => {
if(cur.length % 2 === 1) cur.push(0);
const batchAdd = [];
for(let i=0; i<cur.length; i += 2){
batchAdd.push(addRemote(cur[i], cur[i+1]));
}
Promise.all(batchAdd).then(res => {
if(res.length === 1){
r(res.pop());
}else{
add(...res).then(r);
}
})
})
}

add(1, 2, 4, 5, 6).then(res => console.log(res));