# 常用方法
# 常用判断
function isString(obj) {
return Object.prototype.toString.call(obj) === '[object String]'
}
function isObject(obj) {
return Object.prototype.toString.call(obj) === '[object Object]'
}
function isHtmlElement(node) {
return node instanceof HTMLElement
}
function isIE() {
return !isNaN(Number(document.documentMode))
};
function isEdge() {
return navigator.userAgent.indexOf('Edge') > -1
};
function isFirefox() {
return !!window.navigator.userAgent.match(/firefox/i)
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 首字母大写
function capitalize(str) {
str = String(str)
return str.charAt(0).toUpperCase() + str.slice(1)
}
1
2
3
4
2
3
4
# 事件封装(兼容IE5-8)
function on(element, event, handler, capture = false) {
if (element && event && handler) {
if (document.addEventListener) {
element.addEventListener(event, handler, capture)
} else {
element.attachEvent('on' + event, handler)
}
}
}
function off(element, event, handler, capture = false) {
if (element && event && handler) {
if (document.addEventListener) {
element.removeEventListener(event, handler, capture)
} else {
element.detachEvent('on' + event, handler)
}
}
}
// 直接 addEventListener options 里面配置 once 为 true 也是可以的,只是不兼容IE5-8
function once(el, event, fn) {
const listener = function() {
if (fn) {
fn.apply(this, arguments)
}
off(el, event, listener)
};
on(el, event, listener)
}
function triggerEvent(elm, name, ...opts) {
let eventName;
if (/^mouse|click/.test(name)) {
eventName = 'MouseEvents'
} else if (/^key/.test(name)) {
eventName = 'KeyboardEvent'
} else {
eventName = 'HTMLEvents'
}
const evt = document.createEvent(eventName)
evt.initEvent(name, ...opts)
elm.dispatchEvent
? elm.dispatchEvent(evt)
: elm.fireEvent('on' + name, evt)
return elm
}
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
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
# class操作
const hasClass = function(obj, cls) {
return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'))
}
const addClass = function(obj, cls) {
if (!hasClass(obj, cls)) obj.className += ' ' + cls
}
const removeClass = function(obj, cls) {
if (hasClass(obj, cls)) {
const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)')
obj.className = obj.className.replace(reg, ' ')
}
}
const toggleClass = function(obj, cls) {
if (hasClass(obj, cls)) {
removeClass(obj, cls)
} else {
addClass(obj, cls)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# cookie
function setCookie(cname, cvalue, exdays = 99999) {
const d = new Date()
d.setTime(d.getTime() + (exdays*24*60*60*1000))
const expires = "expires="+ d.toUTCString()
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"
}
function getCookie(cname) {
const name = cname + "=";
const decodedCookie = decodeURIComponent(document.cookie);
const ca = decodedCookie.split(';');
for(let i = 0; i <ca.length; i++) {
let c = ca[i]
while (c.charAt(0) == ' ') {
c = c.substring(1)
}
if (c.indexOf(name) == 0) {
return c.substring(name.length, c.length)
}
}
return "";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 数组去重
/**
* @description 数组去重
* @param {Array} arr 需要被去重的数组
* @return {Array}
*/
function uniqueArr(arr) {
return Array.from(new Set(arr))
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 去抖函数
TIP
下面两个方法功能容易被混淆,这里强调一下
去抖:在小于固定延时
wait
中连续触发时不触发,直到超过延时阈值才会触发,immediate
传参为true
是立刻触发(连续触发无论多久一轮只会触发一次)节流:在小于固定延时
wait
中连续触发时每隔wait
触发一次(连续触发可能触发多次)
/**
* @description 去抖函数
* @param {Function} func 触发的回调函数
* @param {Number} wait 间隔时间(毫秒)
* @param {Boolean} immediate 是否需要立刻执行函数 默认 false
* @return {*}
*/
function debounce(func, wait, immediate) {
let timeout, args, context, timestamp, result
const later = function() {
// 据上一次触发时间间隔
const last = +new Date() - timestamp
// 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last)
} else {
timeout = null
// 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
if (!immediate) {
result = func.apply(context, args)
if (!timeout) context = args = null
}
}
}
return function() {
args = arguments
context = this
timestamp = +new Date()
const callNow = immediate && !timeout
// 如果延时不存在,重新设定延时
if (!timeout) timeout = setTimeout(later, wait)
if (callNow) {
result = func.apply(context, args)
context = args = null
}
return result
}
}
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
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
# 节流函数
/**
* @description 节流函数
* @param {Function} func 触发的回调函数
* @param {Number} wait 间隔时间(毫秒)
* @return {*}
*/
function throttle(func, wait) {
let previous = 0
return function () {
const now = Date.now()
const context = this
const args = arguments
if (now - previous > wait) {
func.apply(context, args)
previous = now
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 格式化时间戳
TIP
用 new Date()
把字符串转化为时间戳的时候
IOS只支持 '2016/06/18 11:11:11',不支持 '2016-06-18 11:11:11' 的转换,安卓双支持
所以需要 time = time.replace(new RegExp(/-/gm), '/')
处理后再统一处理
/**
* @description 格式化时间戳
* @param {String, Number} time 需要格式化时间或者时间戳
* @param {String} cFormat 需要格式化的样式 默认 '{y}-{m}-{d} {h}:{i}:{s}'
* @return {String}
*/
function parseTime(time, cFormat) {
if (arguments.length === 0 || !time) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string')) {
if ((/^[0-9]+$/.test(time))) {
// support "1548221490638"
time = parseInt(time)
} else {
// support safari
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
time = time.replace(new RegExp(/-/gm), '/')
}
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
return value.toString().padStart(2, '0')
})
return time_str
}
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
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
# 千分号格式化数字
/**
* @description 千分号格式化数字
* @param {Number} s 需要被格式化的数字
* @param {Number} n 保留小数的位数 默认2
* @return {String}
*/
function fomatNumber(s, n) {
n = n = 0 && n <= 20 ? n : 2
s = parseFloat((s + "").replace(/[^\d\.-]/g, "")).toFixed(n) + ""
var l = s.split(".")[0].split("").reverse()
r = s.split(".")[1]
t = ""
for (let i = 0; i < l.length; i++)
t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? "," : "")
if (n == 0)
return t.split("").reverse().join("")
else
return t.split("").reverse().join("") + "." + r
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 克隆一个对象或者数组(深拷贝)
TIP
浅拷贝:Object.assign([], arr)
、Object.assign({}, obj)
or [...arr]
、{...obj}
这里我想要强调一下,浅拷贝是复制一层不是直接赋值,面试过很多人对此都有误解。其实大部分场景浅拷贝足矣,无需深拷贝
/**
* @description 克隆一个对象或者数组(深拷贝)
* @param {Object, Array} source 需要被拷贝的对象或者数组
* @return {Object, Array}
*/
function deepClone(source) {
if (typeof source == 'object') {
const targetObj = source.constructor === Array ? [] : {}
Object.keys(source).forEach((keys) = {
if (source[keys] && typeof source[keys] === 'object') {
targetObj[keys] = deepClone(source[keys])
} else {
targetObj[keys] = source[keys]
}
})
return targetObj
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 对象拼接URL传参字符串
/**
* @description 对象拼接URL传参字符串
* @param {Object} json get传参对象
* @return {String}
*/
function obj2Param(json) {
if (!json) return ''
return Object.assign([], Object.keys(json).map(key => {
if (json[key] === undefined) return ''
return encodeURIComponent(key) + '=' +
encodeURIComponent(json[key])
})).join('&')
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 获取URL参数
/**
* @description 获取URL参数
* @param {String} url 需要解析参数的url
* @return {Object}
*/
function param2Obj(url) {
const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ')
if (!search) {
return {}
}
const obj = {}
const searchArr = search.split('&')
searchArr.forEach(v => {
// 避免出现==的入参
const index = v.indexOf('=')
if (index !== -1) {
const name = v.substring(0, index)
const val = v.substring(index + 1, v.length)
obj[name] = val
}
})
return obj
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# key Codes
const keyCodes = Object.freeze({
enter: 13,
tab: 9,
delete: 46,
esc: 27,
space: 32,
up: 38,
down: 40,
left: 37,
right: 39,
end: 35,
home: 36,
del: 46,
backspace: 8,
insert: 45,
pageup: 33,
pagedown: 34,
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 垂直滚动
/**
* @description 垂直滚动
* @param {HTMLElement} element 需要滚动的dom
* @param {Number} to 需要滚动到的位置
* @param {Number} duration 滚动过渡时间(毫秒)
* @return {*}
*/
function scrollTo(element, to, duration) {
if (duration <= 0) return
const difference = to - element.scrollTop
const perTick = difference / (duration / 50)
setTimeout(() => {
element.scrollTop = element.scrollTop + perTick
if (element.scrollTop === to) return
scrollTo(element, to, duration - 50)
}, 50)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ArrayBuffer转Base64
/**
* @description ArrayBuffer转Base64
* @param {Array} buffer byte array
* @return {String}
*/
function transformArrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
for (let len = bytes.byteLength, i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 拼音首字母排序
/**
* @description 拼音首字母排序
* @param {Array} arr 中文汉字组成的数组
* @return {Array}
*/
function sortByPinyin(arr) {
return arr.sort((a, b) => a.localeCompare(b))
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 扁平数据转树数据(已知单根)
/**
* @description 扁平数据转树数据
* @param {Array} items 元素组
* @param {Array} id 根id 默认 null
* @param {Array} link 父id 字段名称 默认 parentId
* @return {Array}
*/
function flat2Tree(items, id = null, link = 'parentId') {
return items
.filter(item => item[link] === id)
.map(item => ({
...item,
children: flat2Tree(items, item.id,link)
}))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
未完待续...