Skip to content

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

imgimg

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 ,实现 sleepeat

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