Appearance
coding
检测模块循环引用
js
let obj = {
a: { dependencies: ['b'] },
b: { dependencies: ['c'] },
c: { dependencies: ['d'] },
d: { dependencies: ['a'] },
}
// 检查模块循环引用
function findCycle(obj) {
if (!obj) return false
let dfs = (key, set) => {
if (set.has(key)) {
return true
}
set.add(key)
for (k of obj[key].dependencies) {
if (dfs(k, set)) {
return true
}
}
set.delete(key)
return false
}
for (key in obj) {
if (dfs(key, new Set())) {
return true
}
}
return false
}
console.log(findCycle(obj))
斐波那契数列 for循环版
js
function fib(n) {
if (n <= 1) return n;
let fibpre = 0;
let fibcur = 1;
for (let i = 2; i <= n; i++) {
let temp = fibcur
fibcur = fibpre + fibcur
fibpre = temp
}
return fibcur
}
function fib(n) {
if (n <= 1) return n;
let fibArr = [1, 1];
for (let i = 2; i <= n; i++) {
fibArr.push(fibArr[i - 1] + fibArr[i - 2]);
}
return fibArr[n - 1];
}
console.log(fib(10));
js
// 斐波那契数列 递归版(性能差)
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
console.log(fib(10));
取最大值
js
var arr = [2, 34, 54, 34, 2, 345];
var max = arr[0];
for (let index = 1; index < arr.length; index++) {
if (arr[index] > max) {
max = arr[index];
}
}
console.log("最大值", max);
console.log("最大值", Math.max.apply(null, arr));
console.log("最大值", Math.max(...arr));
取最小值
js
var min = arr[0];
for (let index = 1; index < arr.length; index++) {
if (arr[index] < min) {
min = arr[index];
}
}
console.log("最小值", min);
console.log("最小值", Math.min.apply(null, arr));
console.log("最小值", Math.min(...arr));
去重复
js
let newArr = arr.reduce((temp, item) => {
if (temp.includes(item)) {
return temp;
}
return temp.concat(item);
}, []);
console.log("去重复", newArr);
let resultArr = [...new Set(arr)];
console.log("去重复", resultArr);
数组展平
js
let arr = [1, 2, 3, [4, 5], 6, [7, 8, 9]];
console.log("数组展平", arr.toString().split(","));
let newArr = arr.reduce((tempArr, y) => {
return tempArr.concat(y);
}, []);
console.log("数组展平", newArr);
var arr = [11, [22, [33, [44]]]];
console.log("数组展平", arr.flat(3));
转换树形菜单
js
const list = [
{ id: 1, name: "电子商品", parentId: 0 },
{ id: 2, name: "衣服", parentId: 0 },
{ id: 3, name: "手表", parentId: 1 },
{ id: 4, name: "手机", parentId: 1 },
{ id: 5, name: "耐克", parentId: 2 },
];
function listToTree(list) {
if (!Array.isArray(list)) return list;
let treeList = [];
list.forEach((item) => {
// 根节点
if (+item.parentId === 0) {
treeList.push(item);
} else {
// 找父级节点
let parent = list.find((p) => p.id === item.parentId);
// 把自己添加到父级节点下
(parent.children || (parent.children = [])).push(item);
}
});
return treeList;
}
console.log(listToTree(list));
树转列表
js
function treeTolist(tree, list = []) {
tree.forEach((item) => {
if (Array.isArray(item.children) && item.children.length > 0) {
let { children, ...data } = item;
list.push(data);
treeTolist(item.children, list);
} else {
list.push(item);
}
});
return list;
}
console.log(treeTolist(treeList));
并发请求
实现一个函数, 可以间隔输出,每4秒输出一次helloWorld, 输出3次
js
// 实现一个函数, 可以间隔输出,每4秒输出一次helloWorld, 输出3次
function createRepeat(fn, repeat, interval) {
return (params)=>{
let beginTime = Date.now()
let endTime = beginTime+(1000*interval)
let c = 0
while(beginTime<=endTime && c<repeat){
beginTime= Date.now()
if(beginTime===endTime){
fn(params)
c++
endTime = beginTime+(1000*interval)
}
}
}
}
const fn = createRepeat(console.log, 3, 4);
fn('helloWorld'); //
console.log('end')
引用地址指向问题
js
function changeObjProperty(o) {
o.siteUrl = "http://www.baidu.com";
o = new Object(); // 指向新的引用地址
o.siteUrl = "http://www.google.com";
}
let webSite = new Object();
changeObjProperty(webSite);
console.log(webSite.siteUrl); // http://www.baidu.com
this指向问题
js
function Parent () {
this.a = 1;
this.b = [ 1, 2, 1 ];
this.c = { dome: 5 };
this.show = function () {
console.log( this.a, this.b, this.c.dome );
};
}
function Chind () {
this.a = 2;
this.change = function () {
this.b.push( this.a );
this.a = this.b.length;
this.c.dome = this.a++;
};
}
Chind.prototype = new Parent(); // 不是一个实例
var parent = new Parent(); // 不是一个实例
var chind1 = new Chind();
var chind2 = new Chind();
chind1.a = 11;
chind2.a = 12;
parent.show(); // 1 [1,2,1] 5
chind1.show(); // 11 [1,2,1] 5
chind2.show(); // 12 [1,2,1] 5
chind1.change();
chind2.change();
parent.show(); // 1 [ 1,2,1] 5
chind1.show(); // 5 [ 1,2,1,11,12] 5
chind2.show(); // 6 [ 1,2,1,11,12] 5
如何让同时满足a==1、a==2、a==3
- 1、创建一个对象,重写toString方法
- 2、对象在和基本类型比较时,会进行隐式转换
- 3、会先调用toString,让n累加
js
let a = {
n: 1,
toString () {
return this.n++;
},
};
if ( a == 1 && a == 2 && a == 3 ) {
console.log( "ok" );
}
parseInt
- parseInt 把操作数转位十进制整数
- parseInt 接受第二个参数,如果不传默认或者参数是 0,都是当做 10 进制处理,范围是(2-36),如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN
js
["1", "2", "3"].map((val, index) => parseInt(val, index));
parseInt(1, 0); // 1
parseInt(2, 1); // NaN
parseInt(3, 2); // NaN 将二进制转位10进制,因为二进制只有0和1
闭包作用域案例
js
let a = 10;
let b = 20;
function A ( a ) {
// var a = 100
A = function ( y ) {
console.log( y + b + a++ ); // 200 + 20 + 101
};
// 先复制在a+1,所以打印100
console.log( a++ ); // 100
console.log( a ); // 101
}
A(100)
A(200) // 结果`200 + 20 + 101= 321`
运算符赋值优先级
- 解析
1、在
堆内存
中声明一个变量a
,在栈内存
开辟一个空间存储对应的引用,a
的地址执行对象的引用2、
堆内存
中声明一个变量b
,直接指向a的引用地址3、在连等赋值的时候,由于
a.x
的优先级比较高优先计算,让引用地址的x属性,指向一个新的引用地址4、在计算a也指向一个新的引用地址
因为a已经指向了新的引用地址,新的对象没有x属性,导致undefined
b始终指向一个引用地址,并且加了一个x属性指向一个新的引用
js
var a = {
n: 1,
};
var b = a;
a.x = a = {
n: 2,
};
console.log( a.x ); // --> undefined
console.log( b ); // --> {n:1,x:{n:2}}
函数颗粒化
- 把一个函数进行分解成 N 个执行单元,每一个执行单元执行时去收集参数,当前收集的参数个数达到执行条件时,执行原有的函数
js
function curry(fn) {
let params = [];
let inner = (...args) => {
params.push(...args);
return params.length >= fn.length ? fn(...params) : inner;
};
return inner;
}
function sum(a, b, c, d) {
return a + b + c + d;
}
let fn = curry(sum);
console.log(fn(1)(2)(3)(4)); // 10
console.log(fn(1, 2)(3, 4)); // 10
console.log(fn(1, 2)(3)(4)); // 10
console.log(fn(1, 2)(3)(4)); // 10
反颗粒化
- 反柯里化主要是借用别人的函数,比如类数组没有 push 函数,但是可以借用 push 函数
js
Function.prototype.unCurry = function () {
let self = this;
return (content) => {
return self.call(content);
};
};
Function.prototype.unCurry = function () {
var self = this;
return function () {
return Function.prototype.call.apply(self, arguments);
};
};
// es6版本
Function.prototype.unCurry = function () {
return this.call.bind(this);
};
Function.prototype.unCurry = function () {
return (...args) => this.call(...args);
};
let isType = Object.prototype.toString.unCurry();
console.log(isType([])); // [object Array]
// 数组复制
const clone = Array.prototype.slice.unCurry();
var a = [1, 2, 3];
var b = clone(a);
console.log("a==b:", a === b); // false
console.log(a, b); // [1, 2, 3] [1, 2, 3]
// 类数组
let push = Array.prototype.push.unCurry();
let o = { a: 1 };
push(o, 2);
console.log(o); // { '0': 2, a: 1, length: 1 }
函数执行上下文
- 函数执行的上级,上下文和函数在哪执行的没关系,只和它的作用域有关系「作用域由创建函数时候的上下文来决定」
js
function fn1(){
let a = 10
return function(){
console.log(a);
}
}
let f = fn1()
function fn2(){
let a = 20
f()
}
fn2() // 10
作用域案例
js
var x =3;
var obj = {x:5};
obj.fn =(function(){
this.x *= ++x // => this.x * (++x)
return function(y){
this.x *= (++x)+y // => this.x * (++x+y)
console.log(x)
}
})()
var fn = obj.fn
obj.fn(6) // 13
fn(4) // 234
console.log(obj.x,x) // 95 234
函数执行,小括号表达式对this的影响
- 小括号中包含“多项”,这样也只取最后一项,但是this受到影响
js
function fn () {
console.log( this )
}
let obj = {
name: 'hl',
fn
}
(obj.fn)(); // obj
('10',obj.fn)(); // Window
var、作用域变量提升
- 在函数作用域中用var声明的会变量提升,优先找函数内部的
- 此时var声明的变量a声明未定义是
undefined
,undefined
取反为true,赋值a=10
js
var a = 1
function f1(){
if(!a){
var a = 10
}
console.log(a)
}
f1() // 10
var变量提升和函数提升案例
- var只会提前提示声明,function声明定义一起完成
- 这里function同一个函数多次声明,后面的会覆盖前面的
- 当代码执行到var时,会覆盖函数的定义
js
fn(); // 5
function fn () { console.log( 1 ); }
fn(); // 5
function fn () { console.log( 2 ); }
fn(); // 5
var fn = function () { console.log( 3 ); }
fn(); // 3
function fn () { console.log( 4 ); } // 执行到这里,因为函数已经提前声明定义了,这里并不会有操作
fn(); // 3
function fn () { console.log( 5 ); }
fn(); // 3
函数调用变量提升(形参)
js
console.log(a, b, c); // undefined undefined undefined
var a = 12,
b = 13,
c = 14;
function fn(a) {
// 形参相当于var a = 10(私有)
console.log(a, b, c); // 10 13 14
a = 100; // 私有
c = 200; // 全局
console.log(a, b, c); // 100 13 200
}
b = fn(10);
console.log(a, b, c); // 12 undefined 200
函数调用变量提升(形参,函数)
- 函数声明和定义都是一起的,并且优先级比var声明的要高
js
var a = 1
function fn(a) {
console.log(a);
// var a = 1
var a = 2; // 私有
function a(){}
}
fn(a); // [Function: a]
函数参数为函数
js
var x =100
function func(x,y=function(){x=10}){
x= 3; // 私有
y() // 找上级作用域,修改x=10
console.log(x) // 10
}
func()
自执行函数
- 自执行函数是无法被修改的
js
var b = 10
;(function b(){
b= 20
console.log(b) // [Function: b]
})()
console.log(b) // 10
对象深度对比
js
function isObject ( obj ) {
return typeof obj === 'object' && obj !== null
}
function isEqual ( obj1, obj2 ) {
if ( !isObject( obj1 ) || !isObject( obj2 ) ) {
return obj1 === obj2
}
if ( obj1 === obj2 ) {
return true
}
const obj1Keys = Object.keys( obj1 )
const obj2Keys = Object.keys( obj2 )
if ( obj1Keys.length !== obj2Keys.length ) {
return false
}
for ( let key in obj1 ) {
const res = isEqual( obj1[ key ], obj2[ key ] )
if ( !res ) {
return false
}
}
return true
}
数组的中的函数this
js
let a = [1]
a.push(function(){
console.log(this)
})
a[1]() // [1, ƒ]
// => a[1] => a.1 => 1.call(a)
bind实现
- bind是一个闭包函数,它会返回一个函数,最终通过call执行函数并且改变this指向
js
Function.prototype.myBind = function () {
const [ content, ...params ] = arguments;
const target = this;
return ( ...arg ) => {
return target.call( content, ...params, ...arg );
};
};
function f3 () {
console.log( "f3", this, arguments );
}
var o3 = {
name: "10",
};
f3.myBind( o3, 10 )( 1, 2 )
call 简单实现
js
Function.prototype.call = function () {
let [self, ...args] = arguments;
self = self ? Object(self) : window; // 防止空数据
self.fn = this; // 当前需要执行的函数,利用函数特性改变this
self.fn(...args); // 执行函数
};
function a(a, b) {
console.log(a, b);
console.log(this.a);
}
let obj = {
a: 20,
};
a.call(null, 100, 200);
函数 call 多次
- 解析,多次cell
- 1、a.call.call.call.call(b) 因为call在原型上多次call都是等价,这里其实可以简写成:call.call(b)
- 2、call内部实现:会把第一个参数作为this,因为需要改变this的指向,并且还需要指向当前的函数
- 3、此时可以简化为b.fn = call=> b.call() // b
js
function a() {
console.log("a");
}
function b() {
console.log("b");
}
a.call(b); // a
a.call.call(b); // b
a.call.call.call.call(b); // b
console.log(a.call === Function.prototype.call); // true
console.log(a.call === a.call.call); // true
console.log(a.call === a.call.call.call); // true
原生ajax请求
js
var xhr;
//创建一个异步对象
xhr = new XMLHttpRequest();
//发送请求的方式 2. 样请求的页面 3.是否异步
xhr.open( "post", "test.json", true );
//Post 方式发送数据
xhr.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
//设置浏览器不使用缓存
xhr.setRequestHeader( "If-Modified-Since", "0" );
xhr.onreadystatechange = function () {
if ( xhr.readystate == 4 ) {
//readyState 属性指出了 XMLHttpRequest 对象在发送/接收数据过程中所处的几个状态。 XMLHttpRequest 对象会经历 5 种不同的状态。
//0:未初始化。对象已经创建,但还未初始化,即还没调用 open 方法;
//1:已打开。对象已经创建并初始化,但还未调用 send 方法;
//2:已发送。已经调用 send 方法,但该对象正在等待状态码和头的返回;
//3:正在接收。已经接收了部分数据,但还不能使用该对象的属性和方法,因为状态和响应头不完整;
//4:已加载。所有数据接收完毕
if ( xhr.status == 200 ) {
alert( xhr.responseText ); //服务器返回的 Response 数据 //解析服务器返回的 jason 格式的数据
var s = xhr.responseText;
var json = eval( "(" + s + ")" );
alert( json.data );
}
}
};
xhr.send( null ); //异步对象发送请求
//xhr.send("txtName=roger&txtPwd=123"); 以post方式发送数据 ajax 中 get 和 post 方式请求数据都是明文的。
节流throttle
- 节流(throttle):每隔一段时间后执行一次,也就是降低频率,将高频操作优化成低频操作,通常使用场景: 滚动条事件 或者 resize 事件
- 反抖 (debounce): 将多次高频操作优化为只在最后一次执行,通常使用的场景是:用户输入,只需再输入完成后做一次输入校验即可。
js
function throttle ( callback, delay ) {
let timer = null;
let starTime = Date.now()
return function () {
let curTime = Date.now()
clearInterval( timer );
// 规定的时间触发,不然用户一直操作就一直不会触发
if ( curTime - starTime >= delay ) {
starTime = curTime;
callback();
} else {
timer = setTimeout( callback, delay );
}
};
}
发布订阅模式
- 发布订阅模式:通过一个队列还耦合,订阅者、发布者,2 者本身没有关系
- 发布和订阅之间没有依赖关系、只是收集 统一触发
js
let pubSub = {
subCallbacks: [],
on(subCallback) {
// 订阅
console.log("订阅了");
this.subCallbacks.push(subCallback);
},
emit(data) {
//发布
this.subCallbacks.forEach((subCallback) => subCallback(data));
},
};
pubSub.on((data) => {
console.log("接收发布的信息:" + data);
});
pubSub.on((data) => {
console.log("接收发布的信息:" + data);
});
pubSub.emit("群发");
观察者模式
- 观察者模式:一个被观察者,一个观察者,把(观察者)的实例,存在被观察者队列中,被观察者状态发生变化,会通知每一个观察者
- 观察者与被观察者有依赖关系,被观察者会收集观察者,被观察者状态发生变化,会通知每一个观察者
js
// 被观察者
class Subscribe {
constructor(name) {
this.name = name;
this.observables = [];
this.state = "";
}
push(observable) {
this.observables.push(observable);
}
setState(state) {
console.log(`${this.name}:通知`);
this.state = state;
this.observables.forEach((observable) => observable.update(state));
}
}
// 观察者
class Observable {
constructor(name) {
this.name = name;
}
update(state) {
console.log(`${this.name}收到了:${state}`);
}
}
// 实例被观察者
let subscribe = new Subscribe("联通");
// 实例观察者
let observable1 = new Observable("小红");
let observable2 = new Observable("小白");
// 把观察者实例绑定在观察者上
subscribe.push(observable1);
subscribe.push(observable2);
// 更新通知观察者
subscribe.setState("发1G流量");
对象克隆
- 直接把一个对象赋值给一个变量,它们指向同一个内存地址,当对象的值发生改变时,另外一个对象的值也会改变(浅拷贝)
- 在赋值时,可以把对象先转成字符串,在转成对象在赋值,当对象的值发生改变,不会影响另外一个(深拷贝), 缺点如果属性值有函数、undefined 会被过滤掉
js
function cloneDeep(data, weakMap = new WeakMap()) {
if (typeof data != null && typeof data === "object") {
let cloneData = new data.constructor();
let circularData = weakMap.get(data);
if (circularData) {
return circularData;
}
weakMap.set(data, data);
for (const key in data) {
if (Object.hasOwnProperty.call(data, key)) {
cloneData[key] = cloneDeep(data[key], weakMap);
}
}
return cloneData;
} else {
return data;
}
}
let a = { a: 10, b: { a: 20, c: { c: 20 } } };
a.h = a;
console.log(cloneDeep(a));
Web 复制文本
js
// 需要人为的触发,需要https
function copyToClipboard(textToCopy) {
if (navigator.clipboard) {
return navigator.clipboard.writeText(textToCopy);
} else {
console.log("其他方式复制");
}
}
btnCopy.onclick = function () {
copyToClipboard("需要拷贝的内容")
.then(() => console.log("拷贝成功"))
.catch(() => console.log("拷贝失败"));
};
设备媒体网络状态
js
window.getSelection().toString();
console.log(navigator.onLine); // true 在线
window.addEventListener("online", function () {
console.log("在线");
});
window.addEventListener("offline", function () {
console.log("离线");
});
toString 怪异现象
- 1、.toString()方法,返回对象的字符串表现
- 2、Array、function 等类型作为 Object 实例,重写了 toString 函数
- 3、想要得到对象具体的类型,需要调用 Object 原型的 toString 函数
- 4、所以我们需要通过 Object.prototype.toString.call([]) 来的到具体的类型
js
let arr = [1, 2, 3];
// 被重写过的
console.log(arr.toString()); // 1,2,3
// 本身有toString属性
console.log(Array.prototype.hasOwnProperty("toString")); // true
// 删除重写的toString属性
delete Array.prototype.toString;
console.log(Array.prototype.hasOwnProperty("toString")); // false
// 调用未被重写过的 Object.prototype.toString
console.log(arr.toString()); // [object Array]
JSON.parse 和 JSON.stringify
js
// JSON.parse(二个参数)
const jsonStr = `
{
"name": "帅哥",
"age": 18,
"isFans": true,
"IDCard": "xxxxxxxxxxxxxxxxxx"
}
`;
// 第二次参数是一个函数,如果返回undefined属性会被过滤掉
var obj = JSON.parse(jsonStr, function (key, value) {
if (key == "IDCard") {
return undefined;
} else {
return value;
}
});
console.log(obj);
// JSON.stringify(三个参数)
//replacer 方法
var person = {
name: "帅哥",
age: 45,
birth: "1990-01-01",
};
var jsonString = JSON.stringify(person, function (key, value) {
if (typeof value === "string") {
return undefined;
}
return value;
});
// 只保留值是数字的
console.log(jsonString); // {"age":45}
var person = {
name: "帅哥",
age: 45,
birth: "1990-01-01",
};
//replacer 只保留name、age
console.log(JSON.stringify(person, ["name", "age"])); // {"name":"帅哥","age":45}
//space 美化格式
var person = {
name: "帅哥",
age: 45,
birth: "1990-01-01",
};
const a = JSON.stringify(person);
console.log(a);
var person = {
name: "帅哥",
age: 45,
birth: "1990-01-01",
};
const c = JSON.stringify(person, null, 10); // 上线10个空格
console.log(c);
运算符的诡异现象
- let、const 不会在 window 上
js
let name = "let的name";
const person = {
name: "person的Name",
getName() {
return this.name;
},
};
const getName = person.getName;
console.log("getName", getName()); // undefined window.getName()
console.log("person.getName", person.getName()); // person的Name
console.log("(person.getName)", (person.getName)()); // person.getName()
/**
* (0, person.getName)() 会转成
*
* let getName = person.getName;
* getName() => window.getName()
*/
console.log("(0, person.getName)", (0, person.getName)());
实例属性
js
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
// 箭头函数是实例函数
getName = () => {
return this.name;
};
// 原型函数
getAge() {
return this.age;
}
}
js
const proto = {
name: "原型",
arr: [1, 2],
};
const person = Object.create(proto);
// 自身加一个属性
person.name = "实例";
// 原型上的
person.arr.push(3);
console.log(person.name);
console.log(proto.name);
console.log(person.arr);
console.log(proto.arr);
encodeURI
beforeunload
- 表单的要焦点之后才会生效
js
window.addEventListener("beforeunload", (event) => {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = "";
});
手写 LazyMan ,实现 sleep
和 eat
- 1、支持链式调用
- 2、支持等待
js
class LazyMan {
constructor(name) {
this.name = name
this.qeues = []
setTimeout(() => {
this.next()
});
}
next() {
const task = this.qeues.shift();
if (task) {
task();
}
}
eat(product) {
this.qeues.push(() => {
console.log(`${this.name}在吃${product}`)
this.next()
})
return this;
}
sleep(time) {
this.qeues.push(() => {
console.log(`${this.name}在睡觉`)
setTimeout(() => {
this.next()
}, time * 1000);
})
return this;
}
}
const me = new LazyMan('hl')
me.eat('苹果').eat('香蕉').sleep(5).eat('葡萄').eat('栗子').sleep(5).eat('菠萝')
旋转数组
- 定义一个函数,实现数组的旋转。如输入
[1, 2, 3, 4, 5, 6, 7]
和key = 3
, 输出[5, 6, 7, 1, 2, 3, 4]
js
let arr = [1,2,3,4,5,6,7]
function rotate(arr,key){
let arr1 = arr.slice(0,arr.length-key)
let arr2 = arr.slice(-key)
return arr2.concat(arr1)
}
console.log(rotate(arr,3))
1-10000 之间的对称数(回文)
- 如:1,11,121,232...
js
function palindrome(n) {
let arr = []
for (let i = 1; i <= n; i++) {
let str = i.toString();
let startIndex = 0;
let endIndex = str.length - 1
while (startIndex <= endIndex) {
if (str[startIndex] != str[endIndex]) {
break
}
arr.push(i)
startIndex++;
endIndex--;
}
}
return arr
}
console.log(palindrome(11)) // [1, 2, 3, 4, 5,6, 7, 8, 9, 11]
连续最多的字符
- 给一个字符串,找出连续最多的字符,以及次数。
- 例如字符串
'aabbcccddeeee11223'
连续最多的是e
,4 次。
js
var maxPower = function (s) {
if (!s) return 0;
let obj = {
str: '',
n: 0
}
let i = 0;
let j = 0;
let tempIndex = 0;
for (; i <= s.length - 1; i++) {
if (s[i] === s[j]) {
tempIndex++;
}
if (s[i] !== s[j]) {
if (tempIndex > obj.n) {
obj.str = s[j];
obj.n = tempIndex
}
tempIndex = 0;
if (i < s.length - 1) {
j = i;
i--;
}
}
}
return obj.n
};
console.log(maxPower('j'))
控制台请求并发数
js
(async () => {
function delay(ms, simulateError = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (simulateError) {
reject();
} else {
console.log("执行任务", ms / 1000);
resolve();
}
}, ms);
});
}
let tasks = [
delay(1000 * 10),
delay(1000 * 20, true),
delay(1000 * 11),
delay(1000 * 30),
delay(1000 * 40),
delay(1000 * 41),
delay(1000 * 21),
delay(1000 * 42),
delay(1000 * 22),
];
async function httpPool(tasks = [], poolMax = 6) {
let i = 0;
let tasksLength = tasks.length;
let taskPool = [];
let failTaskPool = [];
while (i < tasksLength) {
let task = tasks[i];
taskPool.push(task);
task.catch(() => {
failTaskPool.push(task);
console.log('任务发生错误');
}).finally(() => { taskPool.splice(taskPool.findIndex((_) => _ === task), 1) });
if (taskPool.length >= poolMax) {
try {
await Promise.race(taskPool);
} catch (error) {
}
}
i++;
}
await Promise.all(taskPool);
console.log('所有任务执行完毕');
console.log('成功任务', taskPool);
// TODO:重试机制
console.log('失败任务', failTaskPool);
}
await httpPool(tasks);
console.log("合并")
})()