# 自定义封装方法
# 洗牌函数
随机获取给定区间的值
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min)
}
洗牌函数 随机改变数组
function shuffle(arr) {
let _arr = arr.slice()
for (let i =0; i < arr.length; i++) {
let j = getRandomInt(0, i)
let t = _arr[i]
_arr[i] = _arr[j]
_arr[j] = t
}
return _arr
}
# js给元素设置兼容前缀
let elementStyle = document.createElement('div').style
let vendor = (() => {
let transformNames = {
webkit: 'webkitTransform',
Moz: 'MozTransform',
O: 'OTransform',
ms: 'msTransform',
standard: 'transform'
}
for (let key in transformNames) {
if (elementStyle[transformNames[key]] !== undefined) {
return key
}
}
return false
})()
export function prefixStyle(style) {
if (vendor === false) {
return false
}
if (vendor === 'standard') {
return style
}
return vendor + style.charAt(0).toUpperCase() + style.substr(1)
}
# css 一些操作
// 判断是否有这个类名
export function hasClass(el, className) {
let reg = new RegExp('(^|\\s)' + className + '(\\s|$)')
return reg.test(el.className)
}
// 添加类名
export function addClass(el, className) {
if (hasClass(el, className)) {
return
}
let newClass = el.className.split(' ')
newClass.push(className)
el.className = newClass.join(' ')
}
// 设置或者获取data-
export function getData(el, name, val) {
const prefix = 'data-'
if (val) {
return el.setAttribute(prefix + name, val)
}
return el.getAttribute(prefix + name)
}
# list转为tree
/**
* 以下数据结构中,id 代表部门编号,name 是部门名称,parentId 是父部门编号,为 0 代表一级部门,现在要求实现一个 convert 方法,把原始 list 转换成树形结构,parentId 为多少就挂载在该 id 的属性 children 数组下,结构如下:
// 原始 list 如下
let list =[
{id:1,name:'部门A',parentId:0},
{id:2,name:'部门B',parentId:0},
{id:3,name:'部门C',parentId:1},
{id:4,name:'部门D',parentId:1},
{id:5,name:'部门E',parentId:2},
{id:6,name:'部门F',parentId:3},
{id:7,name:'部门G',parentId:2},
{id:8,name:'部门H',parentId:4}
];
const result = convert(list, ...);
// 转换后的结果如下
let result = [
{
id: 1,
name: '部门A',
parentId: 0,
children: [
{
id: 3,
name: '部门C',
parentId: 1,
children: [
{
id: 6,
name: '部门F',
parentId: 3
}, {
id: 16,
name: '部门L',
parentId: 3
}
]
},
{
id: 4,
name: '部门D',
parentId: 1,
children: [
{
id: 8,
name: '部门H',
parentId: 4
}
]
}
]
},
···
];
*/
function convert(list) {
const res = []
const map = list.reduce((res, v) => (res[v.id] = v, res), {})
for (const item of list) {
if (item.parentId === 0) {
res.push(item)
continue
}
if (item.parentId in map) {
const parent = map[item.parentId]
parent.children = parent.children || []
parent.children.push(item)
}
}
return res
}
let list = [
{id:1,name:'部门A',parentId:0},
{id:2,name:'部门B',parentId:0},
{id:3,name:'部门C',parentId:1},
{id:4,name:'部门D',parentId:1},
{id:5,name:'部门E',parentId:2},
{id:6,name:'部门F',parentId:3},
{id:7,name:'部门G',parentId:2},
{id:8,name:'部门H',parentId:4}
]
console.log(convert(list))
# 函数柯里化
/**
* 函数柯里化实现
*
*/
function currying(fn, length) {
length = length || fn.length // 第一次调用函数fn参数的长度, 后续调用获取fn剩余参数的长度
return function (...args) {
console.log(args.length, length)
return args.length >= length
? fn.apply(this, args)
: currying(fn.bind(this, ...args), length - args.length)
}
}
// es6 简化版
const simpleCurrying = fn =>
judge = (...args) =>
args.length >= fn.length
? fn(...args)
: (...arg) => judge(...args, ...arg)
// test
const fn = currying(function(a, b, c) {
console.log([a, b, c])
})
// fn("a", "b", "c")
// fn("a", "b")("c")
// fn("a")("b")("c")
const f1 = simpleCurrying((a, b, c) =>
console.log([a, b, c])
)
f1("a", "b", "c")
f1("a")("b", "c")
f1("a", "c")("b")
f1("a")("b")("c")
# 自定义new
/**
* 实现new的思路:
* 1. 首先创建一个空的对象,空对象的_proto_属性指向构造函数的原型对象
* 2. 把上面的空对象赋值构造函数内部的this,用构造函数内部的方法修改空对象
* 3. 如果构造函数返回一个非基本类型的值,则返回这个值, 否则返回上面创建的对象
*/
function _new (fn, ...arg) {
const obj = Object.create(fn.prototype)
const ret = fn.apply(obj, arg)
return ret instanceof Object ? ret : obj
}
// test
let Dog = function(name) {
this.name = name
}
Dog.prototype.bark = function() {
console.log('bark')
}
Dog.prototype.sayName = function () {
console.log(`My name is ${this.name}`)
}
let dog = _new(Dog, 'simao')
dog.bark()
dog.sayName()
console.log(dog instanceof Dog) // true
# 自定义实现call
Function.prototype.myCall = function (ctx) {
let context = ctx || window
// getValue.call(a, 'yck', '24') => a.fn = getValue
context.fn = this
// 取出后面的参数
let args = [...arguments].slice(1)
// getValue.call(a, 'yck', '24') => a.fn('yck', 24)
let result = context.fn(...args)
// 删除 fn
delete context.fn
return result
}
let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.call(a, 'yck', '24')
getValue.myCall(a, 'xx', 25)
# 自定义实现apply
Function.prototype.myApply = function (ctx) {
let context = ctx || window
context.fn = this
let result
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}
// test
let a = {
value: 1
}
function getValue(name, age) {
console.log(name)
console.log(age)
console.log(this.value)
}
getValue.apply(a, ['yck', '24'])
getValue.myApply(a, ['yck', '24'])
# 自定义实现bind
Function.prototype.myBind = function (ctx) {
if (typeof this !== 'function') {
throw new TypeError('Error')
}
let _this = this
let args = [...arguments].slice(1)
// 返回一个函数
return function F() {
console.log(this, F, this instanceof F)
// 进行类型判断
if (this instanceof F) {
return new _this(...args, ...arguments)
}
return _this.apply(ctx, args.concat(...arguments))
}
}
a = {
name: 'xx'
}
name = 'aa'
function test(arg) {
console.log(this.name, arg)
}
test(111)
test.bind(a, 'ssss')()
test.myBind(a, 'qqqq')()
new test('11').bind
# 实现深拷贝
function deepClone (obj) {
if (obj === null) return null
if (obj instanceof RegExp) return new RegExp(obj)
if (obj instanceof Date) return new Date(obj)
if (typeof obj !== 'object') return obj
let t = new obj.constructor()
for (let key in obj) {
t[key] = deepClone(obj[key]) // 递归
}
return t
}
# 判断pc端
function isPC() {
let userAgentInfo = navigator.userAgent
let Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
let flag = true
for (let v = 0, agent; agent = Agents[v++]) {
if (userAgentInfo.indexOf(agent) > 0) {
flag = false
break
}
}
return flag
}
# 判断手机类型
function phoneType() {
let u = navigator.userAgent
if (u.indexOf('Android') > -1 || u.indexOf('Linux') > -1 ) { // 安卓
return 'Android'
} else if (u.indexOf('iPhone') > -1) { // 苹果
return 'iPhone'
} else if (u.indexOf('iPad') > -1) { // iPad
return 'iPad'
} else if (u.indexOf('Windows Phone') > -1) { // winphone 手机
return 'Windows Phone'
} else { // 其它
return 'other'
}
}
# 判断浏览器类型
function browserType(){
let userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
let isOpera = userAgent.indexOf("Opera") > -1; //判断是否Opera浏览器
let isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera; //判断是否IE浏览器
let isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf("rv:11.0") > -1;
let isEdge = userAgent.indexOf("Edge") > -1 && !isIE; //判断是否IE的Edge浏览器
let isFF = userAgent.indexOf("Firefox") > -1; //判断是否Firefox浏览器
let isSafari = userAgent.indexOf("Safari") > -1 && userAgent.indexOf("Chrome") == -1; //判断是否Safari浏览器
let isChrome = userAgent.indexOf("Chrome") > -1 && userAgent.indexOf("Safari") > -1; //判断Chrome浏览器
if (isIE) {
let reIE = new RegExp("MSIE (\\d+\\.\\d+);");
reIE.test(userAgent);
let fIEVersion = parseFloat(RegExp["$1"]);
if(fIEVersion == 7) return "IE7"
else if(fIEVersion == 8) return "IE8";
else if(fIEVersion == 9) return "IE9";
else if(fIEVersion == 10) return "IE10";
else return "IE7以下"//IE版本过低
}
if (isIE11) return 'IE11';
if (isEdge) return "Edge";
if (isFF) return "FF";
if (isOpera) return "Opera";
if (isSafari) return "Safari";
if (isChrome) return "Chrome";
}
# 货币格式化
const digitsRE = /(\d{3})(?=\d)/g
/**
*
* @param {[type]} value [description] 货币值
* @param {[type]} currency [description] 默认为$
* @param {[type]} decimals [description] 小数默认2位
* @return {[type]} [description]
*/
export function currency (value, currency, decimals) {
value = parseFloat(value)
if (!isFinite(value) || (!value && value !== 0)) return ''
currency = currency != null ? currency : '$'
decimals = decimals != null ? decimals : 2
let stringified = Math.abs(value).toFixed(decimals)
let _int = decimals
? stringified.slice(0, -1 - decimals)
: stringified
let i = _int.length % 3
let head = i > 0
? (_int.slice(0, i) + (_int.length > 3 ? ',' : ''))
: ''
let _float = decimals
? stringified.slice(-1 - decimals)
: ''
let sign = value < 0 ? '-' : ''
return sign + currency + head +
_int.slice(i).replace(digitsRE, '$1,') +
_float
}
# 防抖
/**
* 防抖函数,返回函数连续调用时, 空闲时间必须大于或等于 wait, func 才会执行
*
* @param {*} func 回调函数
* @param {number} [wait=50] 时间间隔
* @param {boolean} [immediate=true] 是否立即调用
* @return {function} 返回客户调用函数
*/
function debounce(func, wait = 50, immediate = true) {
let timer, context, args
// 延迟执行函数
const later = () => setTimeout(() => {
// 延迟函数执行完毕, 清空缓存的定时器序号
timer = null
// 延迟执行的情况下,函数会在延迟函数中执行
// 使用到之前缓存的参数和上下文
if (!immediate) {
func.apply(context, args)
context = args = null
}
}, wait)
// 返回函数是每次实际调用的函数
return function (...params) {
// 如果没有创建延迟执行函数
if (!timer) {
timer = later()
// 如果是立即执行函数,调用函数
// 否则缓存参数和调用上下文
if (immediate) {
func.apply(this, params)
} else {
context = this
args = params
}
} else {
// 如果有延迟执行函数, 调用的时候清除原来的并重新设定一个
clearTimeout(timer)
timer = later()
}
}
}
# 节流
// 获取当前时间
function _now() {
return +new Date()
}
/**
*
*
* @param {*} func 回调函数
* @param {*} wait 时间间隔
* @param {*} [options={}] 如果想忽略开始函数的调用, 传入 {leading: false}
* 如果想忽略结尾函数的调用, 传入 { trailing: false}
* 两者不能共存,否则函数不能执行 *
* @returns 返回库户调用函数
*/
function throttle(func, wait, options = {}) {
let context, args, result
let timeout = null
// 之前的时间戳
let previous = 0
// 定时器回调函数
const later = () => {
// 如果设置了leading, 就将previous 设为0
// 用于下面函数的第一个 if 判断
previous = options.leading === false ? 0 : _now()
timeout = null
result = func.apply(context, args)
if (!timeout) context = args = null
}
return function (...args) {
// 获取当前时间戳
let now = _now()
// 首次进入前者肯定为true
// 如果需要第一次不执行函数
// 就将上次时间戳设为当前的
// 这样在接下来计算remaining 的值是会大于0
if (!previous && options.leading === false) previous = now
// 计算剩余时间
let remaining = wait - (now - previous)
console.log('r', remaining)
context = this
args = args
// 如果当前调用已经大于上次调用时间 + wait
// 或者用户手动调了时间
// 如果设置了 trailing, 只会进入这个条件
// 如果没有设置 leading, 那么第一次会进入这个条件
// 还有一点, 你可能觉得开启了定时器 那么应该就不会进入这个if 条件了
// 其实还是会进入的 因为定时器的延时
// 并不是准确的时间, 很可能你设置了2秒
// 但是她需要2.2秒才触发, 这时候就会进入这个条件
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
result = func.apply(context, args)
if (!timeout) context = args = null
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
console.log('re', result)
return result
}
}
# 时间格式化
let SIGN_REGEXP = /([yMdhsm])(\1*)/g;
let DEFAULT_PATTERN = 'yyyy-MM-dd';
function padding(s, len) {
let len = len - (s + '').length;
for (var i = 0; i < len; i++) { s = '0' + s; }
return s;
};
export default {
formatDate: {
format: function (date, pattern) {
pattern = pattern || DEFAULT_PATTERN;
return pattern.replace(SIGN_REGEXP, function ($0) {
switch ($0.charAt(0)) {
case 'y': return padding(date.getFullYear(), $0.length);
case 'M': return padding(date.getMonth() + 1, $0.length);
case 'd': return padding(date.getDate(), $0.length);
case 'w': return date.getDay() + 1;
case 'h': return padding(date.getHours(), $0.length);
case 'm': return padding(date.getMinutes(), $0.length);
case 's': return padding(date.getSeconds(), $0.length);
}
});
},
parse: function (dateString, pattern) {
let matchs1 = pattern.match(SIGN_REGEXP);
let matchs2 = dateString.match(/(\d)+/g);
if (matchs1.length == matchs2.length) {
let _date = new Date(1970, 0, 1);
for (let i = 0; i < matchs1.length; i++) {
let _int = parseInt(matchs2[i]);
let sign = matchs1[i];
switch (sign.charAt(0)) {
case 'y': _date.setFullYear(_int); break;
case 'M': _date.setMonth(_int - 1); break;
case 'd': _date.setDate(_int); break;
case 'h': _date.setHours(_int); break;
case 'm': _date.setMinutes(_int); break;
case 's': _date.setSeconds(_int); break;
}
}
return _date;
}
return null;
}
}
}
# 查询地址栏的参数
function getQueryStringByName (name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
let r = window.location.search.substr(1).match(reg);
let context = "";
if (r != null) context = r[2];
reg = null;
r = null;
return context == null || context == "" || context == "undefined" ? "" : context;
}