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("合并");
})();