前端方法汇总

有时候总会为了一个小小的功能在网上搜寻了大半天的时间,预览了很多框架和插件,一直没找到合适的,最后只能花时间自己写一个。如果完成的功能没有做好记录,再次遇见这个问题,处理起来就比较费劲。随着开发量的增多,这些功能点根本记不住,所以我把这些开发中遇到比较常见且难实现的方法汇总到一块,再次遇见时简单CV一下,就可以略过这些难题啦。

CSS

文字溢出

p { /*单行显示省略号*/
    width:300px;    
    overflow: hidden;    
    text-overflow:ellipsis;    
    white-space: nowrap;
}
p { /*多行显示省略号*/
    display: -webkit-box;    
    -webkit-box-orient: vertical;    
    -webkit-line-clamp: 3;    
    overflow: hidden;
}

禁止选择

div {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}

touch-action:设置触摸屏用户操纵元素的区域 (例如,浏览器内置的缩放功能)

滚动条样式

::-webkit-scrollbar {
    width: 6px;
    height: 6px;
    background-color: transparent;
}
::-webkit-scrollbar-track { /*空白区域*/
    background-color: transparent;
}
::-webkit-scrollbar-thumb { /*滑动区域*/
    border-radius: 3px;
    background-image: linear-gradient(135deg, #09f, #3c9);
}

JavaScript

在平时的开发中,偶尔也会用到以下插件,在开发中提供了不少的处理函数。

lodashjs:一致性、模块化、高性能的 JavaScript 实用工具库

dayjs:解析、验证、操作和显示日期和时间

数组操作

生成数组

生成一个0-99的数组

const createArr = (n) => Array.from(new Array(n), (v, i) => i)
const arr = createArr(100) // 0 - 99 数组

数组去重

const removeDuplicates = list => [...new Set(list)]
removeDuplicates([0, 0, 2, 4, 5]) // [0,2,4,5]

数组交集

取多个数组中的交集

const intersection = (a, ...arr) => [...new Set(a)].filter((v) => arr.every((b) => b.includes(v)))
intersection([1, 2, 3, 4], [2, 3, 4, 7, 8], [1, 3, 4, 9]) // [3, 4]

数组子集

检查一个数组是否为其他数组的子集

const isSubset = (a, b) => new Set(b).size === new Set(b.concat(a)).size;
isSubset([1, 2], [1, 2, 3, 4]); // true
isSubset([1, 2, 5], [1, 2, 3, 4]); // false

数组重复数

获取数组中指定值的重复次数

const countOccurrences = (arr, val) => arr.filter((item) => item === val).length;
countOccurrences([2, 1, 3, 3, 2, 3], 2); // 2
countOccurrences(['a', 'b', 'a', 'c', 'a', 'b'], 'a'); // 3

最大值索引

获取数组中的最大值的索引

const indexOfMax = (arr) => arr.reduce((prev, curr, i, a) => (curr > a[prev] ? i : prev), 0);
indexOfMax([1, 3, 9, 7, 5]); // 2

最小值索引

获取数组中的最小值的索引

const indexOfMin = (arr) => arr.reduce((prev, curr, i, a) => (curr < a[prev] ? i : prev), 0)
indexOfMin([2, 5, 3, 4, 1, 0, 9]) // 5

数组判空

const isNotEmpty = arr => Array.isArray(arr) && arr.length > 0;
isNotEmpty([1, 2, 3]);  // true

数组合并

const merge = (a, b) => a.concat(b);
const merge = (a, b) => [...a, ...b];

数组项交换

const swapItems = (a, i, j) => (a[i] && a[j] && [...a.slice(0, i), a[j], ...a.slice(i + 1, j), a[i], ...a.slice(j + 1)]) || a;
swapItems([1, 2, 3, 4, 5], 1, 4); // [1, 5, 3, 4, 2]

数组随机项

获取数组随机项

const randomItems = (arr, count) => arr.concat().reduce((p, _, __, arr) => (p[0] < count ? [p[0] + 1, p[1].concat(arr.splice((Math.random() * arr.length) | 0, 1))] : p), [0, []])[1];
randomItems([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3); // [4, 8, 5]

树结构转换

将对象数组转换成树形结构

let arr = [{
  id: 1,
  pid: 0,
  name: 'body'
}, {
  id: 2,
  pid: 1,
  name: 'title'
}, {
  id: 3,
  pid: 2,
  name: 'div'
}]

function toTree (data) {
  let result = [];
  let map = {};
  // 1. 先构建map结构,以各个子项id为key
  data.forEach((item) => {
    map[item.id] = item
  })
  // 2. 再循环目标数组,判断上面构建的map中,是否存在当前遍历的pid
  data.forEach((item) => {
    let parent = map[item.pid];
    if(parent) {
    // 3. 存在就可以进行children的插入
      (parent.children || (parent.children = [])).push(item)
    } else {
    // 4. 不存在就是顶级节点,直接push即可
      result.push(item)
    }
  })
  return result;
}
console.log(toTree(arr));

数字操作

四舍五入

保留小数点后几位

const round = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`)
round(10.255, 2) // 10.26

数字补零

当需要在一个数字不足位数时前面补零操作

const replenishZero = (num, len, zero = 0) => num.toString().padStart(len, zero)
replenishZero(8, 2) // 08

生成随机数

给定一个范围生成一个随机数

const randomNum = (m, n) => Math.floor(Math.random() * (n - m + 1) + m);
randomNum(5, 10)

生成随机数组

给定一个范围生成一个随机数组

const randomArrayInRange = (min, max, n) => Array.from({ length: n }, () => Math.floor(Math.random() * (max - min + 1)) + min);
randomArrayInRange(1, 100, 10); // [11, 82, 41, 35, 76, 83, 43, 15, 60, 54]

求平均数

const average = (...args) => args.reduce((a, b) => a + b) / args.length;
average(1, 2, 3, 4, 5);   // 3

求和

const sum = (...args) => args.reduce((a, b) => a + b);
sum(1, 2, 3, 4); // 10

最大公约数

const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b));
gcd(10, 15); // 5

直线角度

计算由两点定义的直线的角度

// In radians
const radiansAngle = (p1, p2) => Math.atan2(p2.y - p1.y, p2.x - p1.x);
// In degrees
const degreesAngle = (p1, p2) => (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI;
radiansAngle({x:1,y:5}, {x:6,y:10})
degreesAngle({x:1,y:5}, {x:6,y:10})

两点距离

const distance = (p1, p2) => Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
distance({x:1,y:5}, {x:6,y:10})

数的阶乘

const factorial = (n) => (n <= 1 ? 1 : n * factorial(n - 1));
factorial(2); // 2
factorial(3); // 6
factorial(4); // 24

对象操作

验证对象

判断一个值是否是对象

const isObject = (v) => v !== null && typeof v === 'object';

对象判空

const isEmpty = (obj) => JSON.stringify(obj) === '{}';

删除无效属性

删除一个对象中的属性值为null或undefined的所有属性

const removeNullUndefined = (obj) => Object.entries(obj).reduce((a, [k, v]) => (v == null ? a : ((a[k] = v), a)), {});
removeNullUndefined({name: '', age: undefined, sex: null}) // { name: '' }

反转对象键值

当你需要将对象的键值对交换

const invert = (obj) => Object.keys(obj).reduce((res, k) => Object.assign(res, { [obj[k]]: k }), {})
invert({name: 'jack'}) // {jack: 'name'}

对象数组去重

function unique(arr, key) {
    let obj = {};
    if (key) { // 指定对象内部的键名为判断条件
        return arr.filter((item, index) => {
            const i = arr.findIndex(t => t[key] === item[key]);
            return i === index;
        })
    } else {
        return arr.filter(function (item) {
            return obj.hasOwnProperty(typeof item + JSON.stringify(item)) ? false : (obj[typeof item + JSON.stringify(item)] = true)
        })
    }
}
unique([{a: 1,b: 2}, {a: 1,b: 2}, {a: 1,b: 5}]) // [{a: 1,b: 2}, {a: 1,b: 5}]
unique([{a: 1,b: 2}, {a: 1,b: 2}, {a: 1,b: 5}],'a') // [{a: 1,b: 2}]

键值分组

数据结构转换以键值分组

function groupArrayByKey(arr = [], key) {
    return arr.reduce((t, v) => (!t[v[key]] && (t[v[key]] = []), t[v[key]].push(v), t), {})
}
groupArrayByKey([
    {classId: "1",name: "张三",age: 16},
    {classId: "1",name: "李四",age: 15},
    {classId: "2",name: "王五",age: 16},
    {classId: "3",name: "赵六",age: 15},
    {classId: "2",name: "孔七",age: 16}
], 'classId')

{
    "1": [
        {
            "classId": "1",
            "name": "张三",
            "age": 16
        },
        {
            "classId": "1",
            "name": "李四",
            "age": 15
        }
    ],
    "2": [
        {
            "classId": "2",
            "name": "王五",
            "age": 16
        },
        {
            "classId": "2",
            "name": "孔七",
            "age": 16
        }
    ],
    "3": [
        {
            "classId": "3",
            "name": "赵六",
            "age": 15
        }
    ]
}

对象排序

根据键名首字母进行排序

const sort = (obj) =>
    Object.keys(obj)
        .sort()
        .reduce((p, c) => ((p[c] = obj[c]), p), {});
sort({white: '#ffffff',black: '#000000',red: '#ff0000',green: '#008000',blue: '#0000ff'});
// { black: '#000000',blue: '#0000ff',green: '#008000',red: '#ff0000',white: '#ffffff'}

对象深拷贝

function clone(obj) {
  return JSON.parse(JSON.stringify(obj))
}

字符串操作

链接处理

function params(url, params) {
    if (params) { // 有参数时拼接
        return params ? url + '?' + Object.keys(params)
            .filter(key => params[key] || params[key] === 0)
            .map(key => `${key}=${params[key]}`)
            .toString().replace(/,/g, '&') :
            url
    } else { // 没参数时获取
        let obj = {};
        url.match(/(\w+)=(\w+)/g).forEach(item => {
            Object.assign(obj, {
                [item.split('=')[0]]: item.split('=')[1]
            })
        })
        return obj
    }
}
params('http://127.0.0.1?id=15&name=kin') // { id: 15, name: 'kin' }
params('http://127.0.0.1', { id: 20, name: 'kinron' }) // http://127.0.0.1?id=20&name=kinron

基本链接

获取不带参数的基本链接

const baseUrl = (url) => (url.indexOf('?') === -1 ? url : url.slice(0, url.indexOf('?')));
baseUrl('https://127.0.0.1?foo=bar&hello=world'); // 'https://127.0.0.1'

字符串哈希

const hash = (str) => str.split('').reduce((prev, curr) => (Math.imul(31, prev) + curr.charCodeAt(0)) | 0, 0);
hash('hello'); // 99162322

首字母大写

const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
capitalize("hello world")  // Hello world

翻转字符串

const reverse = str => str.split('').reverse().join('');
reverse('hello world');   // 'dlrow olleh'

变量类型

获取字符串中变量的类型

const getTypeOf = (obj) => Object.prototype.toString.call(obj).match(/\[object (.*)\]/)[1];

随机字符串

给定字符生成随机字符串

const generateString = (length, chars) =>
    Array(length)
        .fill('')
        .map((v) => chars[Math.floor(Math.random() * chars.length)])
        .join('');
generateString(10, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');

验证字符串

判断一个值是否是字符串

const isString = (value) => Object.prototype.toString.call(value) === '[object String]';

驼峰式

字符串格式化

const toCamelCase = (str) => str.trim().replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''));
toCamelCase('background-color'); // backgroundColor
toCamelCase('-webkit-scrollbar-thumb'); // WebkitScrollbarThumb
toCamelCase('_hello_world'); // HelloWorld
toCamelCase('hello_world'); // helloWorld

单词数

统计单词数

const countWords = (str) => str.trim().split(/\s+/).length;
countWords('Hello World'); // 2
countWords('Hello    World'); // 2
countWords('  Hello  World  '); // 2

字符重复数

const characterCount = (str, char) => str.split(char).length - 1;
characterCount('192.168.1.1', '.'); // 3

文件拓展名

const ext = (fileName) => fileName.split('.').pop();
ext('data.json') // json

版本判断

function version(newv, oldv) {
    const v1 = newv.split('.');
    const v2 = oldv.split('.');
    const len = Math.max(v1.length, v2.length);

    for (let i = 0; i < len; i++) {
        const num1 = parseInt(v1[i] || 0);
        const num2 = parseInt(v2[i] || 0);
		if (num1 > num2) {
            return true;
        } else if (num1 < num2) {
            return false;
        }
    }
    return 0;
}
version('1.0.6', '1.0.5') // true

系统判断

function system() {
    try {
        let u = navigator.userAgent;
        let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1; //g
        let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
        if (isAndroid) return 'Android'
        else if (isIOS) return 'IOS'
        else return 'pc'
    } catch {
        console.log('navigator is not define')
    }
}

uuid

生成一个id

const uuid = (a) => (a ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid))
uuid()

时间操作

是否为今天

const isToday = (date) => date.toISOString().slice(0, 10) === new Date().toISOString().slice(0, 10)

秒数转换

将秒数转换为 hh:mm:ss 格式

const formatSeconds = (s) => new Date(s * 1000).toISOString().substr(11, 8)
formatSeconds(200) // 00:03:20

间隔天数

const diffDays = (date, otherDate) => Math.ceil(Math.abs(date - otherDate) / (1000 * 60 * 60 * 24))
diffDays(new Date('2014-12-19'), new Date('2020-01-01')); // 1839

间隔月数

const monthDiff = (startDate, endDate) => Math.max(0, (endDate.getFullYear() - startDate.getFullYear()) * 12 - startDate.getMonth() + endDate.getMonth())
monthDiff(new Date('2020-01-01'), new Date('2021-01-01')); // 12

日期排序

const sortDescending = (arr) => arr.sort((a, b) => a.getTime() > b.getTime());
sortDescending([new Date('2023/05/09'),new Date('2022/01/05')])

日期格式转换

当你需要将日期转换为为 YYYY-MM-DD 格式

const formatYmd = (date) => date.toISOString().slice(0, 10);
formatYmd(new Date())

DOM操作

重载页面

const reload = () => location.reload();
reload()

回到顶部

const goToTop = () => window.scrollTo(0, 0);
goToTop()

元素滚动

元素顺滑的滚动到可视区域的起点

const scrollToTop = (element) =>
  element.scrollIntoView({ behavior: "smooth", block: "start" })
scrollToTop(document.body)

元素顺滑的滚动到可视区域的终点

const scrollToBottom = (element) =>
  element.scrollIntoView({ behavior: "smooth", block: "end" })
  scrollToBottom(document.body)

文本粘贴

复制文本到粘贴板上

const copy = (text) => navigator.clipboard?.writeText && navigator.clipboard.writeText(text)
copy('需要粘贴的文本')

是否页面底部

判断是否在页面底部

const isAtBottom = () => document.documentElement.clientHeight + window.scrollY >= document.documentElement.scrollHeight;

元素位置

获取元素相对于文档的位置

const getPosition = (ele) => ((r = ele.getBoundingClientRect()), { left: r.left + window.scrollX, top: r.top + window.scrollY });
getPosition(document.body); // { left: 0, top: 0 }

其他操作

防抖函数

function debounce(fn, delay) {
    let timer = null //借助闭包
    return function () {
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(fn, delay)
    }
}

节流函数

function throttle(fn, delay) {
    let valid = true
    return function () {
       	if (!valid) {
            return false
        }
        valid = false
        setTimeout(() => {
            fn()
            valid = true;
        }, delay)
    }
}

单次函数

函数仅执行一次

const once = (fn) =>
    (
        (ran = false) =>
        () =>
            ran ? fn : ((ran = !ran), (fn = fn()))
    )()
let n = 0;
const incOnce = once(() => ++n);
incOnce(); // n = 1
incOnce(); // n = 1

Node操作

随机字符串

const randomStr = () => require('crypto').randomBytes(32).toString('hex');

调用接口

const axios = require("axios")
function getValue() {
    return new Promise((resolve, reject) => {
        axios.get("https://test.com").then((res) => {
            console.log(res.data)
            resolve(res.data)
        }).catch(() => {
            reject("fail")
        })
    })
}

下载文件

const fs = require('fs');
const request = require("request")
function download(url, outPath) {
    let stream = fs.createWriteStream(outPath)
    return new Promise(function (resolve, reject) {
        request(url).pipe(stream).on('close', function () {
            console.log(targetUrl + '下载完毕');
            resolve(targetUrl)
        });
    })
}

获取文件列表

const testFolder = './test';
const fs = require('fs');
fs.readdirSync(testFolder).forEach(file => {
  console.log(file);
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值