0%

JS工具箱

该文章不会再更新 目前已迁移至 https://daixiongsheng.github.io/utils/

url 相关

将对象转成 query 串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

/**
* 将对象转成query串
* @example object2QueryString({age:123}) => age=123
* @param o
*/
function object2QueryString(o) {
let s = '';
Object.keys(o)
.forEach(key => {
const value = o[key];
const type = typeof value;
switch (type) {
case 'number':
// no NaN
if (value === value) {
s += `${key}=${value}&`;
}
break;
case 'object':
if (value !== null) {
s += `${key}=${encodeURIComponent(object2QueryString(value))}&`;
}
break;
case 'boolean':
s += `${key}=${value}&`;
break;
case 'string':
s += `${key}=${encodeURIComponent(value)}&`;
break;
default:
break;
}
});
return s.substr(0, s.length && s.length - 1);
}

url 拼接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 用于处理url参数传递需要手动拼接字符串的问题
*
* @param {string} path pathname
* @param {object} pramsObj 传给url的参数对象
* @return {string} 返回拼接后的url串
*/
function dealPath(path, pramsObject = {}) {
if (!path || path.length === 1 || Object.keys(pramsObject).length === 0) {
return path;
}
if (path[0] !== '/' && !path.includes('//')) {
path = `/${path}`;
}
// 是否已经经过处理
const isDealt = path.indexOf('?') !== -1;
if (path[path.length - 1] !== '/' && !isDealt) {
path = `${path}/`;
}
path = isDealt ? `${path}&` : `${path}?`;
const queryString = object2QueryString(pramsObject);
path += queryString ? queryString + '&' : '';
return path.substr(0, path.length - 1);
}

解析 query 串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
*
* @param queryString {string} 需要解析的url query 串
* @param decode {boolean} 是否使用encodeURIComponent 进行解码
* @return {Object}
*/
function query2Obj(queryString = '', decode = false) {
if (queryString[0] === '?') {
queryString = queryString.slice(1);
}
const o = Object.create(null);
const qArr = queryString.split('&');
qArr.forEach(item => {
const [k, v] = item.split('=');
o[k] = v === void 0 ? '' : decode ? decodeURIComponent(v) : v;
});
return o;
}

处理参数对象的类型,默认的 url 里面的参数解析后是字符串类型,改方法将进行一些可能类型的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

/**
* 处理参数对象的类型,默认的url里面的参数解析后是字符串类型,改方法将进行一些可能类型的转换
* @param object
* @example dealQueryObject({a:'3',b:'4',c:'null'}) => {a:3,b:4,c:null}
* @return {any}
*/
function dealQueryObject(object) {
const o = makeHash();
Object.keys(object)
.forEach(key => {
const value = object[key];
switch (value) {
case 'null':
o[key] = null;
return;
case 'false':
o[key] = false;
return;
case 'true':
o[key] = true;
return;
case 'undefined':
o[key] = void 0;
return;
default:
break;
}
// value是number
if (parseInt(value, 10)
.toString() === value) {
o[key] = Number(value);
return;
}
try {
const result = JSON.parse(value);
o[key] = dealQueryObject(result);
}
catch (e) {
o[key] = value;
}
});
return o;
}

解析 URL

1
2
3
4
5
6
7
8
9
10
11
12

/**
*
* @param url {string} url 需要解析的url
* @param decode {boolean} 是否使用encodeURIComponent 进行解码
* @example parseUrl('https://a.b.com?a=123&b=456') => {a:'123',b:'456'}
* @return {Object}
*/
function parseUrl(url = '', decode = false) {
const queryString = url.split('?')[1];
return query2Obj(queryString, decode);
}

将对象转成 query 串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

/**
* 将对象转成query串
* @example object2QueryString({age:123}) => age=123
* @param o
*/
function object2QueryString(o) {
let s = '';
Object.keys(o)
.forEach(key => {
const value = o[key];
const type = typeof value;
switch (type) {
case 'number':
// no NaN
if (value === value) {
s += `${key}=${value}&`;
}
break;
case 'object':
if (value !== null) {
s += `${key}=${encodeURIComponent(object2QueryString(value))}&`;
}
break;
case 'boolean':
s += `${key}=${value}&`;
break;
case 'string':
s += `${key}=${encodeURIComponent(value)}&`;
break;
default:
break;
}
});
return s.substr(0, s.length && s.length - 1);
}

类型转换

将数字转换成二进制串

1
2
3
4
5
6
7
8
9
function num2Bin(num) {
if (typeof num !== 'number') throw Error("param must be a number")
let s = ''
while (num > 0) {
s = num % 2 + s
num = Math.floor(num / 2)
}
return s
}

对象相关

将对象的值为 空串,null,undefined, NaN 进行踢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 将对象的值为 空串,null,undefined, NaN 进行踢出
* @example pureObject({name:'',age:null, a:123,d:undefined,e:NaN}) => {a:123}
* @param object
*/
function pureObject(object) {
const o = makeHash();
if (toString(object) === '[object Array]' || typeof object !== 'object' || object === null) {
return object;
}
Object.keys(object)
.forEach(key => {
const value = object[key];
const type = typeof value;
switch (type) {
case 'boolean':
case 'number':
o[key] = value;
break;
case 'object':
if (value !== null) {
const v = pureObject(value);
if (Object.keys(v).length) {
o[key] = pureObject(value);
}
}
break;
case 'string':
if (value) {
o[key] = value;
}
break;
default:
break;
}
});
return o;
}

深拷贝, 解决循环引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
function deepCopy(obj, map = new Map()) {
let copy
switch (typeof obj) {
case 'undefined':
break;
case 'number':
case 'boolean':
copy = obj
break;
case 'string':
copy = obj + ''
break;
case 'bigint':
copy = BigInt(obj)
break;
case 'object':
if (obj === null)
copy = null
else {
const clone = map.get(obj)
if (clone) {
return clone
}
// new Boolean,Number 不处理
if (Object.prototype.toString.call(obj) === '[object Array]') {
const len = obj.length;
copy = new Array(len)
map.set(obj, copy)
for (let i = 0; i < len; i++) {
copy[i] = deepCopy(obj[i], map)
}
}
else {
copy = {}
map.set(obj, copy)
for(const k in obj) {
obj.hasOwnProperty(k) && (copy[k] = deepCopy(obj[k], map))
}
}
}
break;
case 'symbol':
copy = Symbol(obj.description)
break;
case 'function':
const str = obj.toString()
if (!str.includes('native code')) {
copy = Function(str)
}
break
}
return copy
}

自定义 typeOf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 自定义判断类型函数
*
* @export
* @param {*} value
* @return {*} {string}
*/
export function typeOf(value: any): string {
const type = typeof value
switch (type) {
case 'string':
case 'boolean':
case 'bigint':
case 'number':
case 'symbol':
case 'function':
case 'undefined':
/* eslint-disable no-self-compare */
if (value !== value) {
return 'NaN'
}
return type
case 'object':
if (value === null) {
return 'null'
}
if (isObject(value)) {
return 'object'
}
if (Array.isArray(value)) {
return 'array'
}
}
return ''
}

深度比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 深度比较函数,不能比较有循环引用的对象
*
* @export
* @param {*} value
* @param {*} other
* @return {*} {boolean}
*/
export function deepEqual(value: any, other: any): boolean {
if (value === other) {
return true
}
const typeA = typeOf(value)
const typeB = typeOf(other)
if (typeA !== typeB) {
return false
}
switch (typeA) {
case 'Array':
if (value.length !== other.length) {
return false
}
for (let i = 0, len = value.length; i < len; i++) {
if (!deepEqual(value[i], other[i])) {
return false
}
}
return true
case 'object':
for (const key of Object.keys(value)) {
if (!deepEqual(value[key], other[key])) {
return false
}
}
return true
}
return value === other
}

判断是不是有数据的对象

1
2
3
4
5
6
7
8
9
10
/**
* 判断是不是一个有内容的对象
*
* @export
* @param {object} value
* @return {*} {boolean}
*/
export function isValidObject(value: object): boolean {
return value && typeof value === 'object' && Object.keys(value).length !== 0
}

洗牌算法(数组乱序算法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 洗牌算法(数组乱序算法)
* @param {Array} shuffleArray
* @returns
*/
export function shuffle<T = any>(shuffleArray: T[]): T[] {
for (let i = shuffleArray.length - 1; i >= 0; i--) {
let ri = Math.floor(Math.random() * (i + 1))
let temp = shuffleArray[ri]
shuffleArray[ri] = shuffleArray[i]
shuffleArray[i] = temp
}
return shuffleArray
}

产生随机数

1
2
3
4
5
6
7
8
9
10
11
/**
* 获取指定范围的随机整数
* @param lowerValue 最小值
* @param upperValue 最大值
*/
export function getRandomValue(
lowerValue: number = 0,
upperValue: number = 100
) {
return Math.ceil(Math.random() * (upperValue - lowerValue) + lowerValue)
}

DOM,浏览器相关

获取 XPath

1
2
3
4
5
6
7
8
9
10
11
12
function getXPath(element) {
if (element === document.body) return '/html/' + element.tagName.toLowerCase();
let idx = 1,
silibings = element.parentNode.childNodes;
for (const e of silibings) {
if (e === element)
return `${arguments.callee(element.parentNode)}/${element.tagName.toLowerCase()}[${idx}]`
else if (e.nodeType === 1 && e.tagName === element.tagName) {
idx++
}
}
};

使元素可以拖动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function dragAble(obj) {
obj.onmousedown = function(event) {
obj.setCapture && obj.setCapture();
let ol = event.clientX - obj.offsetLeft;
let ot = event.clientY - obj.offsetTop;

event = event || window.event;
document.onmousemove = function(event) {
event = event || window.event;
let left = event.clientX - ol;
let top = event.clientY - ot;

obj.style.position = "absolute";
obj.style.top = top + "px";
obj.style.left = left + "px";
};
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
obj.releaseCapture && obj.releaseCapture();
};
return false;
};
}

获取鼠标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getMouse(ele) {
let mouse = { x: 0, y: 0 }
window.addEventListener('mousemove', function (event) {
let x, y
let e = event || window.event
if (e.pageX || e.pageY) {
x = e.pageX
y = e.pageY
} else {
x = e.clientX + document.body.scrollLeft ||
document.documentElement.scrollLeft
y = e.clientY + document.body.scrollTop ||
document.documentElement.scrollTop
}
x -= ele.offsetLeft
y -= ele.offsetTop
mouse.x = x
mouse.y = y
})
return mouse
}

原生 ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function ajax(url, options = {}) {
return new Promise((resolve, reject) => {
if (typeof url === 'object') {
options = null
options = url
url = options.url
}
let xmlHttp
if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest()
}
else {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP")
}
const
method = options.method || 'GET',
async = true,
data = options.data || {},
headers = options.headers || {
"Contnet-type": "application/x-www-form-urlencoded"
}

xmlHttp.open(method, url, async)
Object.keys(headers).forEach(key => {
xmlHttp.setRequestHeader(
key, headers[key]
)
})

xmlHttp.send(data)
xmlHttp.onreadystatechange = function (event) {
if (xmlHttp.readyState === 4) {
if (xmlHttp.status >= 200 && xmlHttp.status < 300 || xmlHttp.status === 304) {
resolve(xmlHttp)
}
else {
reject(xmlHttp)
}
}
}
})
}

剪贴板相关

读取剪贴板(需要 API 支持)

1
2
3
4
5
6
7
8
9
async function readClipboard(event) {
event.preventDefault();
try {
const text = await navigator.clipboard.readText();
console.log('Pasted content: ', text);
} catch (err) {
console.error('Failed to read clipboard contents: ', err);
}
}

复制到剪贴板

1
2
3
4
5
6
7
8
9
// 需要dom调用
async function copy2clipboard(e, copyText) {
try {
await navigator.clipboard.writeText(copyText);
console.log(`${copyText} copied to clipboard`);
} catch (err) {
console.error('Failed to copy: ', err);
}
}

复制内容到剪贴板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const copy2clipboard = (text) => {
const container = document.body
const fakeElem = document.createElement('textarea')
fakeElem.style.fontSize = '12px'
fakeElem.style.border = '0'
fakeElem.style.padding = '0'
fakeElem.style.margin = '0'
fakeElem.style.position = 'absolute'
fakeElem.style.top = '-1000px'
fakeElem.style.left = '-1000px'
fakeElem.value = text
container.appendChild(fakeElem)
fakeElem.select()
fakeElem.setSelectionRange(0, fakeElem.value.length)
document.execCommand('copy')
container.removeChild(fakeElem)
}

其他

返回这一天是周几

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const dayOfTheWeek =  (day, month, year) => {
// 基姆拉尔森计算公式
const arr = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday',
]
if (month < 3) {
month += 12
--year
}
// w 为 0-6
const w =
((day +
2 * month +
(3 * (month + 1)) / 5 +
year +
((year / 4) >>> 0) -
((year / 100) >>> 0) +
((year / 400) >>> 0) +
1) >>>
0) %
7
return arr[w]
}

求根号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function sqrt(m) {
if (m === 1) return 1
let low = 0,
high = m
let mid = low + (high - low) / 2
while (Math.abs(mid * mid - m) > 1e-9) {
let mid2 = mid * mid
if (mid2 > m) {
high = mid
}
if (mid2 < m) {
low = mid
}
mid = low + (high - low) / 2
}
return mid
}

加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
const Lock = {
isLocked(key) {
return Boolean(this[`$$${key}`]);
},
unlock(key) {
if (`$$${key}` in this) {
delete this[`$$${key}`];
}
},
lock(key) {
this[`$$${key}`] = true;
}
};

深比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function deepEqual(value: any, other: any): boolean {
if (value === other) {
return true;
}
const typeA = typeOf(value);
const typeB = typeOf(other);
if (typeA !== typeB) {
return false;
}
switch (typeA) {
case 'array':
if (value.length !== other.length) {
return false;
}
for (let i = 0, len = value.length; i < len; i++) {
if (!deepEqual(value[i], other[i])) {
return false;
}
}
return true;
case 'object':
for (const key of Object.keys(value)) {
if (!deepEqual(value[key], other[key])) {
return false;
}
}
return true;
}
return value === other;
}

字节单位转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

/**
* 将字节数转成文本形式 1024 --> 1KB
*
* @export
* @param {number} bytes
* @return {*} {string}
*/
function byteConvert(bytes: number): string {
let ret = '';
const symbols = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
let exp = Math.floor(Math.log(bytes) / Math.log(2));
if (exp < 1) {
exp = 0;
}
const i = Math.floor(exp / 10);
ret = `${bytes / Math.pow(2, 10 * i)}`;

if (bytes.toString().length > bytes.toFixed(2).toString().length) {
ret = bytes.toFixed(2);
}
return ret + symbols[i];
}

/**
* 1KB -> 1024
*
* @export
* @param {string} size
* @return {*} {(number | string)}
*/
function unitConversion(size: string): number {
const symbols = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const length = parseInt(size as string, 10);
const unit = (size as string).substring(length.toString().length).toUpperCase();
const index = symbols.findIndex(i => i === unit);
return length * Math.pow(2, 10 * index);
}