缘起
本篇文章主要总结了自己常用的和整理收集的,在项目中可能常用到的方法,如果觉得有用的话,点个赞鼓励下,谢谢!
本篇文章配有案列对应的测试用例
Url 类
url 相关方法,url参数序列化/URL参数获取
url参数序列化
/**
* URL 参数序列化
* @param { String } baseURL :url地址
* @param { Object } params :参数对象
* @return 参数序列化后的字符串
*/
const ParamSerialize = function (baseURL,params){
return Object.keys(params).reduce((pre,cur)=> pre += ((pre===baseURL? '?':'&')+`${cur}=${params[cur]}`),baseURL)
}
//测试用例
ParamSerialize('www.baidu.com',{name:'',age:19}) //www.baidu.com?name=&age=19
ParamSerialize('www.baidu.com',{name:'wuming',age:''}) //www.baidu.com?name=wuming&age=
ParamSerialize('www.baidu.com',{name:'wuming',age:19}) //www.baidu.com?name=wuming&age=19
ParamSerialize('www.baidu.com',{})//www.baidu.com
URL参数获取
/**
* URL 参数获取
* @param { String } url :url地址 可选,不传则默认值是window.location.href
* @return object
*/
const getUrlParms = function (url){
return (url?url:location.href).includes('?')?(url?url:location.href).split('?')[1].match(/([^&=]*)=([^&]*)/g).reduce((pre,cur)=> {pre[cur.split('=')[0]] = cur.split('=')[1];return pre},{}):{}
}
//测试用例
getUrlParms('www.baidu.com?name=&age=19') //{name: "", age: "19"}
getUrlParms('www.baidu.com?name=wuming&age=') //{name: "wuming", age: ""}
getUrlParms('www.baidu.com?name=wuming&age=19') //{name: "wuming", age: "19"}
getUrlParms('www.baidu.com') //{}
getUrlParms() //当前页面的window.location.href = 'www.baidu.com?name=wuming&age=29',则返回 {name: "wuming", age: "19"}
getUrlParms() //当前页面的window.location.href = 'www.baidu.com',则返回 {}
URL模板格式化
/**
* 请求相关:
*
* @param {String} URL: 模板路径,例:'/uap/msg/announcementRecord/{sysId}/{tenantId}/{userId}' 或 '/uap/msg/announcementRecord'
* @param {Object} params: 传入的参数,包含路径参数 或 不包含
* @param {Boolean} flag:是否拼接路径和查询参数
*
* PS:flag为true时,一定是GET请求,GET请求才会拼接参数到URL后面
*/
function getFormatUrl(URL, params, flag=false) {
let url =/\{(\w+)\}/g.test(URL)?URL.replace(/\{(\w+)\}/g, (a, b) => {let tmp = params[b];delete params[b];return tmp}):URL
return flag? Object.keys(params).reduce((pre, cur) =>(pre += (pre === url ? '?' : '&') + `${cur}=${params[cur]}`), url): url
}
//测试用例
getFormatUrl('/uap/msg/announcementRecord/{sysId}/{tenantId}/{userId}',{
sysId:"001",
tenantId:"002",
userId:"003",
username:'wuming',
age:39,
})
//输出:/uap/msg/announcementRecord/001/002/003
getFormatUrl('/uap/msg/announcementRecord/{sysId}/{tenantId}/{userId}',{
sysId:"001",
tenantId:"002",
userId:"003",
username:'wuming',
age:39,
},true)
//输出:/uap/msg/announcementRecord/001/002/003?username=wuming&age=39
getFormatUrl('/uap/msg/announcementRecord',{}) // 输出:/uap/msg/announcementRecord
getUrl('/api/getUerInfo/{userId}/{roleId}',{userId:12,roleId:33})//输出:api/getUerInfo/12/33
getUrl('/api/getUerInfo/{userId}/{roleId}',{userId:12,roleId:33,extraParam:'555'})//输出:api/getUerInfo/12/33
getUrl('/api/getUerInfo/{userId}/{roleId}',{userId:12,roleId:33,extraParam:'555'},true)//输出:/api/getUerInfo/12/33?extraParam=555
Generater 类
快速生成类,快速生成 随机字符串/随机数数组
随机字符串
/**
* 生成随机字符串
* @param {Number} len 随机字符串的长度 随机不重复字符串长度最大为62
* @param {Boolean} isRepeat 是否重复 默认true
* @return 长度为len的随机字符串
*/
const randomStr = function (count,isRepeat=true){
var strArr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'.split(''),res = '';
var _count = isRepeat===true? count : count > 62? 62 : count;
while(res.length < _count){
var random = Math.floor(Math.random()*62)
if(isRepeat){
res+=strArr[random]
}else{
if(!res.includes(strArr[random])) {
res+=strArr[random]
}
}
}
return res
}
//测试用例
randomStr(10)
randomStr(10,false)
randomStr(5)
randomStr(63,false)
randomStr(100)
随机数
/**
* 生成随机数
* @param {Number | Object} start 范围起始数字
* @param {Number} end 范围终止数字
* @param {Number} count 随机数个数
* @param {Boolean} isRepeat 是否重复
* @return 随机数数组
*/
const randomNum = function (start,end,count,isRepeat = true){
var _isRepeat = true;
if(arguments.length==1 && arguments[0] !==null && typeof arguments[0] === 'object' ){
var {start,end,count,...reset} = arguments[0]
_isRepeat = reset.isRepeat=== undefined ?true:reset.isRepeat === false? false : reset.isRepeat ;
}else{
_isRepeat = isRepeat
}
var _arr= []
var _max_count =isRepeat? count : end - start + 1
var _count = count > _max_count? _max_count : count
while(_arr.length < _count){
var random = Math.ceil(Math.random()*end)
var _condition = random>=start && random<=end
if(_condition){
if(_isRepeat){
_arr.push(random)
}else{
!_arr.includes(random) && _arr.push(random)
}
}
}
return _arr
}
//测试用例
randomNum(10,20,10)
randomNum(0,9,1)
randomNum(0,9,0)
randomNum(90,100,5,false)
Date 类
基于当前 / 指定时间的 过去 n 天时间(包含当天日期)
应用场景:获取过去一周/一月/半年等日期去渲染echarts数据可视化UI
/**
* 生成 基于当前 / 指定时间的 过去 n 天时间(包含当天日期)
* @param {Number} days 基于当前 / 指定时间的 过去 n 天时间(包含当天日期)
* @param {Boolean} s 指定时间
* @return 日期数组
*/
const passDaysDate = function(days,s) {
if(!arguments.length)return [];
return [...Array(days*1+1).keys()].map(days=>new Date((s?new Date(s):Date.now()) - 86400000 * days).toLocaleDateString()).map(item=>item.split(/\/|-/).map(i=>i.padStart(2,'0')).join('-')).splice(1)
}
//测试用例
passDaysDate()//[]
passDaysDate(3,'2020/3/13')//["2020-03-12", "2020-03-11", "2020-03-10"]
passDaysDate(3)//["2020-03-14", "2020-03-13", "2020-03-12"]
passDaysDate(3,1530975600000) // 13位时间戳日期是:2018/07/07 23:00:00 返回:["2018-07-06", "2018-07-05", "2018-07-04"]
生成 范围内所有日期 包含起止日期
/**
* 生成 范围内所有日期 包含起止日期
* @param {String | Number} startDate 开始时间
* @param {String | Number} endDate 结束时间 选填,不填则默认值是当前日期
* @return 数组
*/
const betweenDate = function(startDate,endDate){
var arr=[];
var now=new Date();
var endDate=endDate?endDate:(now.getFullYear()+'/'+(now.getMonth()+1)+'/'+now.getDate());
var days=parseInt(Math.abs(new Date(startDate.split(' ')[0]+' 00:00:00') - new Date(endDate.split(' ')[0]+' 00:00:00'))/1000/24/60/60)+1;
for(let i=0;i<days;i++){
let stamp=new Date(new Date(startDate).getTime()+i*86400000);
arr.push(stamp.getFullYear()+'-'+String(stamp.getMonth()+1).padStart(2,0)+'-'+String(stamp.getDate()).padStart(2,0))
}
return arr
}
//测试用例
betweenDate('2017-01-01','2017-01-03') //["2017-01-01", "2017-01-02", "2017-01-03"]
betweenDate('2020-03-13') //当前日期:2020/3/15 ,返回 :["2020-03-13", "2020-03-14", "2020-03-15"]
Array 类
快速生成不确定长度值不确定的数组
快速生成不确定数据类型的不确定长度的数组
//方法1
function GenerateArray(len,value){
return ','.repeat(len-1).split(',').fill(value)
}
//方法2
function GenerateArray(len,value){
return new Array(len).toString().split(',').fill(value)
}
//方法3
function GenerateArray(len,value){
return ','.repeat(len-1).split(',').map(i=>value)
}
//方法4
function GenerateArray(len,value){
return new Array(len).toString().split(',').map(i=>value)
}
//方法5
function GenerateArray(len,value){
return new Array(len).fill(value)
}
//方法6
function GenerateArray(len,value){
return Array(len).fill(value)
}
//测试用例
//生成长度为2 值为空对象的数组
GenerateArray(2,{}) //[{},{}]
//生成长度为5 值为0的数组
GenerateArray(5,0) //[0,0,0,0,0]
//生成长度为5 值为[]的数组
GenerateArray(5,[]) //[[],[],[],[],[]]
快速生成 值为索引递增 长度为len 的数组
function GenerateArray(len){
return [...Array(len).keys()]
}
//测试用例
GenerateArray(10) //[0,1,2,3,4,5,6,7,8,9]
不按索引,从1开始递增
function GenerateArray(len){
let arr = [...Array(len+1).keys()]
arr.splice(0,1)
return arr
}
//测试用例
GenerateArray(10) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Preview 回显类
月份回显
export const previewMonth = function(i){
return i>0&&i<13? ['一','二','三','四','五','六','七','八','九','十','十一','十二'][i-1]+'月':'Invalid month'
}
//测试用例
previewMonth(0) //Invalid month
previewMonth(1) //一月
previewMonth(2) //二月
previewMonth(3) //三月
previewMonth(4) //四月
previewMonth(5) //五月
previewMonth(6) //六月
previewMonth(7) //七月
previewMonth(8) //八月
previewMonth(9) //九月
previewMonth(10) //十月
previewMonth(11) //十一月
previewMonth(12) //十二月
previewMonth(13)//Invalid month
星期回显
const previewWeek = function(i){
return i>0&&i<8?'周'+['一','二','三','四','五','六','日'][i-1]:'Invalid week'
}
//测试用例
previewWeek(0) //Invalid week
previewWeek(1) //周一
previewWeek(2) //周二
previewWeek(3) //周三
previewWeek(4) //周四
previewWeek(5) //周五
previewWeek(6) //周六
previewWeek(7) //周日
previewWeek(8) //Invalid week
时刻回显
类似发表文章的时间回显
const previewDate = function(target = Date.now()){
/**
* 回显 规则:
* {
* <60s:'刚刚',
* '>60s && 3600s':'x分钟之前',
* '>3600s && <86400s':'x小时之前',
* '昨天':'昨天',
* '前天':'前天',
* '具体日期':'月日',
* '不是当年':'显示年月日'
* }
* */
// 时间格式参数 2018/07/08 06:05:59 | 2018-07-08 06:05:59 | 时间戳
let targetStamp = parseInt(new Date(target).getTime()/1000)//参数转时间戳
let diff = parseInt((Date.now()/1000 - targetStamp))// 计算 与当前时间相差秒数
let dt =new Date();
let today = dt.toLocaleDateString() //今天日期 年月日
let yestoday = new Date(dt.setDate(dt.getDate() - 1)).toLocaleDateString()//昨天日期 年月日
let beforeYestoday = new Date(dt.setDate(dt.getDate() - 1)).toLocaleDateString()//前天日期 年月日
let todayDawnStamp = new Date(today +' 00:00:00').getTime()/1000 //2020/2/8
let yestodayDawnStamp =new Date(yestoday +' 00:00:00').getTime()/1000 //昨天凌晨 时间戳 (秒)2020/2/7
let beforeYestodayDawnStamp = new Date(beforeYestoday +' 00:00:00').getTime()/1000 //前天凌晨 时间戳(秒) 2020/2/6
if(diff < 60) {
return '刚刚'
}else if(diff > 60 && diff <3600) {
return parseInt(diff/60)+'分钟前'
}else if(diff >= 3600 && diff < 86400){
return parseInt(diff/3600)+'小时前'
}else if( targetStamp < todayDawnStamp && targetStamp >= yestodayDawnStamp){
return '昨天'
}else if(targetStamp <= yestodayDawnStamp && targetStamp >= beforeYestodayDawnStamp){
return '前天'
}else if(targetStamp > beforeYestodayDawnStamp){
let d = new Date(target);
return (d.getMonth() +1) +'-'+ d.getDate()
}else {
let t = new Date(targetStamp*1000).toLocaleDateString();
let c = new Date().getFullYear();
return t.substr(0,4) == c?t.slice(5):t
}
}
//测试用例
previewDate() //刚刚
previewDate('2020/3/14') //昨天 测试日期:2020-3-15
previewDate('2020/3/13') //前天 测试日期:2020-3-15
previewDate('2020/1/14') // 1/14
previewDate('2019/12/14') //2019/12/14
转换类
JSON 转 fromData
/**
* JSON 转 fromData
* @param {Object} o 需要转成formData 的对象
*/
const jsonToFromData = function jsonToFromData(o){
return Object.keys(o).reduce((p, c) => !p.append(c, o[c]) && p, new FormData())
}
//测试用例
let FromData = jsonToFromData({
sysId:"001",
tenantId:"002",
userId:"003",
username:'wuming',
age:39,
})
FromData // FormData{}
FromData.forEach((v,k)=>{
console.log(k,v)
})
/**输出:
* sysId 001
* tenantId 002
* userId 003
* username wuming
* age 39
*/
获取类
获取字符串模板内容
/**
* 获取模板字符串里模板里的值
* @param {String} str 模板字符串
* @param {String} f 指定模板的左部分
* @param {String} l 指定模板的右部分
* @param {Number} pattern 指定模板的右部分 获取匹配内容的类型 默认1
* @param {Number} index 结果数组的索引
* @return any
*/
function betweenContent(str, f, l, pattern = 1, index = -1) {
let meta = '([{}])\^$|)?*+.',
F = meta.includes(f) ? `\\${f}` : f,
L = meta.includes(l) ? `\\${l}` : l,
p1 = '*?',
p2 = '?',
p3 = '',
regStr = `[${F}]${pattern == 2 ? '?' : ''}([^${F + L}]+)[${L}]${pattern == 1 ? p1 : pattern == 2 ? p2 : p3}`,
reg = new RegExp(regStr, 'gi'),
result = pattern == 1 ? str.match(reg).map(i => i.substr(1)) : str.match(reg);
return index == -1 ? result : result[index]
}
//测试用例
betweenContent('1[2①3]4[35②35]35[43③45]345', '[', ']')// ["2①3", "35②35", "43③45"]
betweenContent('1[2①3]4[35②35]35[43③45]345', '[', ']', 1) //["2①3", "35②35", "43③45"]
betweenContent('1[2①3]4[35②35]35[43③45]345', '[', ']', 1,1) //35②35
betweenContent('1{2①3}4{35②35}35{43③45}345', '{', '}') //["2①3", "35②35", "43③45"]
betweenContent('1{2①3}4{35②35}35{43③45}345', '{', '}', 1)//["2①3", "35②35", "43③45"]
betweenContent('1{2①3}4{35②35}35{43③45}345', '{', '}', 2)//["1", "{2①3}", "4", "{35②35}", "35", "{43③45}", "345"]
betweenContent('1{2①3}4{35②35}35{43③45}345', '{', '}', 3)//["{2①3}", "{35②35}", "{43③45}"]
betweenContent('1{2①3}4{35②35}35{43③45}345', '{', '}', 1,0)//2①3
betweenContent('1-2①3-4-35②35-35-43③45-345', '-', '-')//["2①3", "4", "35②35", "35", "43③45", "345"]
betweenContent('1-2①3-4-35②35-35-43③45-345', '-', '-', 1)//["2①3", "4", "35②35", "35", "43③45", "345"]
betweenContent('1-2①3-4-35②35-35-43③45-345', '-', '-', 2)//["1-", "2①3-", "4-", "35②35-", "35-", "43③45-", "345"]
betweenContent('1-2①3-4-35②35-35-43③45-345', '-', '-', 3)//["-2①3-", "-35②35-", "-43③45-"]
betweenContent('1-2①3-4-35②35-35-43③45-345', '-', '-', 3,1) //-35②35-
获取字html字符串中的img的地址
function getImgSrc(s) {
var r=[];
s.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, function (m,c) { r.push(c);});
return r;
}
function getImgSrc(s) {
let container = document.createElement('div')
container.innerHTML = s
return [...container.querySelectorAll('img')].map(i=>i.getAttribute('src'))
}
//测试用例
let s=
`<p style="white-space: normal;">
<img title="" alt="1.jpg" src="https://lb.yimiyijia.cn/attached/attachment/201812/f7/71/f7f33bd36bfc80118956aaf3817d2d71.jpg_600x2000.jpg"/>
<img title="" alt="2.jpg" src="https://lb.yimiyijia.cn/attached/attachment/201812/d3/da/d3fb189830bb4654a44661ea25d374da.jpg_600x2000.jpg"/>
</p >`;
getImgSrc(s)
//输出:["https://lb.yimiyijia.cn/attached/attachment/201812…f7f33bd36bfc80118956aaf3817d2d71.jpg_600x2000.jpg", "https://lb.yimiyijia.cn/attached/attachment/201812…d3fb189830bb4654a44661ea25d374da.jpg_600x2000.jpg"]
获取html字符串中文本内容
//非正则版
let str= '<div>岁月不饶人啊</div>'
function getHtmlText(s) {
let container = document.createElement('div')
container.innerHTML = s
return container.textContent
}
getHtmlText(str)//岁月不饶人啊
window 相关
监听页面刷新
网页通过点击链接、地址栏输入、表单提交、脚本操作等方式加载
window.addEventListener('pageshow',function(e){
//...
})
扩展类
原生JS实现 Jq的data方法
Jq的data方法
$('#dom').data('id','2');//修改值
$('#dom').data('id'); //获取值
基于Element/NodeList扩展我们自己的data方法
Element.prototype.data = function(key,value){
if(arguments.length==2){//setter
this.cache={
[key]:value
}
return this
}else if(arguments.length==1){//getter
return this.cache[key]
}
}
NodeList.prototype.data= function(key,value){
if(arguments.length==2){//setter
[].forEach.call(this,function(element,index){
element.cache={
[key]:value
}
//element.data(key,value)
})
return this
}else if(arguments.length==1){//getter
return this[0].cache[key]
}
}
测试Code
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>原生JS实现 Jq的data方法</title>
</head>
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
</ul>
<button id="btn">我只是个按钮</button>
</body>
<script>
let oUl = document.querySelector('ul')
let oLis = document.querySelectorAll('li')
let oBtn = document.getElementById('btn')
oUl.data("data",{name:"wuming",age:30})
console.log( oUl.data("data") ) //{name: "wuming", age: 30}
oLis.data('data',[1,2,3,4])
console.log( oLis.data("data") )//[1, 2, 3, 4]
oBtn.data('status',true)
console.log( oBtn.data("status") )//true
</script>
</html>
数组 repeat 方法扩展
Array.prototype.repeat = function(n=0){
let base = 0;
let res= this
while(base<n){
res = res.concat(this)
base++
}
return res
}
测试用例:
[1,2,3].repeat() //[1,2,3] 不传参 默认是不复制 返回原数组
[1,2,3].repeat(1) //[1,2,3,1,2,3] 复制 1 遍
[1,2,3].repeat(2) //[1,2,3,1,2,3,1,2,3] 复制 2 遍
[{name:'lisi'}].repeat(1) //[{name:'lisi'},{name:'lisi'}] 复制 1 遍
数组 findAllIndexNumber 扩展
找到数字数组中所有符合条件的索引
Array.prototype.findAllIndexNumber = function(v){
let arr = [];
for(let i=0;i<this.length;i++){
if(this[i] === v){
arr.push(i)
}
}
return arr
}
测试用例:
[1,2,3,4,1,2,1].findIndexAllNumber(1) // [0, 4, 6]
数组 findAllIndexJson 扩展
找出 JSON数组中 和 给定的回调函数的返回值作为条件匹配到的数据的所有索引值,以数组形式返回
Array.prototype.findIndexAllJson = function(callback){
let arr = [];
for(let i=0;i<this.length;i++){
if(callback(this[i])) {
arr.push(i)
}
}
return arr
}
测试用例:
let jsonArr = [{name:'list',age:27},{name:'wangbo',age:28},{name:'zhangsan',age:27}]
jsonArr.findIndexAllJson(item=>item.age==27) // [0, 2]
数组 substr 扩展
类似字符串的
substr
Array.prototype.substr= function(startIndex,len){
let res = []
for(let i=0;i<this.length;i++){
if(i>=startIndex && res.length<len){
res.push(this[i])
}
}
return res
}
测试用例:
let arr = [1,2,3,4,5]
arr.substr(0,1) // [1]
arr.substr(0,2) // [1,2]
arr.substr(0,3) // [1,2,3]
arr.substr(1,3) // [2,3,4]
判断(检测)类
PC还是移动端
//是否PC
function isPC(){
return !('ontouchstart' in window)
}
//是否移动端
function isMobile(){
return 'ontouchstart' in window
}
原理:PC端下面的
window对象,不存在ontouchstart属性,移动端下面的window对象是有ontouchstart属性
构造函数调用方式
如何判断一个方法的调用是new 的实例,还是构造函数的直接调用
// 方法1
function Person(name) {
if (this.constructor===Person) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
// 方法2
function Person(name) {
if (new.target !== undefined) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
// 方法3
function Person(name) {
if (new.target === Person) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
// 方法4
function Person(name) {
if (this instanceof arguments.callee) {
this.name = name;
} else {
throw new Error('必须使用 new 命令生成实例');
}
}
// 方法5 Vue 使用此种方法
function Person(name) {
if (this instanceof Person) {
this.name = name;
} else {
throw new Error('Person is a constructor and should be called with the `new` keyword');
}
}
let person = new Person('张三'); // 正确
let notAPerson = Person('张三'); // 报错
new是从构造函数生成实例对象的命令。ES6为new命令引入了一个new.target属性,该属性一般用在构造函数之中,返回new命令作用于的那个构造函数。如果构造函数不是通过new命令调用的,new.target会返回undefined,因此这个属性可以用来确定构造函数是怎么调用的;
构造函数的实例上的constructor属性 指向 构造函数,因此也可以来判断 构造函数的调用方式。
数据类型检测
String
function isString(s){
return typeof(s) === 'string'
}
function isString(s){
return ({}).toString.call(s).slice(8,-1) === 'String'
}
Boolean
function isBoolean(s){
return ({}).toString.call(s).slice(8,-1) === 'Boolean'
}
function isBoolean(s){
return s=== false || s=== true
}
Null
function isNull(s){
return ({}).toString.call(s).slice(8,-1) === 'Null'
}
function isNull(s){
return s === null
}
Undefined
function isUndefined(s){
return ({}).toString.call(s).slice(8,-1) === 'Undefined'
}
function isUndefined(s){
return s === undefined
}
Number
/**
*第一种 通过try catch 避免 :
* Cannot read property 'constructor' of undefined
* Cannot read property 'constructor' of null
*/
function isNumber(p){
try{
return p.constructor === Number
}catch(e){
return false
}
}
function isNumber(p){
return typeof p === 'number'
}
function isNumber(o){//准确 包含NaN
return Object.prototype.toString.call(o).slice(8,-1) === 'Number'
}
测试用例
[
{},
[],
undefined,
null,
123,
'123',
Symbol('111'),
function(){},
new Date(),
false,
/\d+/gi,
NaN
].map(item=>{
console.log(isNumber(item))
})
//4false true 6false true
Symbol
function isSymbol(s){
return ({}).toString.call(s).slice(8,-1) === 'Symbol'
}
function isSymbol(s){
return typeof s === 'symbol'
}
Function
function isFunction(s){
return ({}).toString.call(s).slice(8,-1) === 'Function'
}
function isFunction(s){
return typeof s === 'function'
}
Array
/**
*第一种 通过try catch 避免 :
* Cannot read property 'constructor' of undefined
* Cannot read property 'constructor' of null
*/
function isArray(o){//准确
try{
return o.constructor === Array
}catch(e){
return false
}
}
//第二种 isArray 判断
function isArray(o){//准确
return Array.isArray(o)
}
//第三种 isArray 判断
function isArray(o){//准确
return o instanceof Array
}
//第四种
function isArray(o){//准确
return Object.prototype.toString.call(o).slice(8,-1) === 'Array'
}
测试用例
[
{},
[],
undefined,
null,
123,
'123',
Symbol('111'),
function(){},
new Date(),
false,
/\d+/gi
].map(item=>{
console.log(isArray(item))
})
// false true 9false
Date
function isDate(s){
return ({}).toString.call(s).slice(8,-1) === 'Date'
}
RegExp
function isRegExp(s){
return ({}).toString.call(s).slice(8,-1) === 'RegExp'
}
Set
function isSet(s){
return ({}).toString.call(s).slice(8,-1) === 'Set'
}
Map
function isMap(s){
return ({}).toString.call(s).slice(8,-1) === 'Map'
}
判断严格类型
function type(o){
return Object.prototype.toString.call(o).slice(8,-1)
}
//测试用例
let data = [
{},
[],
undefined,
null,
123,
'123',
Symbol('111'),
function(){},
new Date(),
false,
/\d+/gi
]
for(let i = 0; i < data.length; i++) {
console.log( type( data[i] ) )
}
//依次打印出:
/**
* Object,
* Array,
* Undefined,
* Null,
* Number,
* String,
* Symbol,
* Function,
* Date,
* Boolean
* RegExp
*/
判断对象类型
/**
*第一种 通过try catch 避免 :
* Cannot read property 'constructor' of undefined
* Cannot read property 'constructor' of null
*/
function isObject(o){ //准确
try{
return o.constructor === Object
}catch(e){
return false
}
}
//第二种
function isObject(o){//准确
return Object.prototype.toString.call(o).slice(8,-1) === 'Object'
}
测试用例
[
{},
[],
undefined,
null,
123,
'123',
Symbol('111'),
function(){},
new Date(),
false,
/\d+/gi
].map(item=>{
console.log(isObject(item))
})
// true 10false
判断有效数字
判断一个值是否为 有效的 数字(不包含NaN),可以是数字字符串,例如 “123"
function idValidNumber(n){
try{
return !isNaN(parseFloat(n)) && isFinite(n);
}catch(e){
return false
}
}
之所以加上
try catch,是因为 针对Symbol值使用此方法会报错:Uncaught TypeError: Cannot convert a Symbol value to a string,用try catch规避
测试用例
[
{},
[],
undefined,
null,
123,
'123',
Symbol('111'),
function(){},
new Date(),
false,
/\d+/gi,
NaN
].map(item=>{
console.log(idValidNumber(item))
})
//4false 2true 6false
判断空对象
function isEmptyObject(o){
return ({}).toString.call(o).slice(8,-1) === "Object" && !Object.keys(o).length
}
条件1:传入的参数首先得是一个对象;条件2:传入的参数对象身上没有
key;同时满足为true ,其他为false
测试用例
[
{},
[],
function(){},
new Date(),
/\d+/g,
null,
undefined,
'',
'123',
Symbol('sdsd'),
false,
true,
123,
NaN,
1,
0,
{a:1}
].map(item=>{
console.log(isEmptyObject(item))
})
// true 16false
判断空数组
function isEmptyArray(o){
return Array.isArray(o) && !o.length
}
条件1:传入参数确实是数组;条件2:数组长度确实为0;同时满足为
true,其他为false
测试用例
[
{},
[],
function(){},
new Date(),
/\d+/g,
null,
undefined,
'',
'123',
Symbol('sdsd'),
false,
true,
123,
NaN,
1,
0,
{a:1}
].map(item=>{
console.log(isEmptyArray(item))
})
//false true 15false
判断对象key存在与否
//1.检测传入的第一个参数是否是对象
//2.传入的第二个参数是否是字符串
//3.检测这个字符串是否属于这个对象的key
let obj = {
a:0,
b:1,
c:undefined,
d:null,
e:[],
f:'',
j:NaN,
h:{}
}
function hasProp(obj,key){
if(({}).toString.call(obj).slice(8,-1) !== "Object"){
throw Error(`传入的 ${obj} 不是一个有效对象`)
}
if(typeof key !== 'string'){
throw Error(`传入的key ${key} 必须是一个字符串,但是你传入的类型是 ${typeof(key)}`)
}
return obj.hasOwnProperty( key )
}
// obj.hasOwnProperty( key )
// key in obj
obj.hasOwnProperty( key ) 和 key in obj都是可以的
统计类
数组的纵向统计
//示例:
let arr = [
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
]
// [5, 10, 15, 20, 25]
code:
function longitudinalStatistics(arr){
let r = new Array(arr[0].length).toString().split(',').fill(0)
for(let i=0; i<arr.length; i++) {
for(let j=0; j<arr[i].length; j++) {
resArr[j] += arr[i][j]
}
}
return resArr
}
longitudinalStatistics(arr)//[5, 10, 15, 20, 25]
数组的横向统计
//示例
let arr = [
[1,2,3,4,5],// =>1+2+3+4+5= 15
[1,2,3,4,5],// =>1+2+3+4+5= 15
[1,2,3,4,5],// =>1+2+3+4+5= 15 ==> [15, 15, 15, 15, 15]
[1,2,3,4,5],// =>1+2+3+4+5= 15
[1,2,3,4,5],// =>1+2+3+4+5= 15
]
code
function horizontalStatistics(arr){
return arr.map(i=>i.reduce((p,c)=>p+c))
}
horizontalStatistics(arr) // [15, 15, 15, 15, 15]
工具类
深度合并两个对象
function merge(f, s) {
for (var i in s) {
f[i] = f[i] && f[i].toString() === "[object Object]"?merge(f[i], s[i]) : f[i] = s[i];
}
return f;
}
测试用例
var a = {text:'text',chidren:"chidren",c:{d:1,f:{t:1,m:{o:22}}}}
var b = {text:'title',chidren:"list",c:{d:9,g:33,f:{y:88,m:{l:3}}}}
merge(a, b)//{text:'title',chidren:"list",c:{d:9,g:33,f:{t:1,y:88,m:{o:22,l:3}}}
深度合并多个对象
const MergeMultiple = function(...a){
//参数长度
let l = a.length
//没有传参 抛出错误
if(l==0) throw Error('Pass at least one object parameter')
//参数传一个 直接返回这个
if(l==1)return a[0]
//深度合并两个对象策略
let m = function(f, s) {
for (let i in s) {
f[i] = f[i] && f[i].toString() === "[object Object]"?m(f[i], s[i]) : f[i] = s[i]
}
return f
}
//参数传一个 直接返回合并后的对象
if(l==2)return m(a[0],a[1])
//参数大于2,则执行递归合并,并在长度为2时,返回合并对象
while(a.length>2){
a.splice(0,2,m(a[0],a[1]))
if(a.length==2)return m(a[0],a[1])
}
}
测试用例
MergeMultiple({name:'rookie',age:30,job:'web'})
//{name: "rookie", age: 30, job: "web"}
MergeMultiple({name:'rookie',age:30,job:'web',info:{sex:'men'}},{info:{sex:'women',country:'china'}})
//{name: "rookie", age: 30, job: "web",info: {sex: "women", country: "china"}}
MergeMultiple({info:{sex:'men'}},{info:{sex:'women',country:'china'}},{feature:{height:'172cm'}},{feature:{weight:'65kg'}})
//{info: {sex: "women", country: "china"},feature: {height: "172cm", weight: "65kg"}}
精度丢失处理
加减乘除 中的精度丢失处理方法
/**
* 计算方法 calc
* @param { number } type :0 加 1 减 2 乘 3 除
* @param { String | Number } a :计算数a
* @param { String | Number } b :计算数b
* @param { Number } digit :结果保留的位数
* @return Number | String
*/
function calc(type,a,b,digit){
let r1, r2;
try { r1 = a.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = b.toString().split(".")[1].length } catch (e) { r2 = 0 }
let maxLen = Math.pow(10, Math.max(r1, r2))
let tyeps= [
(Math.round(maxLen*a) + Math.round(maxLen*b))/maxLen,//加
(Math.round(maxLen*a) - Math.round(maxLen*b))/maxLen,//减
(Math.round(maxLen*a) * Math.round(maxLen*b))/(maxLen*maxLen),//乘
Math.round(maxLen*a)/Math.round(maxLen*b)//除
]
let round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`)
let str = String(round(tyeps[type],digit))
if(digit){
if(str.includes('.'))return str.split('.')[0]+'.'+str.split('.')[1].padEnd(digit,0)
return (str+'.').padEnd((str+'.').length+digit,0)
}else{
return tyeps[type]
}
}
测试用例:type:0 加法
/**
*
*加法(精度丢失问题) type:0
*
*/
//1.1 加法 - 小数相加 保留位数
console.log(calc(0,0.1,0.2)) //0.3
console.log(calc(0,0.1,0.2,0)) //0.3
console.log(calc(0,0.1,0.2,1)) //0.3
console.log(calc(0,0.1,0.2,2)) //0.30
console.log(calc(0,0.1,0.2,3)) //0.300
//1.2 加法 - 整数相加 保留位数
console.log(calc(0,10,90)) //100
console.log(calc(0,10,90,0)) //100
console.log(calc(0,10,90,1)) //100.0
console.log(calc(0,10,90,2)) //100.00
//1.3 加法 - 整数和小数相加 保留位数
console.log(calc(0,10.05,90)) //100.05
console.log(calc(0,10.15,90,0)) //100.15
console.log(calc(0,10.25,90,1)) //100.3
console.log(calc(0,10.35,90,2)) //100.35
console.log(calc(0,10.45,90,0)) //100.45
console.log(calc(0,10.55,90,2)) //100.55
console.log(calc(0,10.65,90,1)) //100.7
测试用例:type:1 减法
/**
*
*减法(精度丢失问题) type:1
*
*/
//1.1 减法 - 小数相减 保留位数
console.log(calc(1,0.1,0.3)) //-0.2
console.log(calc(1,0.1,0.3,0)) //-0.2
console.log(calc(1,0.1,0.3,1)) //-0.2
console.log(calc(1,0.1,0.3,2)) //-0.20
console.log(calc(1,0.1,0.3,3)) //-0.200
//1.2 减法 - 整数相减 保留位数
console.log(calc(1,10,90)) //-80
console.log(calc(1,100,90,0)) // 10
console.log(calc(1,10,90,1)) //-80.0
console.log(calc(1,10,90,2)) //-80.00
//1.3 减法 - 整数和小数相减 保留位数
console.log(calc(1,10.05,90)) //-79.95
console.log(calc(1,10.15,90,0)) //-79.85
console.log(calc(1,10.25,90,1)) //-79.7
console.log(calc(1,10.35,90,2)) //-79.65
console.log(calc(1,10.45,90,0)) //-79.55
console.log(calc(1,10.55,90,2)) //-79.45
console.log(calc(1,10.65,90,1)) //-79.3
测试用例:type:2 乘法
/**
*
*乘法(精度丢失问题) type:2
*
*/
//1.1 乘法 - 小数相乘 保留位数
console.log(calc(2,0.1,0.1)) //0.01
console.log(calc(2,0.3,0.5698)) //0.17094
console.log(calc(2,0.3,0.5698,1)) //0.2
console.log(calc(2,0.3,0.5698,2)) //0.17
console.log(calc(2,0.3,0.5698,3)) //0.171
//1.2 乘法 - 整数相乘 保留位数
console.log(calc(2,10,90)) //900
console.log(calc(2,100,90,0)) //9000
console.log(calc(2,10,90,1)) //900.0
console.log(calc(2,10,90,2)) //900.00
//1.3 乘法 - 整数和小数相乘 保留位数
console.log(calc(2,10.05,90,2)) //904.50
console.log(calc(2,10.15,90,0)) //913.5
console.log(calc(2,10.25,90,1)) //922.5
console.log(calc(2,10.35,90,2)) //931.50
console.log(calc(2,10.45,90,0)) //940.5
console.log(calc(2,10.55,90,2)) //949.50
console.log(calc(2,10.65,90,1)) //958.5
测试用例:type:3 除法
/**
*
*除法(精度丢失问题) type:3
* PS:有 无限循环小数 情况
*/
//1.1 除法 - 小数相除 保留位数
console.log(calc(3,0.1,0.3)) //0.3333333333333333
console.log(calc(3,0.1,0.3,1)) //0.3
console.log(calc(3,0.1,0.3,2)) //0.33
console.log(calc(3,0.3,0.5698)) //0.5265005265005265
console.log(calc(3,0.3,0.5698,1)) //0.5
console.log(calc(3,0.3,0.5698,2)) //0.53
console.log(calc(3,0.3,0.5698,3)) //0.527
//1.2 除法 - 整数相除 保留位数
console.log(calc(3,10,90)) //0.1111111111111111
console.log(calc(3,100,90,0)) //1.1111111111111112
console.log(calc(3,10,90,1)) //0.1
console.log(calc(3,10,90,2)) //0.11
//1.3 除法 - 整数和小数相除 保留位数
console.log(calc(3,10.05,90,2)) //0.11
console.log(calc(3,10.15,90,0)) //0.11277777777777778
console.log(calc(3,10.25,90,1)) //0.1
console.log(calc(3,10.35,90,2)) //0.12
console.log(calc(3,10.45,90,0)) //0.11611111111111111
console.log(calc(3,10.55,90,2)) //0.12
console.log(calc(3,10.65,90,1)) //0.1
其他类
交换两个变量值的位置
var a = {a:1}
var b = {b:2}
a = [b,b=a][0]
a// {b: 2}
b// {a: 1}
结语
上面的方法大部分是自己总结,一部分参考了其他大佬的思想加以实现,在此,感谢大神们的总结。如果对你有帮助,顺手点个赞
如果你有更好的点子,或者没有找到你想要的工具函数,欢迎留言
文中若有不准确或错误的地方,欢迎指出