书写一个数组去重的方法

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
54
55
56
57
// 利用ES6 Set数据结构去重
function deduplication(array) {
return Array.from(new Set(array));
// return [...new Set(array)];
}

// 遍历一遍 利用对象的属性不能相同的特点去重
function deduplication(array) {
let obj = {}, result = [];
for(let i = 0; i< array.length; i++){
if(!obj[array[i]]){ //如果能查找到,证明数组元素重复了
obj[array[i]] = 1;
result.push(array[i]);
}
}
return result;
};

// 双层遍历,内层遍历改用indexOf
function deduplication(array) {
let result = [];
array.forEach(function(item, index ,arr){ //这里利用map,filter方法也可以实现外层遍历
if(arr.indexOf(item, index + 1) < 0) {
result.push(item);
}
});
return result;
};

// 双层遍历, 遇到重复则跳过,不重复的插入到新数组
function deduplication(array){
var result = [];
for(let i = 0; i < array.length; i++){
for(let j = i + 1; j < array.length; j++){
if(array[i] === array[j]){
j = ++i;
}
}
result.push(array[i]);
}
return result;
}

// 双层遍历,删除重复,返回原数组 *对原数组有修改
function deduplication(array){
let len = array.length;
for(let i = 0; i < len; i++){
for(let j = i + 1; j < len; j++){
if(array[i] == array[j]){
array.splice(j, 1);
len--;
j--;
}
}
}
return array;
};

书写将数组打乱顺序的方法

1
2
3
4
5
6
// 利用 Array.prototype.sort 对数组排序,排序比较函数随机返回
Array.prototype.disorder = function() {
return this.sort((a, b) => {
return Math.random() > .5 ? 1 : -1;
});
}
1
2
3
4
5
6
7
8
9
10
// 洗牌算法,遍历一遍与随机位置交换
Array.prototype.shuffle = function() {
for(var j, x, i = this.length - 1; i; i--) {
j = parseInt(Math.random() * i);
x = this[i];
this[i] = this[j];
this[j] = x;
}
return this;
};

书写一个方法将一个字符串倒序输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 利用 String.prototype.split 拆分字符,利用 Array.prototype.reverse 逆序,利用 Array.prototype.join 合并
String.prototype.reverse = function() {
return this.split('').reverse().join('');
}

// 利用 Array.prototype.reduceRight 从后向前遍历
String.prototype.reverse = function() {
return Array.prototype.reduceRight.call(this, function(result, char) {
return result += char;
}, '');
}

// for 循环从后向前遍历
String.prototype.reverse = function() {
var result = '';
for (var i = this.length - 1; i >=0; i--) {
result += this.charAt(i);
}
return result;
}

书写一个方法使用reduce模拟map/使用map来模拟reduce

使用reduce模拟map

1
2
3
4
5
6
7
8
9
10
11
Array.prototype._map = function(fn, callbackThis) {
// 定义回调函数的执行环境
// call第一个参数传入null,则 this 指向全局对象,同 map 的规则
let CBThis = callbackThis || null;
return this.reduce((res, ele, idx, arr) => {
// 传入map回调函数拥有的参数
// 把每一项的执行结果push进res中
res.push(fn.call(CBThis, ele, idx, arr));
return res;
}, []);
};

解析url,将url中的参数转化为对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getQueryString(name) { 
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]); return null;
}
function GetRequest() {
var url = location.search; //获取url中"?"符后的字串
var theRequest = new Object();
if (url.indexOf("?") != -1) {
var str = url.substr(1);
strs = str.split("&");
for(var i = 0; i < strs.length; i ++) {
theRequest[strs[i].split("=")[0]] = unescape(strs[i].split("=")[1]);
}
}
return theRequest;
}

循环体中不含有break语句时使用while循环来模拟for循环

for循环:

1
for(initialize; test; increment)body;

while循环:

1
2
3
4
5
initialize;
while (test) {
try { body;}
finally { increment;}
}

试写出 bind() 的 polyfill 方法

MDN上的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Does not work with `new funcA.bind(thisArg, args)`
if (!Function.prototype.bind) (function(){
var slice = Array.prototype.slice;
Function.prototype.bind = function() {
var thatFunc = this, thatArg = arguments[0];
var args = slice.call(arguments, 1);
if (typeof thatFunc !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - ' +
'what is trying to be bound is not callable');
}
return function(){
var funcArgs = args.concat(slice.call(arguments))
return thatFunc.apply(thatArg, funcArgs);
};
};
})();

实现 emit/on 观察者模式 发布/订阅模式

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
var Event = (function(){
var list = {},
on,
emit,
remove;
on = function(key,fn){ //监听事件函数
if(!list[key]){
list[key] = []; //如果事件列表中还没有key值命名空间,创建
}
list[key].push(fn); //将回调函数推入对象的“键”对应的“值”回调数组
};
emit = function(){ //触发事件函数
var key = Array.prototype.shift.call(arguments); //第一个参数指定“键”
msg = list[key];
if(!msg || msg.length === 0){
return false; //如果回调数组不存在或为空则返回false
}
for(var i = 0; i < msg.length; i++){
msg[i].apply(this, arguments); //循环回调数组执行回调函数
}
};
remove = function(key, fn){ //移除事件函数
var msg = list[key];
if(!msg){
return false; //事件不存在直接返回false
}
if(!fn){
delete list[key]; //如果没有后续参数,则删除整个回调数组
}else{
for(var i = 0; i < msg.length; i++){
if(fn === msg[i]){
msg.splice(i, 1); //删除特定回调数组中的回调函数
}
}
}
};
return {
on: on,
emit: emit,
remove: remove
}
})();
var fn = function(data){
console.log(data + '的推送消息:xxxxxx......');
}
Event.on('action', fn);
Event.emit('action', '2016.11.26');
Event.remove('action');

对象object深拷贝

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
/**
* 是否是对象
*
* @param {Object | Array} obj 需要判断的数据
* @return {boolean} 是否是对象
*/
export function isObject(obj) {
return (typeof obj === 'object') && (obj !== null);
}

/**
* 深拷贝
*
* @param {Object | Array} source 需要拷贝的数据
*
* @return {{} | []} 深拷贝后的数据
*/
export function deepClone(source) {
// 非对象返回自身
if (!isObject(source)) {
return source;
}
let target = Array.isArray(source) ? [] : {};
Object.entries(source).forEach(([key, val]) => {
target[key] = deepClone(val);
});
return target;
}

判断两个对象内容是否一样

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
/**
* 判断两个对象内容(值)是否相等
*
* @param {Object | Array} a 对象a
* @param {Object | Array} b 对象b
*
* @return {boolean} true/false
*/
export function isObjectValueEqual(a, b) {
// 判断不是对象
if (!isObject(a) || !isObject(b)) {
return a === b;
}
// 键值长度相等
if (Object.keys(a).length !== Object.keys(b).length) {
return false;
}
let target = true;
// 逐个值判断
Object
.entries(a)
.forEach(([key, val]) => {
if (!isObjectValueEqual(a[key], b[key])) {
target = false;
return;
}
});
return target;
}

如下代码,分别弹出什么信息?

1
2
3
4
5
6
7
8
9
var a = 100;
function create() {
var a = 200;
return function () {
console.log(a)
}
}
var fn = create()
fn();
1
2
3
4
5
6
7
8
9
var a = 100;
function invoke(fn) {
var a = 200;
fn();
}
function fn() {
console.log(a);
}
invoke(fn)

返回 200 100
函数的执行依赖于变量作用域,这个作用域是函数定义时决定的。
第一段代码中 fn 实际是 create() 函数返回的函数,这个函数中使用的 a 是 create() 函数中定义的 a 变量,它覆盖了外部定义的 a。
第二段代码中 fn 在 invoke 函数外部定义,这个函数中的 a 是外部定义的 a,所以即使通过 invoke 函数包了一层,还是会返回外部定义的 a。
第二段代码也可以通过将 a 作为 fn 的 入参来使用,这样入参 a 就可以覆盖外部的 a。

1
2
3
4
5
6
7
8
9
var a = 100;
function invoke(fn) {
var a = 200;
fn(a);
}
function fn(a) {
console.log(a);
}
invoke(fn)

以上代码返回 200

现有瀑布流式页面(页面下拉式无限加载图片),用js监听每个图片的点击事件

采用事件委托的方式实现

执行如下代码,然后点击每个 a 标签分别弹出什么信息?并写明原因

1
2
3
4
5
6
7
8
9
10
let $body = $('body')
let arr = [1,2,3,4,5]
let i,length = arr.length, $a
for(i=0; i<length; i++) {
$a = $(`<a>${i}</a>`)
$body.append($a)
$a.click(function() {
alert(i)
})
}

弹出5,因为click绑定了的方法弹出i,i循环结束时为5.若想输出 0 1 2 3 4,可将绑定方法改为自执行函数形成闭包。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
let $body = $('body')
let arr = [1,2,3,4,5]
let i,length = arr.length, $a
for(i=0; i<length; i++) {
$a = $(`<a>${i}</a>`)
$body.append($a);
(function(i) {
$a.click(function() {
alert(i)
})
})(i)
}

执行下面代码会输出什么信息?

1
2
3
4
5
6
7
8
9
10
11
12
const obj = {
a: 100
}
const obj1 = obj;
let a1 = obj.a;

obj1.a = 200;
console.log(obj.a);
console.log(a1);
a1= 300;
console.log(obj.a);
console.log(obj1.a)

200
100
200
200

执行如下代码会输出什么信息?

1
2
3
4
5
6
7
8
setTimeout(function() {
console.log(100)
})

console.log(200)
Promise.resolve().then(function(){
console.log(300)
});

200
300
100

同步代码最先执行
Promise 属于 microTask 第二个执行
setTimeout 属于 macroTask 最后执行

执行如下代码,分别打印出什么?

1
2
3
123 instanceof Number
new Number(123) instanceof Number
Number(123) isntanceof Number

false
ture
false

第一个 首先左侧为number类型,并不是一个对象,更不是由 Number 实例化出来的(基本包装类型),所以为false
第二个 左侧使用 Number 构造实例化对象 右侧为 Number 构造 ,所以为true
第三个 左侧没有使用 new 所以并不是使用构造函数实例化 而是使用 Number 这个函数返回了一个数字, 所以为false

写一个方法返回入参类型

number string boolean null undefined object symbol(ES6) bigint(ES2020)

如果只需要返回基本数据类型:

1
2
3
function typeOf(value) {
return value === null ? 'null' : (typeof value === 'function' ? 'object' : typeof value);
}

如果需要返回类型:

1
2
3
function typeOf(param) {
return Object.prototype.toString.call(param).splice(8, -1);
}

写一个方法完成如下输入输出

输入为一个二维数组,每一项都是由两个 string 组成的数组,将输入参数的每一项与其他项相拼接,返回扁平化的数组。
输入: [[‘a’, ‘b’], [‘n’, ‘m’], [‘0’, ‘1’]]
输出:[‘an0’, ‘an1’, ‘am0’, ‘am1’, ‘bn0’, ‘bn1’, ‘bm0’, ‘bm1’]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let input = [['a', 'b'], ['n', 'm'], ['0', '1']];
function flatArr(param) {
let result = [''];
param.forEach(element => {
// 采用如下注释的方法输出参数顺序和给出的不同
// result = [result.map(item => item + element[0]),
// result.map(item => item + element[1])].flat();
result = result.reduce((res, ele) => {
res.push(ele + element[0], ele + element[1]);
return res;
}, []);
});
console.log(result);
return result;
}
flatArr(input);

写一个方法按照层级将对象进行扁平化处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 输入
var object = {
a: {
b: {
c: {
d: 'e'
}
}
},
f: {
a: {
x: null
}
},
i: ['a', 'v'],
k: {
l: 'm'
}
};
// 输出
[['a', 'f', 'i', 'k'], ['b', 'a', 'l'], ['c', 'x'], ['d']]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let result = [];
function isObj(obj) {
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
}
function flatObj(obj, n) {
if (!isObj(obj)) {
return;
}
for (let key in obj) {
if(!result[n]) {
returl[n] = [];
}
result[n].push(key);
flatObj(obj[key], n + 1);
}
}
flatObj(object, 0);
console.log(result);

实现 sleep 方法

1
2
3
4
5
6
7
// 使用 setTimeout 回调
function sleep(time, callback) {
setTimeout(callback, time);
}
sleep(10000, () => {
// do something
});
1
2
3
4
5
// 不使用 setTimeout
function sleep(time) {
let _start = new Date().getTime();
while(new Date().getTime() - _start < time);
}

写一个方法进行对输入参数进行累加,入参多于5个时输出

1
2
3
// add(1,2,3,4,5) -> 15
// add(1)(2)(3)(4)(5) -> 15
// add(1,2,3)(4,5,6) -> 15 or 21

使用全局变量和递归

1
2
3
4
5
6
7
8
9
10
let count = 0;
let result = 0;
function add() {
Array.prototype.forEach.call(arguments, item => {
result += item;
count += 1;
});
return count < 5 ? add : result;
}
console.log(add(1,2,3)(4,5,6));

使用闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add() {
let count = 0;
let result = 0;
function append() {
count += arguments.length;
result += +eval(Array.prototype.join.call(arguments, '+'));
// Array.prototype.forEach.call(arguments, item => {
// count ++;
// result += item;
// });
return count < 5 ? append : result;
}
return append(...arguments);
}
console.log(add(1)(2)(3)(4)(5));

连续正整数数组,去掉中间一个数字之后乱序,求去掉的数字

使用排序和循环遍历 时间复杂度为 O(nlogn + n) = O(nlogn)

1
2
3
4
5
6
7
8
9
10
11
// [2,4,6,8,7,3] -> 5
let input = [2,4,6,8,7,3];
function find(param) {
let result = param.sort();
result.forEach((element, idx) => {
if(element + 1 !== result[idx + 1] && idx !== result.length -1) {
return element + 1;
}
})
}
console.log(find(input));

利用数组下标 时间复杂度为 O(2n) = O(n)

1
2
3
4
5
6
7
8
9
10
11
12
let input = [2,4,6,8,7,3];
function find(param) {
let tmp = [];
param.forEach(item => {
tmp[item] = 1;
});
for(let i = tmp.length - 1; i >= 0; i--) {
if(!tmp[i])
return i;
}
}
console.log(find(input));

如果连续正整数从1开始(或者知道起始位置),可以从数组的和来获知

只需要遍历一次,时间复杂度为 O(n)

1
2
3
4
5
function find(param) {
let n = param.length + 1;
return n * (n + 1) / 2 - param.reduce((result, item) => {return result + item;}, 0);
}
console.log(find(1,2,4,6,8,7,3));

合并两个长度很大的数组

  1. 使用 concat
1
var x = a.concat(b);

concat 方法连接a、b两个数组后,a、b 两个数组的数据不变,同时会返回一个新的数组。如果 a、b 合并后不再使用,因为 a、b 数据量大会该方法会占用较多内存。

  1. 使用循环 + push

遍历其中一个数组,把该数组中的所有元素依次添加到另外一个数组中

1
2
3
b.forEach(item => {
a.push(item);
});

在循环之前可以先比较下 a、b 的长度,对短的进行循环,减少遍历插入次数。

  1. 使用 push.apply

Function.prototype.apply 的第二个参数是数组,利用该特性,可以简化循环写法

1
a.push.apply(a, b);

取得当前正在执行的function的名字

可以从 arguments.callee 中获取到

如果支持 name

1
2
3
function fn() {
let funcName = arguments.callee.name; // 通过 arguments.callee.name 即可获取到当前 function 的名字
}

否则可以通过正则匹配

1
2
3
4
5
6
7
function getFuncName(fn){
return (/^[\s\(]*function(?:\s+([\w$_][\w\d$_]*))?\(/).exec(fn.toString())[1] || '';
}
function test(){
getFuncName(arguments.callee); // 通过对 arguments.callee 进行正则匹配
}

但是严格模式中无法获取callee

也可以从 Error 的错误堆栈中获取当前 function 名字

1
2
3
4
function getExecFunctionName() {
let names = new Error().stack.match(/at (.*?) /g);
return names[1].replace('at ', '').trim();
}

以下代码的输出顺序

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
console.log('a');
async function async1() {
await async2();
console.log('b');
}

async function async2() {
console.log('c');
}

async1();

setTimeout(() => {
console.log('d');
}, 0);

Promise.resolve().then(() => {
console.log('i');
}).then(()=>{
console.log('j');
});

new Promise((resolve) => {
console.log('e');
resolve();
}).then(() => {
console.log('f');
}).then(() => {
console.log('g');
});

console.log('h');

同步 a c e h /异步microTask b i f j g / 异步macroTask d

输出一个字符串中不重复的连续字符串的最大长度

例如 abcddbca ,其中符合的字符串为 abcd 和 dbca, 所以需要输出 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

function getNum(str) {
let maxLen = 0;
const strArr = str.split('');
strArr.forEach((char, idx) => {
let tmpArr = [char];
for(let i = idx + 1; i < strArr.length; i++) {
if (!tmpArr.includes(strArr[i])) {
tmpArr.push(strArr[i]);
} else {
break;
}
}
if (tmpArr.length > maxLen) {
maxLen = tmpArr.length;
}
});
return maxLen;
}

判断一个字符串是否是回文字符串

需要将字符串先将大写转换成小写,去除符号和数字,如果从前往后和从后往前都是同样的,则是回文字符串。

例如 001ABCD,dcba.是回文字符串

1
2
3
4
5
6
7
8
function isRightStr(str) {
let str1 = str.toLowerCase().replace(/[0-9\s,.]+/g, '');
for(let i = 0; i < Math.floor(str1.length / 2) + 1; i++) {
if (str1[i] !== str1[str1.length - i - 1])
return false;
}
return true;
}

点击inner元素输出结果

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
<div class="outer">
<div class="inner"></div>
</div>
<style>
.outer {
width: 200px;
height: 200px;
background: red;
}
.inner {
width: 100px;
height: 100px;
background: green;
}
</style>
<script>
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');
// Let's listen for attribute changes on outer element
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true,
});

// Here's a click listener...
function onClick(e) {
console.log('click');
setTimeout(function() {
console.log('timeout');
}, 0);

Promise.resolve().then(function() {
console.log('promise');
});

outer.setAttribute('data-random', Math.random());
}
// ...which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
</script>

结果:
click
promise
mutate
click
promise
mutate
timeout
timeout

步骤:

  1. 点击 inner 触发 inner 元素的 onClick 入栈 macroTask
  2. 触发 outer 元素的 onclick 入栈 macroTask
    —- microTask 为空,执行 macroTask
  3. 执行 onClick 方法中的 console.log('click') 打印 click
  4. setTimeout() 中的 console.log('timeout') 入栈 macroTask
  5. Promise.resolve().then() 中的 console.log('promise') 入栈 microTask
  6. outer.setAttribute('data-random', Math.random()); 变更了 outer 的 attritube 触发了 MutationObserver(), 导致 console.log('mutate')入栈 microTask
    —- 执行 microTask
  7. 打印 promise, 打印 mutate
    —- microTask 为空,执行 macroTask
  8. 执行 onClick, 按照 3-7 执行一遍
    —- 执行 macroTask
  9. 打印 timeout
    —- 执行 macroTask
  10. 打印 timeout

限制并发数量的多请求执行

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// options 是形如 {url: '', method: 'GET', params: {}} 的请求相关参数对象
function request(options) {
return new Promise((resolve, reject) => {
console.log(`${options} is start`)
setTimeout(() => {
console.log(`${options} is finished`)
resolve({ data: options });
}, 3000);
});
}

function fetch(options) => {
request(options).then((res) => {
// do something
}).catch((err) => {
// do something
});
}

// 多请求并行执行
function parallelRequest(optionsList) {
return Promise.all(optionsList.map(options => request(options)));
}
parallelRequest([1,2,3]).then(data => {
console.log(data);
});

// 多请求串行执行
async function serialRequest(optionsList) {
let res = [];
for (let i = 0; i < optionsList.length; i++) {
let result = await request(optionsList[i]);
res.push(result);
}
return Promise.resolve(res);
}
serialRequest([1,2,3]).then(data => {
console.log(data);
});

// 限制并发数量的多请求执行
function multiRequest(optionsList = [], maxNum) {
const len = optionsList.length;
const result = new Array(len).fill(false);
let count = 0;
return new Promise((resolve, reject) => {
while (count < maxNum) {
next();
}
function next() {
let current = count++;
if (current >= len) {
!result.includes(false) && resolve(result);
return;
}
const options = optionsList[current];
request(options)
.then((res) => {
result[current] = res;
if (current < len) {
next();
}
}).catch((err) => {
result[current] = err;
if (current < len) {
next();
}
});
}
});
}
multiRequest([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3).then(res => {
console.log(res);
});

// 提供给第三方使用的 request 方法,内部实现支持限制并发数量的请求,并发量为3
class LimitRequest {
constructor(limit) {
this.limit = limit;
this.tasks = [];
this.current = 0;
}
addRequest(reqFn) {
if (!reqFn || !(reqFn instanceof Function)) {
console.error('当前请求不是一个Function');
return;
}
this.tasks.push(reqFn);
if (this.current < this.limit) {
this.run();
}
}
async run() {
try {
this.current++;
const fn = this.tasks.shift();
await fn();
} catch(err) {
throw new Error(err);
} finally {
this.current--;
if (this.tasks.length > 0) {
this.run();
}
}
}
}
let limitRequest = new LimitRequest(3);

limitRequest.addRequest(fetch.bind(null, '1'));
limitRequest.addRequest(fetch.bind(null, '2'));
limitRequest.addRequest(fetch.bind(null, '3'));
limitRequest.addRequest(fetch.bind(null, '4'));
limitRequest.addRequest(fetch.bind(null, '5'));
limitRequest.addRequest(fetch.bind(null, '6'));
limitRequest.addRequest(fetch.bind(null, '7'));
limitRequest.addRequest(fetch.bind(null, '8'));
limitRequest.addRequest(fetch.bind(null, '9'));
limitRequest.addRequest(fetch.bind(null, '10'));

优化下,无需每次都 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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class LimitRequest {
constructor(limit) {
this.limit = limit; // 并发限制数量
this.tasks = []; // 等待的请求队列
this.current = 0; // 当前进行中的请求数量
}
request(options) {
return new Promise((resolve, reject) => {
let reqFn = request.bind(null, options);
this.tasks.push([reqFn, resolve, reject]);
if (this.current < this.limit) {
this.run();
}
});
}
async run() {
try {
this.current++;
const [fn, resolve, reject] = this.tasks.shift();
await fn().then(res => {
resolve(res);
}).catch(err => {
reject(err);
});
} catch(err) {
throw new Error(err);
} finally {
this.current--;
if (this.tasks.length > 0) {
this.run();
}
}
}
}

let limitRequest = new LimitRequest(3);
let newRequest = limitRequest.request.bind(limitRequest);
newRequest('1').then(console.log);
newRequest('2').then(console.log);
newRequest('3').then(console.log);
newRequest('4').then(console.log);
newRequest('5').then(console.log);
newRequest('6').then(console.log);
newRequest('7').then(console.log);
newRequest('8').then(console.log);
newRequest('9').then(console.log);
newRequest('10').then(console.log);

utils写法

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
const limit = 3; // 并发限制数量
let tasks = []; // 等待的请求队列
let current = 0; // 当前进行中的请求数量
export function newRequest(options) {
return new Promise((resolve, reject) => {
let reqFn = request.bind(null, options);
tasks.push([reqFn, resolve, reject]);
if (current < limit) {
run();
}
});
}
async function run() {
try {
current++;
const [fn, resolve, reject] = this.tasks.shift();
await fn().then(res => {
resolve(res);
}).catch(err => {
reject(err);
});
} catch(err) {
throw new Error(err);
} finally {
current--;
if (tasks.length > 0) {
run();
}
}
}
newRequest('1').then(console.log);
newRequest('2').then(console.log);
newRequest('3').then(console.log);
newRequest('4').then(console.log);
newRequest('5').then(console.log);
newRequest('6').then(console.log);
newRequest('7').then(console.log);
newRequest('8').then(console.log);
newRequest('9').then(console.log);
newRequest('10').then(console.log);

版本号比较

1
2
3
4
5
6
7
8
9
10
11
12
function compareVersion(version1, version2) {
let arr1 = version1.split('.');
let arr2 = version2.split('.');
let len = Math.max(arr1.length, arr2.length);
for (let i = 0; i < len; i++) {
const n1 = Number(arr1[i]||0);
const n2 = Number(arr2[i]||0);
if (n1 > n2) return 1;
if (n1 < n2) return -1;
}
return 0;
}

m 个红球, n 个白球,依次全部取出,求共有多少种不同的取出顺序

即求概率论中C(m+n)n

1
2
3
4
5
6
7
8
9
10
11
12
13
function factorial(number) {
let result = 1;
if (number === 0 ||number === 1)
return 1;
for (let i = 2; i <= number; i++) {
result *= i;
}
return result;
}

function getNum(m, n) {
return factorial(m + n)/factorial(m)/factorial(n);
}