JavaScript 常用函数
1、 数组去重
arrayUnique.js
function arrayUnique(arr) {
return [...new Set(arr)];
}
2、 Promise执行控制器
promiseController.js
function promiseController(arr, concurrency = 3) {
let running = 0;
const total = arr.length;
return new Promise((resolve) => {
const result = [];
const pick = () => {
// 全部执行完毕
if (result.length === total) {
resolve(result);
return;
}
// 并行任务达到上限 或者 任务列表没有任务了
if (running >= concurrency || !arr.length) {
return;
}
const fn = arr.shift();
fn().then(rs => {
result.push(rs);
running -= 1;
pick();
});
running += 1;
pick();
};
pick();
});
}
// test
const start = Date.now();
const tasks = Array(10).fill(0).map((_, index) => {
const fn = () => new Promise((resolve) => {
const idx = index + 1;
setTimeout(() => {
console.log('run', idx, Date.now() - start);
resolve(idx);
}, 2000);
});
return fn;
});
promiseController(tasks).then((res) => {
console.log('run', res);
});
3、 指定范围的随机数
random.js
const rand = (min, max) => {
return parseInt(Math.random()*(max - min + 1) + min, 10);
}
// 100以内的随机数
rand(1, 100);
4、 从数组随机提取元素
randomPickItemFromArray.js
const items = ["apple", "banana", "orange", "pear", "grape"];
const randomPickFromArray = (array) => {
const rand = (min, max) => {
return parseInt(Math.random()*(max - min + 1) + min, 10);
};
return array[rand(0, array.length-1)];
}
console.log(randomPickFromArray(items))
5、 随机字符串
randomString.js
const rand = (min, max) => {
return parseInt(Math.random() * (max - min + 1) + min, 10);
};
const getRandStr = (len = 12) => {
const base = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
const max = base.length - 1;
return Array(len).fill().map((_, idx) => base[rand(idx === 0 ? 10 : 0, max)]).join('');
};
getRandStr(32);
6、 异步加载 JS 文件
loadScript.js
function loadScript(url, props) {
return new Promise((resolve) => {
const script = document.createElement('script');
if (script.readyState) {
script.onreadystatechange = function () {
if (script.readyState == 'loaded' || this.readyState == 'complete') {
resolve();
}
};
} else {
script.onload = () => {
resolve();
};
}
script.src = url;
Object.keys(props || {}).forEach((key) => {
script.setAttribute(key, props[key]);
});
document.head.appendChild(script);
});
}
7、 检查元素是否可见
checkVisible.js
function checkVisible(elem, areaNode) {
let viewport;
if (areaNode) {
viewport = areaNode.getBoundingClientRect();
} else {
viewport = {
top: window.scrollY,
left: window.scrollX,
right: 0,
bottom: 0
};
viewport.right = viewport.left + window.innerWidth;
viewport.bottom = viewport.top + window.innerHeight;
}
const width = elem.offsetWidth;
const height = elem.offsetHeight;
const bodyRect = document.body.getBoundingClientRect();
const elemRect = { ...elem.getBoundingClientRect() };
elemRect.bottom = window.innerHeight - elemRect.top - elemRect.height;
const bounds = {
top: elemRect.top - bodyRect.top,
right: 0,
bottom: 0,
left: elemRect.left - bodyRect.left
};
bounds.right = bounds.left + width;
bounds.bottom = bounds.top + height;
return !(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom);
}
8、 获取元素在屏幕里的坐标
getElementPosition.js
const getElementPosition = (o) => {
let x = 0;
let y = 0;
while (o.offsetParent) {
x += o.offsetLeft;
y += o.offsetTop;
o = o.offsetParent;
}
return {'x':x,'y':y};
}
9、 获取 URL 参数
getUrlParam.js
function getURLParams(key, url = window?.location?.href) {
try {
const r = (((url || '').split('?')[1] || '').split('#')[0] || '').match(new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i'));
if (r != null) {
return decodeURIComponent(r[2]);
}
return '';
} catch (error) {
return '';
}
}
10、 获取页面选中区域内容
getSelectionText.js
const getSelectionText = () => {
try {
const selecter = window.getSelection().toString();
if (selecter != null && selecter.trim() != '') {
return selecter;
}
} catch (err) {
const selecter = document.selection.createRange();
const s = selecter.text;
if (s != null && s.trim() != '') {
return s;
}
}
}
11、 将字符串下载为文件
downloadText.js
function downloadText(text, fileName) {
const dataURL = `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`;
const a = document.createElement('a');
const filename = fileName || `s_${+new Date()}.md`;
a.href = dataURL;
a.setAttribute('download', filename);
a.innerHTML = 'downloading...';
a.style.display = 'none';
document.body.appendChild(a);
setTimeout(() => {
a.click();
document.body.removeChild(a);
}, 66);
}
downloadText('demo-text', 'test.js');
12、 base64 转 file
base64ToFile.js
const base64ToFile = (dataurl, filename) => {
const arr = dataurl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n) {
n -= 1;
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
};
13、 XML 转 JSON
xmlToJson.js
function xmlToJson(xml, obj) {
obj = obj || {};
if (xml && xml.childNodes && xml.childNodes.length) {
var childs = xml.childNodes,
len = xml.childNodes.length;
var item;
if (len === 1 && (childs[0].nodeType === 3 || childs[0].nodeType === 4)) {
if (isArray(obj)) {
obj.push(childs[0].nodeValue);
} else {
obj = childs[0].nodeValue;
}
} else {
for (var i = 0; i < len; i++) {
item = childs[i];
if (item.nodeType === 1) {
if (obj.hasOwnProperty(item.nodeName) &&
!isArray(obj[item.nodeName])) {
obj[item.nodeName] = [obj[item.nodeName]];
}
if (isArray(obj[item.nodeName])) {
obj[item.nodeName].push(xml2json(item, {}));
} else {
obj[item.nodeName] = xml2json(item, {});
}
}
}
}
} else {
obj = xml.nodeValue || '';
}
function isArray(a) {
if (typeof a === 'object' && a.constructor === Array) return true;
return false;
}
return obj
}
14、 页面标题转 URL 格式
titleToUrl.js
const slugify = (string, separator = "-") => {
return string
.toString() // Cast to string (optional)
.toLowerCase() // Convert the string to lowercase letters
.trim() // Remove whitespace from both sides of a string (optional)
.replace(/\s+/g, separator) // Replace spaces with -
.replace(/[^\w\-]+/g, "") // Remove all non-word chars
.replace(/\_/g, separator) // Replace _ with -
.replace(/\-\-+/g, separator) // Replace multiple - with single -
.replace(/\-$/g, ""); // Remove trailing -
};
slugify("Hello, World!");
// Expected output: "hello-world"
slugify("Hello, Universe!", "_");
// Expected output: "hello_universe"
15、 Promise线性队列
promiseQueue.js
const removeOneFromArray = (v, array) => array.filter(item => item !== v);
const promiseFormation = (promiseList, max = 1, options) => {
let running = [];
let tasks = promiseList.slice(0);
let isSuccess = true;
const result = [];
const next = (resolve, reject) => {
// tasks all done
const isTaskFinish = tasks.length === 0 && running.length === 0;
const isBreakOff = options && options.stop;
if (isTaskFinish || isBreakOff) {
if (isBreakOff || !isSuccess) {
reject(result);
}
else {
resolve(result);
}
}
// nextTick
else {
const availableNumber = max - running.length;
if (availableNumber > 0) {
tasks.slice(0, availableNumber).forEach((task) => {
const index = promiseList.indexOf(task);
tasks = removeOneFromArray(task, tasks);
running.push(task);
task()
.then((res) => {
if (typeof result[index] === "undefined") {
result[index] = res;
}
running = removeOneFromArray(task, running);
next(resolve, reject);
})
.catch((err) => {
isSuccess = false;
running = removeOneFromArray(task, running);
if (typeof result[index] === "undefined") {
result[index] = err;
}
next(resolve, reject);
});
});
}
}
};
return new Promise((resolve, reject) => {
if (options && !options.addOne) {
// eslint-disable-next-line no-param-reassign
options.addOne = (fn) => {
promiseList.push(fn);
tasks.push(fn);
next(resolve, reject);
};
}
// stop current promise and skip next
if (options && !options.stopOne) {
// eslint-disable-next-line no-param-reassign
options.stopOne = (taskId) => {
const runningIndex = running.map(item => item.id).indexOf(taskId);
// no task
if (runningIndex === -1) {
const taskIdx = tasks.map((item) => item.id).indexOf(taskId);
if (taskIdx > -1) {
const tsk = tasks[taskIdx];
tasks = removeOneFromArray(tsk, tasks);
const rIndex = promiseList
.map((item) => item.id)
.indexOf(taskId);
result[rIndex] = new Error("cancel");
}
return;
}
const taskIndex = promiseList
.map((item) => item.id)
.indexOf(taskId);
const task = running[runningIndex];
running = removeOneFromArray(task, running);
result[taskIndex] = new Error("cancel");
next(resolve, reject);
};
}
// start
next(resolve, reject);
});
};
用法示例:
const p1 = () =>
new Promise(resolve => {
setTimeout(() => {
console.log(1);
resolve("A");
}, 3);
});
const p2 = () =>
new Promise(resolve => {
setTimeout(() => {
console.log(2);
resolve("B");
}, 2);
});
const p3 = () =>
new Promise(resolve => {
setTimeout(() => {
console.log(3);
resolve("C");
}, 1);
});
promiseFormation([p1, p2, p3]).then(rs => {
console.log(rs);
// 1
// 2
// 3
// ["A", "B", "C"]
});
16、 手写一个promise all
function myPromiseAll(iterable) {
return new Promise((resolve,reject) => {
const promises = Array.from(iterable);
// 定义Promise对象resolve的数组
const result = [];
// 定义一个计数器用来判断是否所有的promise执行完毕
let count = 0;
// 并发执行每一个promise
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(res => {
result[i] = res;
count++;
if (count === promises.length) {
resolve(result);
}
}).catch(err => reject(err))
}
})
}