记录一些平时工作中或者练习算法时写的函数
1. 获取url参数
function getSearch(name, search) {
if (!search && typeof window !== 'undefined') {
search = window?.location?.search
}
if (!search || typeof search !== 'string') return null
search = search.startsWith('?') ? search.slice(1) : search
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'g')
const match = search.match(reg)?.map((item) => item.replaceAll('&', ''))
if (!match) {
return null
}
if (match.length == 1) {
return decodeURIComponent(match[0].split('=')[1])
}
return match.map((item) => decodeURIComponent(item.split('=')[1]))
}
function getSearch2(search,name) {
if (!search && typeof window !== 'undefined') {
search = window?.location?.search
}
if (!search || typeof search !== 'string') return null
search = search.startsWith('?') ? search.slice(1) : search
let paramsObj = search
.split('&')
?.map((item) => item.split('='))
?.reduce((acc, curr) => {
// 将 ?q=1&q=2 转换成 q:[1,2]
if (acc[curr[0]]) {
// 如果是数组
if (Array.isArray(acc[curr[0]])) {
acc[curr[0]].push(curr[1])
} else {
let one = acc[curr[0]]
acc[curr[0]] = [one, decodeURIComponent(curr[1])]
}
} else {
acc[curr[0]] = decodeURIComponent(curr[1])
}
return acc
}, {})
return name ? paramsObj[name] : paramsObj
}
2. 扁平化数组转tree
/**
* @Description:
* @param {Array} arr 目标扁平化数组
* @param {String} pidFiled 表示父节点的字段名
* @param {any} pidValue 表示父节点的值(基础数据类型)
* @param {String} childFiled 表示子节点的字段名
* @return {Array} 树形 数组
* @author _Autumn_
*/
//实际情况下,后端通常返回的字段名都是pid 和 id ,所以我们可以设置一下默认值(或者直接函数内部写死,看自己习惯,以及需求吧)
function array2Tree(arr, pidFiled = 'pid', pidValue = 0, childFiled = 'id') {
return arr.filter((item) => {
item.children = arr.filter((curr) => {
return curr[pidFiled] == item[childFiled];
});
return item[pidFiled] == pidValue;
});
}
3. 数组去重
1. 利用Set对象去重
function doAwayBySet(arr){
return [...new Set(arr)]
}
2. 利用indexOf去重
function doAwayByArrMethod(arr){
return arr.filter((item,idx) => arr.indexOf(item) == idx)
}
//对象数组去重(可指定字段,支持多字段,O(n))
function doAwayRepeatByFields(target, ...fields) {
let tempArr = []
let tempSet = new Set()
for (let i = 0, len = target.length; i < len; i++) {
// 生成键名
const fieldsStr = fields.reduce((acc, curr) => {
return (acc += target[i][curr])
}, '')
if (tempSet.has(fieldsStr)) {
continue
} else {
tempSet.add(fieldsStr)
tempArr.push(target[i])
}
}
return tempArr
}
4. 生成指定范围的随机整数
function createRandomNum(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
5. 生成rgba随机颜色
//依赖于 4 的函数
function setRandomColor(opacity = 1) {
return `rgba(${createRandomNum(0, 255)},${createRandomNum(
0,
255
)},${createRandomNum(0, 255)},${opacity})`;
}
6. 时间格式化
//补零
function padLeft(num) {
return String(num).length > 1 ? String(num) : '0' + num;
}
//时间格式化
function formatDate(date = new Date(), format = 'YYYY-MM-DD hh:mm:ss') {
date = date ? new Date(date) : new Date()
let regMap = {
Y: date.getFullYear(),
M: padLeft(date.getMonth() + 1),
D: padLeft(date.getDate()),
h: padLeft(date.getHours()),
m: padLeft(date.getMinutes()),
s: padLeft(date.getSeconds())
}
return Object.entries(regMap).reduce((acc, [key, value]) => {
return acc.replace(new RegExp(`${key}+`, 'g'), value)
}, format)
}
7. 限制数字的最大最小值
function getLimitedNum(val, min, max) {
let num = Math.max(min, val);
num = Math.min(max, val);
return num;
};
8. setTimeout 模拟 setInterval
function myInterval(fn, time, endC) {
let timer;
return {
clearTimer() {
if (timer) clearTimeout(timer);
},
setTimer() {
if (timer) this.clearTimer();
if (endC && typeof endC === "function") {
if (endC(...arguments)) return;
}
timer = setTimeout(() => {
fn.apply(this, arguments);
this.setTimer.apply(this, arguments);
}, time);
},
};
}
9. 深拷贝
deepClone(o) {
if (Array.isArray(o)) {
return o.map((item) => deepClone(item));
}
if (Object.prototype.toString.call(o) == "[object Object]") {
let obj = {};
for (let key in o) {
obj[key] = deepClone(o[key]);
}
return obj;
}
return o;
}
10.根据某字段大小进行排序
//由于sort 方法 会直接改变原数组,所以这里利用 9 的 深拷贝函数,如果想直接使用原数组的化,就没必要做深拷贝,返回值也不需要
/**
* @Description:
* @param {Array} target 要排序的数组
* @param {String} filed 要用来排序的字段
* @param {Boolean} order (默认升序)true 代表 降序 ,false(可不传,因为undefined是个假值) 代表升序
* @return {Array} 排序后的数组
* @author _Autumn_
*/
function sortByFiled(target, filed, order) {
let arr = deepClone(target);
if (order) {
arr.sort((a, b) => b[filed] - a[filed]);
} else {
arr.sort((a, b) => a[filed] - b[filed]);
}
return arr;
}
11.判断字符串是否是回文字符串
这里写了两种方法,第一种方法效率比第二种方法低
function isPalindrome1(str){
return str.split("").reverse().join("") == str;
}
function isPalindrome2(str) {
let end = str.length;
if (end < 2) return true;
let start = 0;
end--;
while (start < end) {
if (str[start] !== str[end]) return false;
start++;
end--;
}
return true;
}
12.最长回文字符串
//最长回文字符串
function longestPalindrome(str) {
let end = str.length;
if (str.length == 0 || str.length == 1) return str;
let ret = "";
//记录当前已有的子回文字符串的长度
let count = 0;
while (end >= 2) {
for (let i = 0; i < end; i++) {
//这一段代码如果是使用的 isPalindrome1 必须要加上,否则代码运行效率很低,leetcode那道题(最长回文字符串)我不加这段代码总是超时,如果使用 isPalindrome2 ,它本身算法就做了限制,效率高,无需这段代码,当然也可以加上
if (str[i] !== str[end - 1]) continue;
let nStr = str.slice(i, end);
let len = nStr.length;
if (len <= count) break;
if (isPalindrome2(nStr)) {
count = len;
ret = nStr;
break;
}
}
end--;
}
return ret;
13. 判断一个数据是否是数组
function isArray(arg) {
if (!Array.isArray) {
return Object.prototype.toString.call(arg) === "[object Array]";
}
return Array.isArray(arg);
}
14. 获取数据的真实数据类型
function getType(val) {
return Object.toString
.call(val)
.slice(8, -1)
.toLowerCase();
}
15. 判断两个数据是否相等,对象进行递归判断
//实际上这个函数和深拷贝函数都有一个问题,就是对于非(数组和常规object类型) 的对象其实仍然是对地址值的比较
function isSame(o1, o2) {
//先获取数据类型
let type1 = Object.prototype.toString.call(o1);
let type2 = Object.prototype.toString.call(o2);
if (type1 !== type2) {
return false;
}
//如果是数组先比较length
if (Array.isArray(o1)) {
let len1 = o1.length;
let len2 = o2.length;
if (len1 !== len2) {
return false;
}
return o1.every((item, idx) => isSame(item, o2[idx]));
}
if (type1 === "[object Object]") {
if (Object.keys(o1).length !== Object.keys(o2).length) {
return false;
}
for (let key in o1) {
if (!o2.hasOwnProperty(key)) {
console.log(key, o2);
return false;
}
if (!isSame(o1[key], o2[key])) {
return false;
}
}
return true;
}
return o1 === o2;
}
16. 千分位
function NumFormat(str) {
//如果传进来的是个数值,转成String
if (typeof str === "number" && !isNaN(str)) {
str = String(str);
}
const reg = /(?!^)(?=(\d{3})+$)/g;
return str.replace(reg, ",");
}
17. 对树结构数组将有子节点的数据排列在无子节点的兄弟节点的前方
//这个函数可以用于前端树结构展示时,让有子节点的数据展示在树结构前方,没有子节点的数据展示在后面(前提是数据量不大,没有使用懒加载时)
//判断一个数据是否是长度大于0的数组
function hasChildren(item) {
return Array.isArray(item.children) && item.children.length ;
}
//排序
function sortByChildren(arr) {
let hasChildrenArr = arr.filter((item) => hasChildren(item));
let noChildrenArr = arr.filter((item) => !hasChildren(item));
let concatArr = hasChildrenArr.concat(noChildrenArr);
return concatArr.map((item) => {
item.children =
(hasChildren(item) && sortByChildren(item.children)) || [];
return item;
});
}
18. 模拟数组map方法
// 1. 首先要知道map方法的入参: 一个回调函数,参数为 (数组的每一项,每一项的索引,数组本身)
// 2. 其次要知道map方法的返参: 一个新的数组,数组的每一项元素就是入参回调函数的返回值
// 由于是原型链上的方法,所以我们使用Array.prototype ; 当然也可以把数组当作参数传入,不适用原型链
Array.prototype.myMap = function(callback){
let retArr = []
for(let i = 0,len = this.length;i < len;i++){
retArr.push(callback && callback(this[i],i,this))
}
return retArr
}
19. 发布订阅者模式
//这样的写法存在一个问题,就是如果同名的once监听和on监听,once监听执行顺序是优先于on监听的,这两个监听没有办法按照插入顺序执行
class Publisher {
//基础功能
/*
1. 发布者有一个发布事件器
2. 每一个发布事件器都是一个数组,存取的是订阅者想要触发的事件
3. 只触发一次就会销毁的方法
4. 移除所有事件器中函数的方法
5. 只移指定事件器中指定函数的方法
6. 给事件器添加函数的方法
7. 添加事件和函数的方法
8. 触发事件中函数的方法
*/
constructor() {
this.subscribers = {} //订阅者列表,可是应该有订阅事件名,所以应该是一个对象
this.onceCallBacks = {} //监听一次的对象
}
//只触发一次的事件
once() {
//只监听一次,一旦被触发之后就会立刻被销毁
let eventName = Array.prototype.shift.call(arguments)
if (!this.onceCallBacks[eventName]) {
this.onceCallBacks[eventName] = []
}
for (let i = 0, cb; (cb = arguments[i++]); ) {
if (typeof cb !== 'function') {
continue
}
this.onceCallBacks[eventName].push(cb)
}
}
//可多次触发的事件
on() {
//监听事件
let eventName = Array.prototype.shift.call(arguments)
if (!this.subscribers[eventName]) {
this.subscribers[eventName] = []
}
for (let i = 0, cb; (cb = arguments[i++]); ) {
if (typeof cb !== 'function') {
continue
}
this.subscribers[eventName].push(cb)
}
}
//抽取的事件触发函数
emitEvent(arr, isOnce, args) {
if (!arr) {
return false
}
for (let i = 0, len = arr.length; i < len; i++) {
let cb = arr[i]
cb.apply(this, args)
}
if (isOnce) {
arr.length = 0
}
}
//事件触发
emit() {
//事件触发
let eventName = Array.prototype.shift.call(arguments)
let listener = this.subscribers[eventName],
onceListener = this.onceCallBacks[eventName]
if (this.isArrEmpty(listener) && this.isArrEmpty(onceListener)) {
return null
}
//触发一次性事件
this.emitEvent(onceListener, true, arguments)
//触发非一次性事件
this.emitEvent(listener, '', arguments)
}
//判断一个值是否为数组,如果是,数组长度是否存在(是否有元素)
isArrEmpty(arr) {
return !(Array.isArray(arr) && arr.length)
}
//抽取的公用取消事件函数
offEvent(arr, cb) {
if (!arr) {
return false
}
//由于要删除元素,所以需要从后往前遍历
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i] !== cb) {
continue
}
arr.splice(i, 1)
}
}
//取消事件
off(eventName, cb, isOnce) {
//移除事件
let listener = this.subscribers[eventName],
onceListener = this.onceCallBacks[eventName]
if (this.isArrEmpty(listener) && this.isArrEmpty(onceListener)) return false
if (!cb) {
//删除所有事件
!this.isArrEmpty(onceListener) && (onceListener.length = 0)
if (!isOnce) {
listener.length = 0
}
return true
}
this.offEvent(onceListener, cb)
if (!isOnce) {
this.offEvent(listener, cb)
}
return true
}
}
20. 对一组数据进行求和(数字的求和,字符串的拼接)
function getSum(...args){
return args.reduce((acc,curr) => acc += curr)
}
21. 求一组数值类型数据的平均值
function getAverage(...args){
return args.reduce((acc,curr) => acc += curr) / args.length
}
22. 实现一个自己的call方法
首先要分析call方法他做了哪些事,以及入参和出参
- 作用: 使用特定的上下文环境(引用,其实就是改变一下函数调用的this),来执行函数,并返回该函数执行后的结果
- 入参: 一个context对象(用于修改函数调用的上下文环境); 以及后面的函数的入参(这里需要注意一下,如果context参数为null 或者 undefined 实际上,如果在非严格模式下,其实会有隐式的this声明,所以如果为这两个值,我们可以不用改变this指向)
- 返参: 函数调用的返回值
Function.prototype.myCall = function (context, ...args) {
// xx != null 可以只排除undefined 和 null 两个值
if (context != null) {
context[fn] = this;
let res = context[fn](...args);
delete context[fn];
return res;
}
//如果不为null 和 undefined
//非严格模式下会有隐式this绑定;严格模式下会抛出TypeError错误,因为此时this 为 undefined了
return this(...args);
};
23.节流函数
这里暂时只使用时间戳来实现一个节流函数
/**
* 节流函数
* 1. 是什么
* 节流函数是一种规定最小频率执行的函数,也就是说,如果函数执行之后,没有到达时间间隔,再调用函数时就不会再次执行这个函数
* 2. 实现方式
* 1. 时间戳
* 2. 定时器
* 3. 用法
* 一般用于滚动事件,鼠标移动等会持续触发的事件函数中
*
*/
//1. 时间戳
function throttle(fn, wait = 500, immediate = false) {
//获取当前时间戳
let start = Date.now();
let args = Array.prototype.slice.call(arguments, 3);
if (immediate) {
typeof fn == "function" && fn.apply(null, args);
start = Date.now();
}
return function () {
let end = Date.now();
if (end - start >= wait) {
typeof fn == "function" && fn.apply(null, arguments);
//执行完成之后要修改start时间,用其作为下一次函数执行的起始时间
start = Date.now();
}
};
}
24. 防抖函数
/**
* 防抖
* 1. 是什么:
* 给函数设置一个执行周期,在这个时间周期内如果再次执行这个函数,就会重置该函数的执行时间
* 2. 实现方式
* 1. 定时器
* 3. 用法: 通常用于input输入框中input事件
*/
function debounce(fn, wait = 500) {
let timer;
return function () {
//如果定时器存在就清除定时器,这一步是核心
timer && clearTimeout(timer);
//重新设置定时器
timer = setTimeout(() => {
fn.apply(null, arguments);
}, wait);
};
}
25. 给指定目录下的js文件头部添加内容(通常是头注释)
const fs = require("fs").promises;
const path = require("path");
//向指定目录下的js文件头部添加内容(单层)
function addData2Header(dir, writeData) {
if (!writeData) return false;
fs.readdir(dir)
.then(async (files) => {
for (let i = 0, file; (file = files[i++]); ) {
try {
const filePath = path.join(dir, file);
//获取Stats对象
const stats = await fs.stat(filePath);
//如果是目录
if (stats.isDirectory(filePath)) continue;
//如果是文件
if (stats.isFile()) {
if (!/js/.test(path.extname(filePath))) continue;
//读取文件中的数据
const data = await fs.readFile(filePath);
//将要写入的内容存入buffer
const buff = Buffer.from(writeData + "\n");
fs.writeFile(filePath, Buffer.concat([buff, data]))
.then(() => console.log("内容添加成功" + " " + filePath))
.catch((err) => console.log(err + " " + "内容添加失败"));
}
} catch (error) {
console.log(error);
}
}
})
.catch((err) => console.log(err));
//向指定目录下的js文件的头部添加内容(递归)
function addData2HeaderAll(dir, writeData) {
if (!writeData) return false;
fs.readdir(dir)
.then(async (files) => {
for (let i = 0, file; (file = files[i++]); ) {
try {
const filePath = path.join(dir, file);
const stats = await fs.stat(filePath);
if (stats.isFile()) {
if (!/js/.test(path.extname(filePath))) continue;
const data = await fs.readFile(filePath);
const buff = Buffer.from(writeData + "\n");
fs.writeFile(filePath, Buffer.concat([buff, data]))
.then(() => console.log("内容添加成功" + " " + filePath))
.catch((err) => console.log(err + " " + "内容添加失败"));
continue;
}
if (stats.isDirectory(filePath)) {
addData2Header(filePath);
}
} catch (error) {
console.log(error);
}
}
})
.catch((err) => console.log(err));
}
}
26.tree转数组
function tree2Arr(tree) {
const retArr = [];
inner(tree);
function inner(tree) {
tree.forEach((item) => {
const children = item.children;
delete item.children;
retArr.push(item);
if (children && Array.isArray(children)) {
inner(children);
}
});
}
return retArr;
}
27. 动态创建二维数组
function doubleArray(x, y, init = 0) {
return new Array(x).fill(0).map(() => new Array(y).fill(init))
}
28.二分查找
function search(nums, target) {
let left = 0;
let right = nums.length - 1;
let idx;
while (left <= right) {
idx = ~~((left + right) / 2);
if (nums[idx] == target) return idx;
if (nums[idx] > target) {
right = idx - 1;
} else {
left = idx + 1;
}
}
return -1;
}
search([2,5,10,1,-1], 5) // 1
search([2,0,10,1,-1], 5) // -1
29. 快速排序
function quickSort(nums, left, right) {
if (left > right) return nums;
let middle = getMiddle(nums, left, right);
quickSort(nums, left, middle - 1);
quickSort(nums, middle + 1, right);
function getMiddle(nums, left, right) {
let baseNum = nums[left];
while (left < right) {
//从右向左
while (right > left) {
right--;
if (nums[right] < baseNum) {
[nums[left], nums[right]] = [nums[right], nums[left]];
break;
}
}
//从左向右
while (left < right) {
left++;
if (nums[left] > baseNum) {
[nums[right], nums[left]] = [nums[left], nums[right]];
break;
}
}
}
return left;
}
return nums;
}
const nums = [ 9,7,1,3,5,4,6,8,2];
quickSort(nums, 0, nums.length); // [1,2,3,4,5,6,7,8,9]
30. 匹配括号内容
//按照前序遍历
function matchBracket(str) {
let fronts = [];
let ends = [];
let ret = [];
for (let i = 0, len = str.length; i < len; i++) {
if (str[i] == "(") {
fronts.push(i);
continue;
}
if (str[i] == ")") {
ends.push(i);
let frontsLen = fronts.length;
let endsLen = ends.length
//考虑 ) 先出现的情况
if(endsLen > frontsLen) {
ends.pop()
continue
}
if (endsLen == frontsLen) {
for (let j = 0; j < frontsLen; j++) {
const frontIdx = fronts.shift();
const endIdx = ends.pop();
ret.push(str.slice(frontIdx + 1, endIdx));
}
continue
}
}
}
return ret;
}
//按照出栈顺序遍历
function matchBracket2(str) {
let fronts = [];
let ret = [];
for (let i = 0, len = str.length; i < len; i++) {
if (str[i] == "(") {
fronts.push(i);
continue;
}
if (str[i] == ")" && fronts.length) {
const frontIdx = fronts.pop();
ret.push(str.slice(frontIdx + 1, i));
}
}
return ret;
}
const str = "(1 + 2) * (3 / (5 - 4))";
console.log(matchBracket(str)); // [ '1 + 2', '3 / (5 - 4)', '5 - 4' ]
console.log(matchBracket2(str)); // [ '1 + 2', '5 - 4', '3 / (5 - 4)' ]
31.杨辉三角
function YHTriangle(numRows) {
if (numRows < 1) return [];
let triangle = [[1]];
for (let i = 1; i < numRows; i++) {
let ret = [];
for (let j = 0; j < i + 1; j++) {
// console.log(i, j);
let pre = triangle[i - 1][j - 1];
let current = triangle[i - 1][j] || 0;
num = j == 0 ? current : current + pre;
ret.push(num);
}
triangle.push(ret);
}
return triangle;
}
console.log(YHTriangle(5));// [ [ 1 ], [ 1, 1 ], [ 1, 2, 1 ], [ 1, 3, 3, 1 ], [ 1, 4, 6, 4, 1 ] ]
32.根据node错误优先处理回调封装promisify
function promisify(fn) {
return (...args) => {
return new Promise((resolve, reject) => {
args.push((err, ...res) => {
//错误优先处理
if (err) {
reject(err);
return;
}
resolve(...res);
});
fn(...args);
});
};
}
33. kebab-case 转 小驼峰
function toLowerCamelCase(str) {
const reg = /(?:(?:^-?)|\-)([A-z])/g
return str.trim().replace(reg, (match, p1, offset) => {
if (offset == 0) {
return p1 ? p1.toLowerCase() : ''
}
return p1 ? p1.toUpperCase() : ''
})
}
console.log(toLowerCamelCase('-web-kit')) // webKit
console.log(toLowerCamelCase('web-kit')) // webKit
console.log(toLowerCamelCase('Web-kit')) // webKit
34. kebab-case 转大驼峰
function toUpperCamelCase(str) {
const reg = /(?:(?:^-?)|\-)([A-z])/g
return str.trim().replace(reg, (match, p1) => {
return p1 ? p1.toUpperCase() : ''
})
}
console.log(toUpperCamelCase('-web-kit')) //WebKit
console.log(toUpperCamelCase('web-kit')) //WebKit
# 35. 驼峰转成kebabcase
function toKebabCase(str) {
const reg = /([A-Z])/g
let ret = str.replace(reg, '-$1').toLowerCase()
if (ret.startsWith('-')) {
return ret.slice(1)
}
return ret
}
console.log(toKebabCase('webKit')) // web-kit
console.log(toKebabCase('WebKit')) // web-kit
37. 根据身份证去获取信息
function getAnalysisIdCard(card) {
if (!card) return false;
//当前时间信息
const myDate = new Date(),
myDateYear = myDate.getFullYear(),
myDateMonth = myDate.getMonth() + 1,
myDateDate = myDate.getDate();
// 用户出生信息
const birthYear = card.substring(6, 10),
birthMonth = card.slice(10, 12),
birthDate = card.slice(12, 14),
birthday = `${birthYear}-${birthMonth}-${birthDate}`;
let age = myDateYear - birthYear;
//
if (birthMonth > myDateMonth) {
age--;
}
if (birthMonth == myDateMonth && birthDate > myDateDate) {
age--;
}
const sex = parseInt(card.substr(16, 1)) % 2 == 1 ? 1 : 2;
return {
age,
birthday,
sex,
};
}
//单独抽取一个判断性别的函数
function getGenderByIdcard(idcard) {
return idcard.slice(-2, -1) % 2 === 0 ? "女" : "男";
}
38 最小堆数据结构实现
class MinHeap {
constructor() {
this.heap = [];
}
size() {
return this.heap.length;
}
peek() {
if (this.size()) {
return this.heap[0];
}
return null;
}
/*
添加的时候,向堆尾添加,向上面移动(如果从堆顶添加,整个二叉树就乱掉了)
*/
push(node) {
this.heap.push(node);
// 将node传入主要是为了后面继承的改写罢了,万一node是一个对象呢?我们只需要使用node节点的某一个字段进行比较,抽出compare函数也是一样的道理,后面继承的时候直接改写compare函数就可以了
this.shiftUp(node, this.size() - 1);
}
/*
删除节点
使用最后一个节点替换掉第一个节点,然后向下移动
*/
pop() {
if (!this.size()) {
return null;
}
if (this.size() === 1) {
return this.heap.pop();
}
let lastNode = this.heap.pop();
this.heap[0] = lastNode;
this.shiftDown(this.heap[0], 0);
}
// 比较函数
compare(a, b) {
return a - b < 0;
}
// 变量交换
swap(idx, parentIdx) {
[this.heap[parentIdx], this.heap[idx]] = [
this.heap[idx],
this.heap[parentIdx],
];
}
// 向上移动
shiftUp(node, idx) {
let parentIdx = (idx - 1) >> 1;
while (idx > 0) {
if (this.compare(node, this.heap[parentIdx])) {
this.swap(idx, parentIdx);
idx = parentIdx;
parentIdx = (idx - 1) >> 1;
} else {
break;
}
}
}
// 向下移动
shiftDown(node, idx) {
while (idx < this.size() - 1) {
let leftIdx = (idx + 1) * 2 - 1;
let rightIdx = leftIdx + 1;
// 左子节点肯定有,右子节点未必有,防止出现compare函数为NaN < 0 = false 的情况,要做typeof判断一下
if (this.compare(this.heap[leftIdx], node)) {
// 左子节点小于父节点
if(typeof this.heap[rightIdx] !== 'undefined' && this.compare(this.heap[rightIdx], this.heap[leftIdx])){
// 右子节点存在并且小于左子节点
this.swap(rightIdx, idx);
idx = rightIdx;
}else {
this.swap(leftIdx, idx);
idx = leftIdx;
}
} else if (typeof this.heap[rightIdx] !== 'undefined' && this.compare(this.heap[rightIdx], node)){
// 右子节点存在并且小于父节点
this.swap(rightIdx, idx);
idx = rightIdx;
}else {
break
}
}
}
}
39. 支付宝小程序alipays链接转成 appId,page,query 对象形式,使用 my.navigateToMiniProgram() API 跳转小程序
function schemeToParams(schema) {
if (!schema.startsWith('alipays:')) {
return { message: '! 非 alipays: 开头' };
}
const url = schema.split('?')[1];
let data = url.split('&');
let appId = data[0]?.split('=')[1];
let page, query;
const decodeUrl = decodeURIComponent(url);
// 有page参数就获取,没有的话默认匹配小程序首页
if (decodeUrl.includes('page=')) {
page = decodeURIComponent(data[1]).slice(5);
}
if (decodeUrl.includes('query=')) {
if (page) {
query = data[2] ? decodeURIComponent(data[2]).slice(6) : {};
} else {
query = data[1] ? decodeURIComponent(data[1])?.slice(6) : {};
}
}
try {
const temp = query?.split('&').map((item) => {
return [item?.split('=')[0], decodeURIComponent(item?.split('=')[1])];
});
query = {};
temp?.forEach(([key, value]) => {
query[key] = value;
});
} catch (error) {
query = {};
}
return {
appId,
page,
query,
};
};