在我们日常开发过程中,我们自己经常会写大量的方法,也就是function,来封装一些常用功能,来满足我们的开发需要。本篇文章呢,我把自己这几年前端开发写的一些可复用的方法分享出来,与大家一起学习探讨:
数据类型检测方法
众所周知js的typeOf或者instanceof在数据类型检测上都有局限性,该方法检测返回值是以字符串形式: [object String],[object Array],[object Function],[object Object],[object Null],[object RegExp],[object Undefined],[object Symbol]
`
function dataType (data){ // 检测数据类型
let typeStr = Object.prototype.toString.call(data)
return typeStr.substring(8, typeStr.length - 1).toLowerCase()
}
`
检测返回数据是否为空数据
我们日常开发中,数据多数是后端返回来的,我们对于是否有返回来数据做相应的处理,该方法接收一个参数data,返回一个布尔值,是否为空数据,true为空,false为否: `
function isEmpty(data){ //是否为空数据
//null,undefined,'',"",{},[],[{}] 匹配到这些值时,都是返回true
if(data==='' || data==="" || data==='undefined' || data===undefined || data===null)
{return true}
if(JSON.stringify(data)=='{}' || JSON.stringify(data)=='[]' || JSON.stringify(data)=='[{}]')
{return true}
return false
} `
多维数组拉伸一维数组方法
多维数组指的是我们处理的数据里,数组里面还嵌套有数组,有时候我们的代码逻辑需要不数组拉伸维一维的数组来处理,这就是此方法的作用了,该方法只接受一个参数,该参数就是个数组,返回也是个数组: `
var arry = [11, 7, 5, [1, 9], 3, [15, [13, 17]]];
function flattenDepth(arry){
let result = [];
arry.map(item => {
if (Array.isArray(item)) { // es6语法检测当前是否是数组
result.push(...flattenDepth(item)) // 此处是用了es6语法,扩展运算符
} else {
result.push(item)
}
})
return result;
}
`
URL解析方法
对url的解析,我们的主要目的都是获取url上的参数,也就是query值,该方法接收一个url参数,会返回一个参数对象: `
function queryUrl(url) { // 解析url数据传参
var objUrl = {};
var urlData = url.split('?')[1];
if (urlData) {
var urlArr = urlData.split('&');
for (var i = 0, len = urlArr.length; i < len; i++) {
var curArr = urlArr[i].split('=');
objUrl[curArr[0]] = curArr[1]
}
return objUrl
}
}
`
转千位符方法
转千位符是我们对数字处理的一个方法,该方法接收一个数字: `
function toThousands1(num) { //转千位符 方法1
var num = Number(num).toFixed(2);
var n = num.indexOf('.');
var str1 = num.slice(0, n), str2 = num.slice(n, num.length), result = '';
while (str1.length > 3) {
result = ',' + str1.slice(-3) + result;
str1 = str1.slice(0, str1.length - 3);
}
if (str1) { result = str1 + result; }
return result+str2;
}
function toThousands2(num) { // 转千位符 方法2
var num = Number(num).toFixed(2); //如果你想把你转的数保留3位小数,把2改3就好了
var n = num.indexOf('.');
var str1 = num.slice(0, n), str2 = num.slice(n, num.length);
var result = '', counter = 0;
for (var i = str1.length - 1; i >= 0; i--) {
counter++;
result = str1.charAt(i) + result;
if (!(counter % 3) && i != 0) { result = ',' + result; }
}
return result+str2;
}
`
数字金额转大写方法
对数字转大写,特别是开发金融类型的web或者h5时,使用最多,该方法接收一个参数,就是要转的数字, `
function convertCurrency(money) { // 金额数字转大写
//汉字的数字
var cnNums = new Array('零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖');
//基本单位
var cnIntRadice = new Array('', '拾', '佰', '仟');
//对应整数部分扩展单位
var cnIntUnits = new Array('', '万', '亿', '兆');
//对应小数部分单位
var cnDecUnits = new Array('角', '分', '毫', '厘');
//整数金额时后面跟的字符
var cnInteger = '整';
//整型完以后的单位
var cnIntLast = '元';
//最大处理的数字
var maxNum = 999999999999999.9999;
//金额整数部分
var integerNum;
//金额小数部分
var decimalNum;
//输出的中文金额字符串
var chineseStr = '';
//分离金额后用的数组,预定义
var parts;
if (money == '') { return ''; }
money = parseFloat(money);
if (money >= maxNum) {
//超出最大处理数字
return '';
}
if (money == 0) {
chineseStr = cnNums[0] + cnIntLast + cnInteger;
return chineseStr;
}
//转换为字符串
money = money.toString();
if (money.indexOf('.') == -1) {
integerNum = money;
decimalNum = '';
} else {
parts = money.split('.');
integerNum = parts[0];
decimalNum = parts[1].substr(0, 4);
}
//获取整型部分转换
if (parseInt(integerNum, 10) > 0) {
var zeroCount = 0;
var IntLen = integerNum.length;
for (var i = 0; i < IntLen; i++) {
var n = integerNum.substr(i, 1);
var p = IntLen - i - 1;
var q = p / 4;
var m = p % 4;
if (n == '0') {
zeroCount++;
} else {
if (zeroCount > 0) {
chineseStr += cnNums[0];
}
//归零
zeroCount = 0;
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
}
if (m == 0 && zeroCount < 4) {
chineseStr += cnIntUnits[q];
}
}
chineseStr += cnIntLast;
}
//小数部分
if (decimalNum != '') {
var decLen = decimalNum.length;
for (var i = 0; i < decLen; i++) {
var n = decimalNum.substr(i, 1);
if (n != '0') {
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
}
}
}
if (chineseStr == '') {
chineseStr += cnNums[0] + cnIntLast + cnInteger;
} else if (decimalNum == '') {
chineseStr += cnInteger;
}
return chineseStr;
}
`
常规js运动动画函数
js动画是我们前端开发最常接触的功能之一,虽然市面上有很多成熟的动画框架,但是有时候我们只是一个小功能,没有必要引入那些库的时候,那么这个方法就足够你使用了: `
//js运动框架,四个参数,第一个要运动的元素,第二,要变化的值,注意此处是个json对象,第三个是要运动的速度,第四个是链式运动的函数
function cartoon(el, json, s, fn) {
clearInterval(el.timer); //开启定时器前线清除上个定时器
el.timer = setInterval(function () { //开启一个定时器
var isStop = true;
for (var attr in json) { //循环参数里的json对象
var curStyle = null; //声明一个空对象,用来存储当前元素的值
if(attr == 'opacity'){ //如果参数是opacity,表示要改变透明度的变化
curStyle = Math.round(parseFloat(getCss(el, attr))*100); //parseFloat是保留小数,Math.round是四舍五入
//之所以要用Math.round,就是考虑getCss取出来的数会有小数,所以让他四舍五入
} else {
curStyle = parseInt(getCss(el, attr)); //parseInt是取整
}
var speed = (json[attr] - curStyle) / s; //计算速度
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); //如果speed大于0就向上取整,小于0向下取整
if (curStyle != json[attr]) { //如果当前元素的值没有等于目标值
isStop = false; //把isStop设置为false
}
if (attr == 'opacity') { //如果属性为opacity透明度,则单独设置属性
el.style[attr] = (curStyle + speed) / 100;
el.style.filter = 'alpha(opacity:'+(curStyle+speed)+')';
} else { //不是的采用原来设置的方式
el.style[attr] = curStyle + speed + 'px';
}
if (isStop) { //只有isStop为true,才停止定时器
clearInterval(el.timer);
if (fn) fn(); //判断用户有没有传入链式函数
}
}
}, 30)
}
function getCss(el, attr) { // 这个是取元素样式的公共方法
if (el.currentStyle) {
return el.currentStyle[attr];
} else {
return getComputedStyle(el, null)[attr];
}
}
`
js图片压缩
图片压缩的功能,在移动端开发的应用上会较多,因为流量就是金钱,我们要为用户省钱呀,最典型的图片压缩场景就是上传头像,你不可能自拍个10几MB的上传吧,那么试试这个方法吧:该方法是结合input标签的file类型使用的,接收两个参数,第一个是事件对象,第二个是个回调函数,回调函数的参数里可以拿到你需要的数据:
`
function compressImg (event, callback) { // callback回调函数带一个对象参数
// 选择的文件对象(file里只包含图片的体积,不包含图片的尺寸)
var file = event.target.files[0];
// 选择的文件是图片
if(file.type.indexOf("image") === 0) {
// 压缩图片需要的一些元素和对象
var reader = new FileReader(),
img = new Image();
reader.readAsDataURL(file);
// 文件base64化,以便获知图片原始尺寸
reader.onload = function(e) {
img.src = e.target.result;
};
// base64地址图片加载完毕后执行
img.onload = function () {
// 缩放图片需要的canvas(也可以在DOM中直接定义canvas标签,这样就能把压缩完的图片不转base64也能直接显示出来)
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// 图片原始尺寸
var originWidth = this.width;
var originHeight = this.height;
// 最大尺寸限制,可通过设置宽高来实现图片压缩程度
var maxWidth = 1920,
maxHeight = 1080;
var targetWidth = originWidth,
targetHeight = originHeight;
if(originWidth > maxWidth || originHeight > maxHeight) {
if(originWidth / originHeight > maxWidth / maxHeight) {
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originHeight / originWidth));
} else {
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originWidth / originHeight));
}
}
// canvas对图片进行缩放
canvas.width = targetWidth;
canvas.height = targetHeight;
// 清除画布
context.clearRect(0, 0, targetWidth, targetHeight);
// 图片压缩
context.drawImage(img, 0, 0, targetWidth, targetHeight);
/*第一个参数是创建的img对象;第二三个参数是左上角坐标,后面两个是画布区域宽高*/
//压缩后的图片转base64 url
/*canvas.toDataURL(mimeType, qualityArgument),mimeType 默认值是'image/png';
* qualityArgument表示导出的图片质量,只有导出为jpeg和webp格式的时候此参数才有效,默认值是0.92*/
var newUrl = canvas.toDataURL('image/jpeg', 0.75);//base64 格式
let obj = {imgSrc: newUrl}
//也可以把压缩后的图片转blob格式用于上传
canvas.toBlob((blob) => {
obj.blob = blob;
callback(obj);
}, 'image/jpeg', 0.75)
};
} else {
console.log('请选择你的图片')
}
}
compressImg(event, function(data){ // 使用
console.log(data)
})
`
检测浏览器类型
检测浏览器类型这个方法,主要在移动端做h5开发会用的较多,因为我们并不知道用户用什么手机,用什么浏览,而我们的代码逻辑也需要做相应的处理,此方法就是用来判断浏览器类型的,该方法可以返回个布尔值: `
var browser = {
versions:function(){
var u = window.navigator.userAgent; // 通过navigator.userAgent获取当前浏览器的信息
return {
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Firefox') > -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/)||!!u.match(/AppleWebKit/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者安卓QQ浏览器
iPad: u.indexOf('iPad') > -1, //是否为iPad
webApp: u.indexOf('Safari') == -1 ,//是否为web应用程序,没有头部与底部
weixin: u.indexOf('MicroMessenger') == -1 //是否为微信浏览器
};
}()
}
console.log(browser.versions.trident) // 检测是否为IE浏览器
console.log(browser.versions.webKit) // 检测是否为谷歌浏览器
`
树数据递归处理函数
所谓树数据,其实我指的就是我们日常开发中处理的那些树形结构,一般后台只会返回一个数组的数据,需要我们处理他的数据层级结构,并显示在我们的组件上去,例如element-ui的el-tree树组件: `
let data = [
{id: 1, value: '动物'}, {id: 2, value: '植物'}, {id: 3, value: '微生物'}, {id: 4, value: '无机物'},
{id: 5, parentId: 1, value: '脊椎类动物'}, {id: 6, parentId: 2, value: '木科植物'}, {id: 7, parentId: 1, value: '无脊椎类动物'}, {id: 8, parentId: 2, value: '草本植物'},
{id: 9, parentId: 3, value: '细菌'}, {id: 10, parentId: 5, value: '老虎'}, {id: 11, parentId: 5, value: '狮子'}, {id: 12, parentId: 5, value: '猎豹'},
{id: 13, parentId: 6, value: '松树'}, {id: 14, parentId: 6, value: '樟树'}, {id: 15, parentId: 6, value: '桦树'}, {id: 16, parentId: 7, value: '虾'},
{id: 17, parentId: 7, value: '章鱼'}, {id: 18, parentId: 3, value: '真菌'}, {id: 19, parentId: 3, value: '病毒'}, {id: 20, parentId: 4, value: '黄金'},
{id: 21, parentId: 8, value: '牡丹花'}, {id: 22, parentId: 8, value: '四叶草'}, {id: 23, parentId: 4, value: '白银'}, {id: 24, parentId: 4, value: '玉石'},
{id: 25, parentId: 4, value: '珍珠'}, {id: 26, parentId: 9, value: '球状杆菌'}
]
function classify (arr) { // 树数据递归处理函数
let first = [], others = [];
arr.forEach(item => {
item.children = [];
if (!item.parentId) {
first.push(item)
} else {
others.push(item)
}
});
function recursion (arr, ary) {
arr.forEach(item => {
let other = []
ary.map(cur => {
if (item.id === cur.parentId) {
item.children.push(cur)
} else {
other.push(cur)
}
})
recursion(item.children, other);
})
}
recursion(first, others);
return first;
}
let ary = classify(data);
` 注意:该方法拿过去后,需要把你数据里的关键字段做个替换,因为一般的树数据都有当前数据的id和归属哪个父数据下的一个parentId,我这里只是做的模拟,并不确定你的数据也是这个字段名称,你需要找到你的关键字段替换就OK。
取限定范围内的随机数
该方法在我们开发抽取随机幸运观众,随机礼品那种功能的时候应用较多,该方法接收两个参数,一个正整数类型的参数,一个取多少个随机数,会返回一个数组,数组里是随机的数字: `
function getRandom(integer, size) {
let arr = [];
function count() {
if (arr.length < size) {
let n = Math.round(Math.random() * integer);
if(!arr.includes(n)) {
arr.push(n)
}
count()
}
}
count()
return arr
}
getRandom(100, 10) // 随机取出0-100以内10位数字
`
字符串加密方法
加密的功能一般来说是前后端都需要做的,前端也会用啥md5加密之类的工具,此方法应用的场所只是针对我们有些普通的数据,例如存储在localStorage这些数据,我们不希望他这么直白的显示在那,我们就需要对他做个加密处理,这里包含两个方法,一个加密,一个解密,都需要一个字符串参数:
`
function compileStr (code) { // 对字符串进行加密
var c = String.fromCharCode(code.charCodeAt(0) + code.length);
for(var i = 1; i < code.length; i++){
c += String.fromCharCode(code.charCodeAt(i) + code.charCodeAt(i - 1));
}
return escape(c);
}
function uncompileStr (code) { // 字符串进行解密
code = unescape(code);
var c = String.fromCharCode(code.charCodeAt(0)-code.length);
for(var i = 1; i < code.length; i++){
c += String.fromCharCode(code.charCodeAt(i) - c.charCodeAt(i - 1));
}
return c;
}
` 注意:该方法的加密规则,你可以自行变化下,保证可以做到即便是拷贝同一份代码,你们的加解密的方式也不同。
倒计时方法
倒计时是我们前端开发较常见的功能需求之一了,例如商品列表的倒计时,啥啥活动倒计时,节日倒计时,本示例以节日倒计时为例演示,略微修改你就可以改成任何你想要的倒计时了,该方法接收一个参数,就是目标时间字符串: `
function countDown (targetDate) { // 参数targetDate是目标时间
var targetDateMs = Date.parse(targetDate); // 目标时间毫秒数
var myDate = new Date()
var curDateMs = Date.now(); // 获取当前时间毫秒数 方法1
// var curDateMs = Date.parse(myDate) // 获取当前时间的毫秒数 方法2
if (curDateMs != targetDateMs) { // 当前时间只要不等于目标时间毫秒数
var timeDifference = targetDateMs-curDateMs; // 当前时间与目标时间的时间差毫秒数
var monthTotalMs = 1000 * 60 * 60 * 24 * 30; // 月毫秒总数
var dayTotalMs = 1000 * 60 * 60 * 24; // 1天毫秒总数
var hoursTotalMs = 1000 * 60 * 60; // 1小时毫秒总数
var minutesTotalMs = 1000 * 60; // 1分钟毫秒总数
var month = Math.floor(timeDifference / monthTotalMs); //计算月
var surplusDayMs = timeDifference - (month * monthTotalMs); // 计算减去月后剩余的毫秒数
var day = Math.floor(surplusDayMs / dayTotalMs); // 计算天
var surplusHoursMs = surplusDayMs - (day * dayTotalMs); // 计算减去天后剩余的毫秒总数
var hours = Math.floor(surplusHoursMs / hoursTotalMs); // 计算小时
var surplusMinutesMs = surplusHoursMs - (hours * hoursTotalMs); // 计算减去小时后还剩余的毫秒总数
var minutes = Math.floor(surplusMinutesMs / minutesTotalMs);// 计算分
var seconds = Math.ceil((surplusMinutesMs - (minutes * minutesTotalMs)) / 1000); //剩余多少秒
return `距离2019年倒计时:${month}月${day}天${toDou(hours)}时${toDou(minutes)}分${toDou(seconds)}秒`
} else { // 等于就停止计时器
clearInterval(time);
}
}
function toDou(n){ // 用于补0的方法
if(n<10){
return '0'+n;
}else{
return ''+n;
}
}
var time = setInterval(function(){
document.querySelector('.div-date').innerText = countDown ('2020-01-01');
}, 1000)
`
点击外部区域关闭弹框
在日常开发中,弹框是一种非常常见的组件,在一些特定需求当中,我们的弹框可能只需要一个内容部分,也就是没有遮罩,那么这个时候,如果我们隐藏了遮罩层,那么ui组件库提供的点击遮罩层关闭弹窗的方法就不可用了,而我们同样要处理点击其他地方关闭弹窗,或者我们其他自定义显示的浮层。
` <button @click="showDialog">开启弹框
`
颜色16进制转RGB
在日常开发中,设置颜色,除了常见的色彩,会有对应的英文,常用的方式主要就是设置16进制和rgb两种模式,比如黑色,它的16进制就是 #000000, 而它的rgb则是 0,0,0 ,我们设置颜色,比如背景色,就会这样输入:background: #000000 或者 background: rgb(0,0,0);这是最常见的两种设置方式,但是有一些需求,我们需要设置透明度,不考虑影响子级dom元素的情况下,使用opacity:0.5 是ok, 因为这个属性会让整个标签都透明,包括子级dom,但是不想影响子级,那么就会用到另外个方法:rgba(), rgba()方法接收的就是rgb格式,比如黑色rgba(0,0,0,0.5)。那么这个时候问题来了,我们多数情况下拿到的是16进制的颜色数据,因此我们就需要将这个数据做转换,转成rgb格式的。下面是实现方法:
`
function transColorRgb(color) {
let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
let sColor = color.toLowerCase();
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
let newColor = '#'
for (let i = 1; i < 4; i+= 1) {
newColor += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
}
sColor = newColor;
}
let colorNumber = []
for (let i = 1; i < 7; i +=2) {
colorNumber.push(parseInt(`0x${sColor.slice(i, i + 2)}`));
}
let rgbText = colorNumber.join(',')
return rgbText
} else {
return sColor
}
}
console.log(transColorRgb('#000000')) // 输出 0,0,0
// 如何应用这个呢,使用css的变量,看应用插图:
`
vue指令v-collapse收起展开动画效果
收起展开是不论pc还是移动端都很常见的功能,实现的方式也多种多样,css3动画实现,常规js处理实现都有,今天介绍一个使用指令的方式实现的收起展开功能。
`
// v-collapse.js
export default {
mounted(el, binding) {
el.__expanded = !!binding.value;
el.style.overflow = 'hidden';
el.style.transition = 'height 0.3s ease, opacity 0.3s ease';
if (el.__expanded) {
el.style.height = 'auto';
el.style.opacity = '1';
} else {
el.style.height = '0';
el.style.opacity = '0';
}
},
updated(el, binding) {
const isExpanded = !!binding.value;
if (isExpanded === el.__expanded) return;
el.__expanded = isExpanded;
const currentHeight = el.scrollHeight + 'px';
if (isExpanded) {
el.style.height = '0';
el.style.opacity = '0';
requestAnimationFrame(() => {
el.style.height = currentHeight;
el.style.opacity = '1';
});
el.addEventListener(
'transitionend',
() => {
el.style.height = 'auto';
},
{ once: true }
);
} else {
el.style.height = el.scrollHeight + 'px';
requestAnimationFrame(() => {
el.style.height = '0';
el.style.opacity = '0';
});
}
},
beforeUnmount(el) {
delete el.__expanded;
}
};
// main.js;
import { createApp } from 'vue';
import App from './App.vue';
import vCollapse from './directives/v-collapse';
const app = createApp(App);
// 注册指令
app.directive('collapse', vCollapse);
app.mount('#app');
`
小结
本章节的多数方法都是可以可以直接拿来使用的,个别方法需要根据你自己具体需求略微小改就OK了,当然这里的这些方法都是以我过往的业务需求写的,也许与你的需求有些差异,只要功能相似,我相信调整一下都是可以实现的,本章节后续会继续补充,各位大佬要是也有啥实用的公共小方法也可以共享一下,谢谢。