js代码片段
getValue
给定一个对象或数组,函数将返回指定路径的值,否则为 null。
const getValue = (obj, path) => path
.replace(/\[([^[\]]*)]/g, '.$1.')
.split('.')
.filter(prop => prop !== '')
.reduce((prev, next) => (
prev instanceof Object ? prev[next] : undefined
), obj)
getValue({ a: { b: c: 'd' } }, 'a.b.c') // = d
getValue({ a: { b: c: [1, 2] } }, 'a.b.c[1]') // = 2
clamp
确保值在指定范围内,否则“clamp”到最接近的最小值和最大值。
const clamp = (min, max, value) => {
if (min > max) throw new Error('min cannot be greater than max');
return value < min ?
min :
value > max ?
max :
value
}
clamp(0, 10, -5) // = 0
clamp(0, 10, 20) // = 10
sleep
在执行下一个操作之前等待指定的持续时间(以毫秒为单位)。
const sleep = duration => (
new Promise(resolve => {
const timeoutHandle = setTimeout(() => {
clearTimeout(timeoutHandle)
resolve()
}, duration)
})
)
await sleep(1000)
groupBy
根据键控功能对对象中的相关项进行分组和索引。
const groupBy = (fn, list) => (
list.reduce((prev, next) => ({
...prev,
[fn(next)]: [...(prev[fn(next)] || []), next]
}), {})
)
groupBy(vehicle => vehicle.make, [{
make: 'tesla',
model: '3'
},
{
make: 'tesla',
model: 'y'
},
{
make: 'ford',
model: 'mach-e'
},
])
// {
// tesla: [ { make: 'tesla', model: '3' }, { make: 'tesla', model: 'y' } ],
// ford: [ { make: 'ford', model: 'mach-e' } ]
// }
collectBy
根据键控功能创建包含相关项目的子列表。
import groupBy from './groupBy'
const collectBy = (fn, list) => Object.values(groupBy(fn, list))
collectBy(vehicle => vehicle.make, [
{ make: 'tesla', model: '3' },
{ make: 'tesla', model: 'y' },
{ make: 'ford', model: 'mach-e' },
])
// [
// [ { make: 'tesla', model: '3' }, { make: 'tesla', model: 'y' } ],
// [ { make: 'ford', model: 'mach-e' } ]
// ]
head
获取列表的第一个元素。这个函数对于编写干净易读的代码很有用。
const head = list => list[0]
head([1, 2, 3]) // = 1
head([]) // = undefined
tail
获取列表中除第一个元素之外的所有元素。这个函数对于编写干净易读的代码很有用。
const tail = list => list.slice(1)
tail([1, 2, 3]) // = [2, 3]
tail([]) // = []
flatten
通过递归地从嵌套子列表中提取所有项目来创建一个平面列表。
const flatten = list => list.reduce((prev, next) => ([
...prev,
...(Array.isArray(next) ? flatten(next) : [next])
]), [])
flatten([[1, 2, [3, 4], 5, [6, [7, 8]]]])
// [
// 1, 2, 3, 4,
// 5, 6, 7, 8
// ]
intersectionBy
查找键控函数定义的两个列表中存在的所有值。
const intersectionBy = (fn, listA, listB) => {
const b = new Set(listB.map(fn))
return listA.filter(val => b.has(fn(val)))
}
intersectionBy(v => v, [1, 2, 3], [2, 3, 4]) // = [2, 3]
intersectionBy(v => v, [{ a: 1 }, { a: 2 }], [{ a: 2}, { a: 3 }, { a: 4 }]) // = [{ a: 2 }]
indexBy
通过键控函数确定的值对列表中的每个元素进行索引。
const indexBy = (fn, list) => list.reduce((pre, next) => ({
...pre,
[fn(next)]: next
}), {})
indexBy(val => val.a, [{ a: 1 }, { a: 2 }, { a: 3 }])
// { '1': { a: 1 }, '2': { a: 2 }, '3': { a: 3 } }
differenceBy
查找第一个列表中不存在于第二个列表中的所有项目,由键控功能确定。
import indexBy from './indexBy'
const differenceBy = (fn, listA, listB) => {
const bIndex = indexBy(fn, listB)
return listA.filter(val => !bIndex[fn(val)])
}
differenceBy(val => val, [1, 2, 3], [3, 4, 5])
// [ 1, 2 ]
differenceBy(
vehicle => vehicle.make,
[{ make: 'tesla' }, { make: 'ford' }, { make: 'gm' }],
[{ make: 'tesla' }, { make: 'bmw' }, { make: 'audi' }],
)
// [ { make: 'ford' }, { make: 'gm' } ]
recoverWith
如果给定函数抛出错误,则返回默认值。
const recoverWith = async (defaultValue, fn, ...args) => {
try {
const result = await fn(...args)
return result
} catch (_e) {
return defaultValue
}
}
recoverWith('A', val => val, 'B') // = B
recoverWith('A', () => { throw new Error() }) // A
distance
计算两点 p1 和 p2 之间的距离。
const distance = ([x0, y0], [x1, y1]) => (
Math.hypot(x1 - x0, y1 - y0)
)
distance([0, 1], [5, 4]) // = 5.8309518948453
dropWhile
从列表中删除元素,从第一个元素开始,直到满足 som 谓词。
const dropWhile = (pred, list) => {
let index = 0
list.every(elem => {
index++
return pred(elem)
})
return list.slice(index-1)
}
dropWhile(val => (val < 5), [1, 2, 3, 4, 5, 6, 7]) // = [ 5, 6, 7 ]
sumBy
给定一些产生每个元素的单独值的函数,计算列表中所有元素的总和。
const sumBy = (fn, list) => list.reduce((prev, next) => prev + fn(next), 0)
sumBy(product => product.price, [
{ name: 'pizza', price: 10 },
{ name: 'pepsi', price: 5 },
{ name: 'salad', price: 5 }
]) // = 20
升序
给定一个评估函数,创建一个升序比较器函数。
const ascending = fn => (a, b) => {
const valA = fn(a)
const valB = fn(b)
return valA < valB ? -1 : valA > valB ? 1 : 0
}
const byPrice = ascending(val => val.price)
[{ price: 300 }, { price: 100 }, { price: 200 }].sort(byPrice)
// [ { price: 100 }, { price: 200 }, { price: 300 } ]
降序
给定一个评估函数,创建一个降序比较器函数。
const descending = fn => (a, b) => {
const valA = fn(b)
const valB = fn(a)
return valA < valB ? -1 : valA > valB ? 1 : 0;
}
const byPrice = descending(val => val.price)
[{ price: 300 }, { price: 100 }, { price: 200 }].sort(byPrice)
// [ { price: 300 }, { price: 200 }, { price: 100 } ]
findKey
在满足给定predicate的索引中找到第一个键值。
const findKey = (predicate, index) => Object
.keys(index)
.find(key => predicate(index[key], key, index))
findKey(
car => !car.available,
{
tesla: { available: true },
ford: { available: false },
gm: { available: true }
},
) // = ford
bifurcateBy
将给定列表的值拆分为两个列表,一个包含predicate函数评估为真值的值,另一个包含假值。
const bifurcateBy = (predicate, list) => list.reduce((acc, val, i) => (
acc[predicate(val, i) ? 0 : 1].push(val), acc),
[[], []]
)
bifurcateBy(val => val > 0, [-1, 2, -3, 4])
// [ [ 2, 4 ], [ -1, -3 ] ]
pipe
执行从左到右的功能组合。所有额外的参数都将传递给列表中的第一个函数,因此可以有任何数量。结果将在第二个传递,第二个的结果将传递给第三个,……以此类推,直到处理完所有函数。
const pipe = (functions, ...args) => (
functions.reduce(
(prev, next) => Array.isArray(prev) ? next(...prev) : next(prev),
args)
)
pipe([Math.abs, Math.floor, val => -val], 4.20) // = -4
pipe([(a, b) => a - b, Math.abs], 5, 10) // = 5
检查日期是否为工作日
const isWeekday = d => d.getDay() % 6 !== 0
isWeekday(new Date(2022, 7, 14)) // = false
isWeekday(new Date(2022, 7, 16)) // = true
反转字符串
const reverse = s => s.split('').reverse().join('')
reverse('mkimq') // = qmikm
检查一个数字是否为偶数。
const isEven = n => n % 2 === 0
isEven(2) // = true
isEven(3) // = false
大写字符串
const capitalize = s => s.charAt(0).toUpperCase() + s.slice(1)
capitalize('mkimq') // = Mkimq
检查数组是否为空
const isArrayEmpty = arr => Array.isArray(arr) && !arr.length
isArrayEmpty([]) // = true
isArrayEmpty([1, 2, 3]) // = false
检查对象/数组是否为空
const isObjectEmpty = obj => obj && Object.keys(obj).length === 0
isObjectEmpty({}) // = true
isObjectEmpty({ foo: 'bar' }) // = false
随机生成整数
const randomInteger = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
randomInteger(1, 10) // = 7
生成随机布尔值
const randomBoolean = () => Math.random() >= 0.5
randomBoolean() // = true
切换布尔值
切换布尔值,变假为真,变真为假。
const toggleBoolean = val => !val
toggleBoolean(false) // = true
转换
将字符串转换为带“-”的连字字符串。
const slugify = str => str.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]+/g, '')
slugify('Hello World') // = hello-world
生成随数组组合
随机生成一组任何类型的数组。
const shuffleArray = arr => arr.sort(() => Math.random() - 0.5)
shuffleArray([1, 2, 3, 4]) // = [2, 4, 1, 3]
将连字字符串转换为骆峰字符串
const snakeToCamel = s => s.toLowerCase().replace(/(_\w)/g, (w) => w.toUpperCase().substring(1))
snakeToCamel('foo_bar') // fooBar
随机数字符串
根据当前时间生成随机数字符串。
const randomNumberString = () => new Date().getTime() + Math.random().toString(36).slice(2)
randomNumberString() // = 1657782974681lupy1hcmb8e
将数字转换为字符/字母
const numberToLetter = value => String.fromCharCode(94 + value)
numberToLetter(4) // = b
生成随机的十六进制颜色
const randomHexColor = () => `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, '0')}`
randomHexColor() // = #a9f759
输入一个值,返回其数据类型
function type(para) {
return Object.prototype.toString.call(para)
}
数组去重
function unique1(arr) {
return [...new Set(arr)]
}
function unique2(arr) {
var obj = {};
return arr.filter(ele => {
if (!obj[ele]) {
obj[ele] = true;
return true;
}
})
}
function unique3(arr) {
var result = [];
arr.forEach(ele => {
if (result.indexOf(ele) == -1) {
result.push(ele)
}
})
return result;
}
字符串去重
String.prototype.unique = function () {
var obj = {},
str = '',
len = this.length;
for (var i = 0; i < len; i++) {
if (!obj[this[i]]) {
str += this[i];
obj[this[i]] = true;
}
}
return str;
}
//去除连续的字符串
function uniq(str) {
return str.replace(/(\w)\1+/g, '$1')
}
深拷贝 浅拷贝
// 简单粗暴版本
function deepClone(obj) {
let res = {};
// 类型判断的通用方法
function getType(obj) {
return Object.prototype.toString.call(obj).replaceAll(new RegExp(/\[|\]|object /g), "");
}
const type = getType(obj);
const reference = ["Set", "WeakSet", "Map", "WeakMap", "RegExp", "Date", "Error"];
if (type === "Object") {
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
res[key] = deepClone(obj[key]);
}
}
} else if (type === "Array") {
console.log('array obj', obj);
obj.forEach((e, i) => {
res[i] = deepClone(e);
});
}
else if (type === "Date") {
res = new Date(obj);
} else if (type === "RegExp") {
res = new RegExp(obj);
} else if (type === "Map") {
res = new Map(obj);
} else if (type === "Set") {
res = new Set(obj);
} else if (type === "WeakMap") {
res = new WeakMap(obj);
} else if (type === "WeakSet") {
res = new WeakSet(obj);
}else if (type === "Error") {
res = new Error(obj);
}
else {
res = obj;
}
return res;
}
// 合并冗余代码
function deepClone(obj) {
let res = null;
// 类型判断的通用方法
function getType(obj) {
return Object.prototype.toString.call(obj).replaceAll(new RegExp(/\[|\]|object /g), "");
}
const type = getType(obj);
const reference = ["Set", "WeakSet", "Map", "WeakMap", "RegExp", "Date", "Error"];
if (type === "Object") {
res = {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
res[key] = deepClone(obj[key]);
}
}
} else if (type === "Array") {
console.log('array obj', obj);
res = [];
obj.forEach((e, i) => {
res[i] = deepClone(e);
});
}
// 优化此部分冗余判断
// else if (type === "Date") {
// res = new Date(obj);
// } else if (type === "RegExp") {
// res = new RegExp(obj);
// } else if (type === "Map") {
// res = new Map(obj);
// } else if (type === "Set") {
// res = new Set(obj);
// } else if (type === "WeakMap") {
// res = new WeakMap(obj);
// } else if (type === "WeakSet") {
// res = new WeakSet(obj);
// }else if (type === "Error") {
// res = new Error(obj);
//}
else if (reference.includes(type)) {
res = new obj.constructor(obj);
} else {
res = obj;
}
return res;
}
// 进一步优化
// 判断类型的方法移到外部,避免递归过程中多次执行
const judgeType = origin => {
return Object.prototype.toString.call(origin).replaceAll(new RegExp(/\[|\]|object /g), "");
};
const reference = ["Set", "WeakSet", "Map", "WeakMap", "RegExp", "Date", "Error"];
function deepClone(obj) {
// 定义新的对象,最后返回
//通过 obj 的原型创建对象
const cloneObj = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
// 遍历对象,克隆属性
for (let key of Reflect.ownKeys(obj)) {
const val = obj[key];
const type = judgeType(val);
if (reference.includes(type)) {
newObj[key] = new val.constructor(val);
} else if (typeof val === "object" && val !== null) {
// 递归克隆
newObj[key] = deepClone(val);
} else {
// 基本数据类型和function
newObj[key] = val;
}
}
return newObj;
}
// 上面代码的基础上进行了改造
function deepClone(obj) {
let res = null;
const reference = [Date, RegExp, Set, WeakSet, Map, WeakMap, Error];
if (reference.includes(obj?.constructor)) {
res = new obj.constructor(obj);
} else if (Array.isArray(obj)) {
res = [];
obj.forEach((e, i) => {
res[i] = deepClone(e);
});
} else if (typeof obj === "object" && obj !== null) {
res = {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
res[key] = deepClone(obj[key]);
}
}
} else {
res = obj;
}
return res;
}
// 最后,还有循环引用问题,避免出现无线循环的问题。
// 我们用hash来存储已经加载过的对象,如果已经存在的对象,就直接返回。
function deepClone(obj, hash = new WeakMap()) {
if (hash.has(obj)) {
return obj;
}
let res = null;
const reference = [Date, RegExp, Set, WeakSet, Map, WeakMap, Error];
if (reference.includes(obj?.constructor)) {
res = new obj.constructor(obj);
} else if (Array.isArray(obj)) {
res = [];
obj.forEach((e, i) => {
res[i] = deepClone(e);
});
} else if (typeof obj === "object" && obj !== null) {
res = {};
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
res[key] = deepClone(obj[key]);
}
}
hash.set(obj, res);
} else {
res = obj;
}
return res;
}
//深克隆(深克隆不考虑函数)
function deepClone(obj, result) {
var result = result || {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
if (typeof obj[prop] == 'object' && obj[prop] !== null) {
// 引用值(obj/array)且不为null
if (Object.prototype.toString.call(obj[prop]) == '[object Object]') {
// 对象
result[prop] = {};
} else {
// 数组
result[prop] = [];
}
deepClone(obj[prop], result[prop])
} else {
// 原始值或func
result[prop] = obj[prop]
}
}
}
return result;
}
// 深浅克隆是针对引用值
function deepClone(target) {
if (typeof (target) !== 'object') {
return target;
}
var result;
if (Object.prototype.toString.call(target) == '[object Array]') {
// 数组
result = []
} else {
// 对象
result = {};
}
for (var prop in target) {
if (target.hasOwnProperty(prop)) {
result[prop] = deepClone(target[prop])
}
}
return result;
}
// 无法复制函数
var o1 = jsON.parse(jsON.stringify(obj1));
reverse底层原理和扩展
// 改变原数组
Array.prototype.myReverse = function () {
var len = this.length;
for (var i = 0; i < len; i++) {
var temp = this[i];
this[i] = this[len - 1 - i];
this[len - 1 - i] = temp;
}
return this;
}
圣杯模式的继承
function inherit(Target, Origin) {
function F() {};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
// 最终的原型指向
Target.prop.uber = Origin.prototype;
}
找出字符串中第一次只出现一次的字母
String.prototype.firstAppear = function () {
var obj = {},
len = this.length;
for (var i = 0; i < len; i++) {
if (obj[this[i]]) {
obj[this[i]]++;
} else {
obj[this[i]] = 1;
}
}
for (var prop in obj) {
if (obj[prop] == 1) {
return prop;
}
}
}
找元素的第n级父元素
function parents(ele, n) {
while (ele && n) {
ele = ele.parentElement ? ele.parentElement : ele.parentNode;
n--;
}
return ele;
}
返回元素的第n个兄弟节点
function retSibling(e, n) {
while (e && n) {
if (n > 0) {
if (e.nextElementSibling) {
e = e.nextElementSibling;
} else {
for (e = e.nextSibling; e && e.nodeType !== 1; e = e.nextSibling);
}
n--;
} else {
if (e.previousElementSibling) {
e = e.previousElementSibling;
} else {
for (e = e.previousElementSibling; e && e.nodeType !== 1; e = e.previousElementSibling);
}
n++;
}
}
return e;
}
封装mychildren,解决浏览器的兼容问题
function myChildren(e) {
var children = e.childNodes,
arr = [],
len = children.length;
for (var i = 0; i < len; i++) {
if (children[i].nodeType === 1) {
arr.push(children[i])
}
}
return arr;
}
判断元素有没有子元素
function hasChildren(e) {
var children = e.childNodes,
len = children.length;
for (var i = 0; i < len; i++) {
if (children[i].nodeType === 1) {
return true;
}
}
return false;
}
一个元素插入到另一个元素的后面
Element.prototype.insertAfter = function (target, elen) {
var nextElen = elen.nextElenmentSibling;
if (nextElen == null) {
this.appendChild(target);
} else {
this.insertBefore(target, nextElen);
}
}
返回当前的时间(年月日时分秒)
function getDateTime() {
var date = new Date(),
year = date.getFullYear(),
month = date.getMonth() + 1,
day = date.getDate(),
hour = date.getHours() + 1,
minute = date.getMinutes(),
second = date.getSeconds();
month = checkTime(month);
day = checkTime(day);
hour = checkTime(hour);
minute = checkTime(minute);
second = checkTime(second);
function checkTime(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
return "" + year + "年" + month + "月" + day + "日" + hour + "时" + minute + "分" + second + "秒"
}
获得视口的尺寸
function getViewportOffset() {
if (window.innerWidth) {
return {
w: window.innerWidth,
h: window.innerHeight
}
} else {
// ie8及其以下
if (document.compatMode === "BackCompat") {
// 怪异模式
return {
w: document.body.clientWidth,
h: document.body.clientHeight
}
} else {
// 标准模式
return {
w: document.documentElement.clientWidth,
h: document.documentElement.clientHeight
}
}
}
}
获取任一元素的任意属性
function getStyle(elem, prop) {
return window.getComputedStyle ? window.getComputedStyle(elem, null)[prop] : elem.currentStyle[prop]
}
绑定事件的兼容代码
function addEvent(elem, type, handle) {
if (elem.addEventListener) { //非ie和非ie9
elem.addEventListener(type, handle, false);
} else if (elem.attachEvent) { //ie6到ie8
elem.attachEvent('on' + type, function () {
handle.call(elem);
})
} else {
elem['on' + type] = handle;
}
}
解绑事件
function removeEvent(elem, type, handle) {
if (elem.removeEventListener) { //非ie和非ie9
elem.removeEventListener(type, handle, false);
} else if (elem.detachEvent) { //ie6到ie8
elem.detachEvent('on' + type, handle);
} else {
elem['on' + type] = null;
}
}
取消冒泡的兼容代码
function stopBubble(e) {
if (e && e.stopPropagation) {
e.stopPropagation();
} else {
window.event.cancelBubble = true;
}
}
检验字符串是否是回文
function isPalina(str) {
if (Object.prototype.toString.call(str) !== '[object String]') {
return false;
}
var len = str.length;
for (var i = 0; i < len / 2; i++) {
if (str[i] != str[len - 1 - i]) {
return false;
}
}
return true;
}
检验字符串是否是回文
function isPalindrome(str) {
str = str.replace(/\W/g, '').toLowerCase();
console.log(str)
return (str == str.split('').reverse().join(''))
}
兼容getElementsByClassName方法
Element.prototype.getElementsByClassName = Document.prototype.getElementsByClassName = function (_className) {
var allDomArray = document.getElementsByTagName('*');
var lastDomArray = [];
function trimSpace(strClass) {
var reg = /\s+/g;
return strClass.replace(reg, ' ').trim()
}
for (var i = 0; i < allDomArray.length; i++) {
var classArray = trimSpace(allDomArray[i].className).split(' ');
for (var j = 0; j < classArray.length; j++) {
if (classArray[j] == _className) {
lastDomArray.push(allDomArray[i]);
break;
}
}
}
return lastDomArray;
}
运动函数
function animate(obj, json, callback) {
clearInterval(obj.timer);
var speed,
current;
obj.timer = setInterval(function () {
var lock = true;
for (var prop in json) {
if (prop == 'opacity') {
current = parseFloat(window.getComputedStyle(obj, null)[prop]) * 100;
} else {
current = parseInt(window.getComputedStyle(obj, null)[prop]);
}
speed = (json[prop] - current) / 7;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (prop == 'opacity') {
obj.style[prop] = (current + speed) / 100;
} else {
obj.style[prop] = current + speed + 'px';
}
if (current != json[prop]) {
lock = false;
}
}
if (lock) {
clearInterval(obj.timer);
typeof callback == 'function' ? callback() : '';
}
}, 30)
}
弹性运动
function ElasticMovement(obj, target) {
clearInterval(obj.timer);
var iSpeed = 40,
a, u = 0.8;
obj.timer = setInterval(function () {
a = (target - obj.offsetLeft) / 8;
iSpeed = iSpeed + a;
iSpeed = iSpeed * u;
if (Math.abs(iSpeed) <= 1 && Math.abs(a) <= 1) {
console.log('over')
clearInterval(obj.timer);
obj.style.left = target + 'px';
} else {
obj.style.left = obj.offsetLeft + iSpeed + 'px';
}
}, 30);
}
封装自己的forEach方法
Array.prototype.myForEach = function (func, obj) {
var len = this.length;
var _this = arguments[1] ? arguments[1] : window;
// var _this=arguments[1]||window;
for (var i = 0; i < len; i++) {
func.call(_this, this[i], i, this)
}
}
封装自己的filter方法
Array.prototype.myFilter = function (func, obj) {
var len = this.length;
var arr = [];
var _this = arguments[1] || window;
for (var i = 0; i < len; i++) {
func.call(_this, this[i], i, this) && arr.push(this[i]);
}
return arr;
}
数组map方法
Array.prototype.myMap = function (func) {
var arr = [];
var len = this.length;
var _this = arguments[1] || window;
for (var i = 0; i < len; i++) {
arr.push(func.call(_this, this[i], i, this));
}
return arr;
}
数组every方法
Array.prototype.myEvery = function (func) {
var flag = true;
var len = this.length;
var _this = arguments[1] || window;
for (var i = 0; i < len; i++) {
if (func.apply(_this, [this[i], i, this]) == false) {
flag = false;
break;
}
}
return flag;
}
数组reduce方法
Array.prototype.myReduce = function (func, initialValue) {
var len = this.length,
nextValue,
i;
if (!initialValue) {
// 没有传第二个参数
nextValue = this[0];
i = 1;
} else {
// 传了第二个参数
nextValue = initialValue;
i = 0;
}
for (; i < len; i++) {
nextValue = func(nextValue, this[i], i, this);
}
return nextValue;
}
获取url中的参数
function getWindonHref() {
var sHref = window.location.href;
var args = sHref.split('?');
if (args[0] === sHref) {
return '';
}
var hrefarr = args[1].split('#')[0].split('&');
var obj = {};
for (var i = 0; i < hrefarr.length; i++) {
hrefarr[i] = hrefarr[i].split('=');
obj[hrefarr[i][0]] = hrefarr[i][1];
}
return obj;
}
数组排序
// 快排 [left] + min + [right]
function quickArr(arr) {
if (arr.length <= 1) {
return arr;
}
var left = [],
right = [];
var pIndex = Math.floor(arr.length / 2);
var p = arr.splice(pIndex, 1)[0];
for (var i = 0; i < arr.length; i++) {
if (arr[i] <= p) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 递归
return quickArr(left).concat([p], quickArr(right));
}
// 冒泡
function bubbleSort(arr) {
for (var i = 0; i < arr.length - 1; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr;
}
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len - 1; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
遍历Dom树
// 给定页面上的DOM元素,将访问元素本身及其所有后代(不仅仅是它的直接子元素)
// 对于每个访问的元素,函数讲元素传递给提供的回调函数
function traverse(element, callback) {
callback(element);
var list = element.children;
for (var i = 0; i < list.length; i++) {
traverse(list[i], callback);
}
}
原生js封装ajax
function ajax(method, url, callback, data, flag) {
var xhr;
flag = flag || true;
method = method.toUpperCase();
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
xhr = new ActiveXObject('Microsoft.XMLHttp');
}
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log(2)
callback(xhr.responseText);
}
}
if (method == 'GET') {
var date = new Date(),
timer = date.getTime();
xhr.open('GET', url + '?' + data + '&timer' + timer, flag);
xhr.send()
} else if (method == 'POST') {
xhr.open('POST', url, flag);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);
}
}
异步加载script
function loadScript(url, callback) {
var oscript = document.createElement('script');
if (oscript.readyState) { // ie8及以下版本
oscript.onreadystatechange = function () {
if (oscript.readyState === 'complete' || oscript.readyState === 'loaded') {
callback();
}
}
} else {
oscript.onload = function () {
callback()
};
}
oscript.src = url;
document.body.appendChild(oscript);
}
cookie管理
var cookie = {
set: function (name, value, time) {
document.cookie = name + '=' + value + '; max-age=' + time;
return this;
},
remove: function (name) {
return this.setCookie(name, '', -1);
},
get: function (name, callback) {
var allCookieArr = document.cookie.split('; ');
for (var i = 0; i < allCookieArr.length; i++) {
var itemCookieArr = allCookieArr[i].split('=');
if (itemCookieArr[0] === name) {
return itemCookieArr[1]
}
}
return undefined;
}
}
实现bind()方法
Function.prototype.myBind = function (target) {
var target = target || window;
var _args1 = [].slice.call(arguments, 1);
var self = this;
var temp = function () {};
var F = function () {
var _args2 = [].slice.call(arguments, 0);
var parasArr = _args1.concat(_args2);
return self.apply(this instanceof temp ? this : target, parasArr)
}
temp.prototype = self.prototype;
F.prototype = new temp();
return F;
}
实现call()方法
Function.prototype.myCall = function () {
var ctx = arguments[0] || window;
ctx.fn = this;
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i])
}
var result = ctx.fn(...args);
delete ctx.fn;
return result;
}
实现apply()方法
Function.prototype.myApply = function () {
var ctx = arguments[0] || window;
ctx.fn = this;
if (!arguments[1]) {
var result = ctx.fn();
delete ctx.fn;
return result;
}
var result = ctx.fn(...arguments[1]);
delete ctx.fn;
return result;
}
防抖
function debounce(handle, delay) {
var timer = null;
return function () {
var _self = this,
_args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
handle.apply(_self, _args)
}, delay)
}
}
节流
function throttle(handler, wait) {
var lastTime = 0;
return function (e) {
var nowTime = new Date().getTime();
if (nowTime - lastTime > wait) {
handler.apply(this, arguments);
lastTime = nowTime;
}
}
}
requestAnimFrame兼容性方法
window.requestAnimFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
cancelAnimFrame兼容性方法
window.cancelAnimFrame = (function () {
return window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
function (id) {
window.clearTimeout(id);
};
})();
jsonp底层方法
function jsonp(url, callback) {
var oscript = document.createElement('script');
if (oscript.readyState) { // ie8及以下版本
oscript.onreadystatechange = function () {
if (oscript.readyState === 'complete' || oscript.readyState === 'loaded') {
callback();
}
}
} else {
oscript.onload = function () {
callback()
};
}
oscript.src = url;
document.body.appendChild(oscript);
}
获取url上的参数
function getUrlParam(sUrl, sKey) {
var result = {};
sUrl.replace(/(\w+)=(\w+)(?=[&|#])/g, function (ele, key, val) {
if (!result[key]) {
result[key] = val;
} else {
var temp = result[key];
result[key] = [].concat(temp, val);
}
})
if (!sKey) {
return result;
} else {
return result[sKey] || '';
}
}
格式化时间
function formatDate(t, str) {
var obj = {
yyyy: t.getFullYear(),
yy: ("" + t.getFullYear()).slice(-2),
M: t.getMonth() + 1,
MM: ("0" + (t.getMonth() + 1)).slice(-2),
d: t.getDate(),
dd: ("0" + t.getDate()).slice(-2),
H: t.getHours(),
HH: ("0" + t.getHours()).slice(-2),
h: t.getHours() % 12,
hh: ("0" + t.getHours() % 12).slice(-2),
m: t.getMinutes(),
mm: ("0" + t.getMinutes()).slice(-2),
s: t.getSeconds(),
ss: ("0" + t.getSeconds()).slice(-2),
w: ['日', '一', '二', '三', '四', '五', '六'][t.getDay()]
};
return str.replace(/([a-z]+)/ig, function ($1) {
return obj[$1]
});
}
函数柯里化
//是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术
function curryIt(fn) {
var length = fn.length,
args = [];
var result = function (arg) {
args.push(arg);
length--;
if (length <= 0) {
return fn.apply(this, args);
} else {
return result;
}
}
return result;
}
大数相加
function sumBigNumber(a, b) {
var res = '', //结果
temp = 0; //按位加的结果及进位
a = a.split('');
b = b.split('');
while (a.length || b.length || temp) {
//~~按位非 1.类型转换,转换成数字 2.~~undefined==0
temp += ~~a.pop() + ~~b.pop();
res = (temp % 10) + res;
temp = temp > 9;
}
return res.replace(/^0+/, '');
}
单例模式
function getSingle(func) {
var result;
return function () {
if (!result) {
result = new func(arguments);
}
return result;
}
}
复制到剪贴板
const copyToClipboard = (text) => navigator.clipboard?.writeText && navigator.clipboard.writeText(text)
检测黑暗模式
const isDarkMode = () => window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
滚动到顶部
const scrollToTop = (element) => element.scrollIntoView({ behavior: "smooth", block: "start" })
滚动到底部
const scrollToBottom = (element) => element.scrollIntoView({ behavior: "smooth", block: "end" })
检测互联网带宽
navigator.connection.downlink
振动你的手机
//vibrating the device for 600 milliseconds
window.navigator.vibrate(600)
获取文件后缀名
/**
* 获取文件后缀名
* @param {String} filename
*/
export function getExt(filename) {
if (typeof filename == 'string') {
return filename
.split('.')
.pop()
.toLowerCase()
} else {
throw new Error('filename must be a string type')
}
}
生成随机字符串
/**
* 生成随机id
* @param {*} length
* @param {*} chars
*/
export function uuid(length, chars) {
chars =
chars ||
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
length = length || 8
var result = ''
for (var i = length; i > 0; --i)
result += chars[Math.floor(Math.random() * chars.length)]
return result
}
对象转化为FormData对象
/**
* 对象转化为formdata
* @param {Object} object
*/
export function getFormData(object) {
const formData = new FormData()
Object.keys(object).forEach(key => {
const value = object[key]
if (Array.isArray(value)) {
value.forEach((subValue, i) =>
formData.append(key + `[${i}]`, subValue)
)
} else {
formData.append(key, object[key])
}
})
return formData
}
检查元素是否被聚焦
const hasFocus = (ele) => ele === document.activeElement;
获取元素的所有兄弟元素
const siblings = (ele) =>[].slice.call(ele.parentNode.children).filter((child) => child !== ele);
获取选定的文本
const getSelectedText = () => window.getSelection().toString();
返回上一个页面
history.back();
// Or
history.go(-1);
清除所有 cookie
const clearCookies = () => document.cookie
.split(';')
.forEach((c) =>(document.cookie = c.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`)));
将 cookie 转换为对象
const cookies = document.cookie.split(';').map((item) => item.split('=')).reduce((acc, [k, v]) => (acc[k.trim().replace('"', '')] = v) && acc, {});
比较两个数组
// `a` and `b` are arrays
const isEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
// Or
const isEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
// Examples
isEqual([1, 2, 3], [1, 2, 3]); // true
isEqual([1, 2, 3], [1, '2', 3]); // false
将对象数组转换为单个对象
const toObject = (arr, key) => arr.reduce((a, b) => ({ ...a, [b[key]]: b }), {});
// Or
const toObject = (arr, key) => Object.fromEntries(arr.map((it) => [it[key], it]));
// Example
toObject([
{ id: '1', name: 'Alpha', gender: 'Male' },
{ id: '2', name: 'Bravo', gender: 'Male' },
{ id: '3', name: 'Charlie', gender: 'Female' }],
'id');
/*
{
'1': { id: '1', name: 'Alpha', gender: 'Male' },
'2': { id: '2', name: 'Bravo', gender: 'Male' },
'3': { id: '3', name: 'Charlie', gender: 'Female' }
}
*/
按对象数组的属性计数
const countBy = (arr, prop) => arr.reduce((prev, curr) => ((prev[curr[prop]] = ++prev[curr[prop]] || 1), prev), {});
// Example
countBy([
{ branch: 'audi', model: 'q8', year: '2019' },
{ branch: 'audi', model: 'rs7', year: '2020' },
{ branch: 'ford', model: 'mustang', year: '2019' },
{ branch: 'ford', model: 'explorer', year: '2020' },
{ branch: 'bmw', model: 'x7', year: '2020' },
],
'branch');
// { 'audi': 2, 'ford': 2, 'bmw': 1 }
检查数组是否为空
const isNotEmpty = (arr) => Array.isArray(arr) && Object.keys(arr).length > 0;
// Examples
isNotEmpty([]); // false
isNotEmpty([1, 2, 3]); // true
检查多个对象是否相等
const isEqual = (...objects) => objects.every((obj) => JSON.stringify(obj) === JSON.stringify(objects[0]));
// Examples
isEqual({ foo: 'bar' }, { foo: 'bar' }); // true
isEqual({ foo: 'bar' }, { bar: 'foo' }); // false
从对象数组中提取属性的值
const pluck = (objs, property) => objs.map((obj) => obj[property]);
// Example
pluck([
{ name: 'John', age: 20 },
{ name: 'Smith', age: 25 },
{ name: 'Peter', age: 30 },
],
'name');
// ['John', 'Smith', 'Peter']
反转对象的键和值
const invert = (obj) => Object.keys(obj).reduce((res, k) => Object.assign(res, { [obj[k]]: k }), {});
// Or
const invert = (obj) => Object.fromEntries(Object.entries(obj).map(([k, v]) => [v, k]));
// Example
invert({ a: '1', b: '2', c: '3' }); // { 1: 'a', 2: 'b', 3: 'c' }
从对象中删除所有空和未定义的属性
const removeNullUndefined = (obj) =>
Object.entries(obj)
.reduce((a, [k, v]) => (v == null ? a : ((a[k] = v), a)), {});
// Or
const removeNullUndefined = (obj) =>
Object.entries(obj)
.filter(([_, v]) => v != null)
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
// Or
const removeNullUndefined = (obj) => Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null));
// Example
removeNullUndefined({
foo: null,
bar: undefined,
fuzz: 42});
// { fuzz: 42 }
按属性对对象进行排序
const sort = (obj) =>
Object.keys(obj)
.sort()
.reduce((p, c) => ((p[c] = obj[c]), p), {});
// Example
const colors = {
white: '#ffffff',
black: '#000000',
red: '#ff0000',
green: '#008000',
blue: '#0000ff',
};
sort(colors);
/*
{
black: '#000000',
blue: '#0000ff',
green: '#008000',
red: '#ff0000',
white: '#ffffff',
}
*/
检查一个对象是否是一个 Promise
const isPromise = (obj) =>
!!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
检查对象是否为数组
const isArray = (obj) => Array.isArray(obj);
检查代码是否在 Node.js 中运行
const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
检查代码是否在浏览器中运行
const isBrowser = typeof window === 'object' && typeof document === 'object';
将 URL 参数转换为对象
const getUrlParams = (query) =>Array.from(new URLSearchParams(query)).reduce((p, [k, v]) => Object.assign({}, p, { [k]: p[k] ? (Array.isArray(p[k]) ? p[k] : [p[k]]).concat(v) : v }),{});
// Examples
getUrlParams(location.search); // Get the parameters of the current URL
getUrlParams('foo=Foo&bar=Bar'); // { foo: "Foo", bar: "Bar" }
// Duplicate key
getUrlParams('foo=Foo&foo=Fuzz&bar=Bar'); // { foo: ["Foo", "Fuzz"], bar: "Bar" }
使用 Node crypto 模块生成随机字符串
const randomStr = () => require('crypto').randomBytes(32).toString('hex')
为 Promise 添加超时限制
function addTimeoutToPromise(targetPromise, timeout) {
let timeoutHandle;
const timeoutLimitPromise = new Promise((res, rej) => {
timeoutHandle = setTimeout(
() => rej(new Error("Timeout exceeded")),
timeout
);
});
return Promise.race([targetPromise, timeoutLimitPromise]).then((res) => {
clearTimeout(timeoutHandle);
return res;
});
}
按顺序完成 Promise
function completeInSequence(promiseFactories) {
return promiseFactories.reduce(
(chain, promiseFactory) => chain.then(() => promiseFactory()),
Promise.resolve()
);
}
// 使用
completeInSequence([
() => sleep(1000).then(() => console.log('1')),
() => sleep(3000).then(() => console.log('2')),
() => sleep(1000).then(() => console.log('3'))
])
只同时完成 N 个 Promise
function completePromisesInPool(
promiseFactories,
maxPoolSize
) {
return new Promise((res) => {
let nextPromise = 0;
const runPromise = () => {
nextPromise++;
if (nextPromise > promiseFactories.length) {
res();
return;
}
return promiseFactories[nextPromise - 1]()
.then(() => runPromise())
}
Array.from({
length: maxPoolSize
})
.map(() => runPromise());
})
}
completePromisesInPool([
() => delay(1000).then(() => console.log('1')),
() => delay(1000).then(() => console.log('2')),
() => delay(1000).then(() => console.log('3')),
() => delay(1000).then(() => console.log('4')),
() => delay(1000).then(() => console.log('5')),
() => delay(1000).then(() => console.log('6'))
], 2)
下载一个excel文档
function download(link, name) {
if(!name) {
name = link.slice(link.lastIndexOf('/') + 1)
}
const eleLink = document.createElement('a')
eleLink.download = name
eleLink.style.display = 'none'
eleLink.href = link
document.body.appendChild(eleLink)
eleLink.click()
document.body.removeChild(eleLink)
}
在浏览器中自定义下载一些内容
场景:我想下载一些DOM内容,我想下载一个JSON文件
/**
* 浏览器下载静态文件
* @param {String} name 文件名
* @param {String} content 文件内容
*/
function downloadFile(name, content) {
if (typeof name == 'undefined') {
throw new Error('The first parameter name is a must')
}
if (typeof content == 'undefined') {
throw new Error('The second parameter content is a must')
}
if (!(content instanceof Blob)) {
content = new Blob([content])
}
const link = URL.createObjectURL(content)
download(link, name)
}
//下载一个链接
function download(link, name) {
if (!name) {//如果没有提供名字,从给的Link中截取最后一坨
name = link.slice(link.lastIndexOf('/') + 1)
}
let eleLink = document.createElement('a')
eleLink.download = name
eleLink.style.display = 'none'
eleLink.href = link
document.body.appendChild(eleLink)
eleLink.click()
document.body.removeChild(eleLink)
}
提供一个图片链接,点击下载
图片、pdf等文件,浏览器会默认执行预览,不能调用download方法进行下载,需要先把图片、pdf等文件转成blob,再调用download方法进行下载,转换的方式是使用axios请求对应的链接
//可以用来下载浏览器会默认预览的文件类型,例如mp4,jpg等
import axios from 'axios'
//提供一个link,完成文件下载,link可以是 http://xxx.com/xxx.xls
function downloadByLink(link,fileName){
axios.request({
url: link,
responseType: 'blob' //关键代码,让axios把响应改成blob
}).then(res => {
const link=URL.createObjectURL(res.data)
download(link, fileName)
})
}
注意:会有同源策略的限制,需要配置转发
下载文件
// api 接口
// params 请求参数
// fileName 文件名
const downloadFile = (api, params, fileName, type = 'get') => {
axios({
method: type,
url: api,
responseType: 'blob',
params: params
}).then((res) => {
let str = res.headers['content-disposition']
if (!res || !str) {
return
}
let suffix = ''
// 截取文件名和文件类型
if (str.lastIndexOf('.')) {
fileName ? '' : fileName = decodeURI(str.substring(str.indexOf('=') + 1, str.lastIndexOf('.')))
suffix = str.substring(str.lastIndexOf('.'), str.length)
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + suffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(link.href);
}
}).catch((err) => {
console.log(err.message);
})
}
性能检测
有时你需要知道你的代码完成其执行需要多少时间。查看下面的代码示例,则可以帮助你完成检测工作。
const startTime = performance.now();
something();
const endTime = performance.now();
console.log("Cpde take ${startTime - endTime} milliseconds")
手机号脱敏
export const hideMobile = (mobile) => {
return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2")
}
开启全屏
export const launchFullscreen = (element) => {
if (element.requestFullscreen) {
element.requestFullscreen()
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen()
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen()
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullScreen()
}
}
关闭全屏
export const exitFullscreen = () => {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.msExitFullscreen) {
document.msExitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen()
}
}
解析URL参数
export const getSearchParams = () => {
const searchPar = new URLSearchParams(window.location.search)
const paramsObj = {}
for (const [key, value] of searchPar.entries()) {
paramsObj[key] = value
}
return paramsObj
}
判断手机是Andoird还是IOS
export const getOSType=() => {
let u = navigator.userAgent, app = navigator.appVersion;
let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;
let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isIOS) {
return 1;
}
if (isAndroid) {
return 2;
}
return 3;
}
数组对象根据字段去重
export const uniqueArrayObject = (arr = [], key = 'id') => {
if (arr.length === 0) return
let list = []
const map = {}
arr.forEach((item) => {
if (!map[item[key]]) {
map[item[key]] = item
}
})
list = Object.values(map)
return list
}
滚动到页面顶部
export const scrollToTop = () => {
const height = document.documentElement.scrollTop || document.body.scrollTop;
if (height > 0) {
window.requestAnimationFrame(scrollToTop);
window.scrollTo(0, height - height / 8);
}
}
滚动到元素位置
export const smoothScroll = element =>{
document.querySelector(element).scrollIntoView({
behavior: 'smooth'
});
};
uuid
export const uuid = () => {
const temp_url = URL.createObjectURL(new Blob())
const uuid = temp_url.toString()
URL.revokeObjectURL(temp_url) //释放这个url
return uuid.substring(uuid.lastIndexOf('/') + 1)
}
金额格式化
// {number} number:要格式化的数字
// {number} decimals:保留几位小数
// {string} dec_point:小数点符号
// {string} thousands_sep:千分位符号
export const moneyFormat = (number, decimals, dec_point, thousands_sep) => {
number = (number + '').replace(/[^0-9+-Ee.]/g, '')
const n = !isFinite(+number) ? 0 : +number
const prec = !isFinite(+decimals) ? 2 : Math.abs(decimals)
const sep = typeof thousands_sep === 'undefined' ? ',' : thousands_sep
const dec = typeof dec_point === 'undefined' ? '.' : dec_point
let s = ''
const toFixedFix = function(n, prec) {
const k = Math.pow(10, prec)
return '' + Math.ceil(n * k) / k
}
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.')
const re = /(-?\d+)(\d{3})/
while (re.test(s[0])) {
s[0] = s[0].replace(re, '$1' + sep + '$2')
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || ''
s[1] += new Array(prec - s[1].length + 1).join('0')
}
return s.join(dec)
}
金额大写转换函数
function transform(tranvalue) {
try {
var i = 1;
var dw2 = new Array("", "万", "亿"); //大单位
var dw1 = new Array("拾", "佰", "仟"); //小单位
var dw = new Array(
"零",
"壹",
"贰",
"叁",
"肆",
"伍",
"陆",
"柒",
"捌",
"玖"
);
//整数部分用
//以下是小写转换成大写显示在合计大写的文本框中
//分离整数与小数
var source = splits(tranvalue);
var num = source[0];
var dig = source[1];
//转换整数部分
var k1 = 0; //计小单位
var k2 = 0; //计大单位
var sum = 0;
var str = "";
var len = source[0].length; //整数的长度
for (i = 1; i <= len; i++) {
var n = source[0].charAt(len - i); //取得某个位数上的数字
var bn = 0;
if (len - i - 1 >= 0) {
bn = source[0].charAt(len - i - 1); //取得某个位数前一位上的数字
}
sum = sum + Number(n);
if (sum != 0) {
str = dw[Number(n)].concat(str); //取得该数字对应的大写数字,并插入到str字符串的前面
if (n == "0") sum = 0;
}
if (len - i - 1 >= 0) {
//在数字范围内
if (k1 != 3) {
//加小单位
if (bn != 0) {
str = dw1[k1].concat(str);
}
k1++;
} else {
//不加小单位,加大单位
k1 = 0;
var temp = str.charAt(0);
if (temp == "万" || temp == "亿")
//若大单位前没有数字则舍去大单位
str = str.substr(1, str.length - 1);
str = dw2[k2].concat(str);
sum = 0;
}
}
if (k1 == 3) {
//小单位到千则大单位进一
k2++;
}
}
//转换小数部分
var strdig = "";
if (dig != "") {
var n = dig.charAt(0);
if (n != 0) {
strdig += dw[Number(n)] + "角"; //加数字
}
var n = dig.charAt(1);
if (n != 0) {
strdig += dw[Number(n)] + "分"; //加数字
}
}
str += "元" + strdig;
} catch (e) {
return "0元";
}
return str;
}
//拆分整数与小数
function splits(tranvalue) {
var value = new Array("", "");
temp = tranvalue.split(".");
for (var i = 0; i < temp.length; i++) {
value = temp;
}
return value;
}
模糊搜索
// list 原数组
// keyWord 查询的关键词
// attribute 数组需要检索属性
export const fuzzyQuery = (list, keyWord, attribute = 'name') => {
const reg = new RegExp(keyWord)
const arr = []
for (let i = 0; i < list.length; i++) {
if (reg.test(list[i][attribute])) {
arr.push(list[i])
}
}
return arr
}
遍历树节点
export const foreachTree = (data, callback, childrenName = 'children') => {
for (let i = 0; i < data.length; i++) {
callback(data[i])
if (data[i][childrenName] && data[i][childrenName].length > 0) {
foreachTree(data[i][childrenName], callback, childrenName)
}
}
}
检查当前 Tab 页是否在前台
const isBrowserTabInView = () => document.hidden;
isBrowserTabInView();
从日期中获取时间
const timeFromDate = date => date.toTimeString().slice(0, 8);
console.log(timeFromDate(new Date(2021, 0, 10, 17, 30, 0)));
// Result: "17:30:00"
console.log(timeFromDate(new Date()));
// Result: will log the current time
保留小数点(非四舍五入)
使用 Math.pow() 方法,我们可以将一个数字截断到某个小数点。
const toFixed = (n, fixed) => ~~(Math.pow(10, fixed) * n) / Math.pow(10, fixed);
// Examples
toFixed(25.198726354, 1); // 25.1
toFixed(25.198726354, 2); // 25.19
toFixed(25.198726354, 3); // 25.198
toFixed(25.198726354, 4); // 25.1987
toFixed(25.198726354, 5); // 25.19872
toFixed(25.198726354, 6); // 25.198726
检查元素当前是否为聚焦状态
const elementIsInFocus = (el) => (el === document.activeElement);
elementIsInFocus(anyElement)
// Result: will return true if in focus, false if not in focus
检查浏览器是否支持触摸事件
const touchSupported = () => {
('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch);
}
console.log(touchSupported());
// Result: will return true if touch events are supported, false if not
检查当前用户是否为苹果设备
const isAppleDevice = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
console.log(isAppleDevice);
// Result: will return true if user is on an Apple device
获取所有参数平均值
const average = (...args) => args.reduce((a, b) => a + b) / args.length;
average(1, 2, 3, 4);
// Result: 2.5
转换华氏度/摄氏度。(这个应该很少在国内用到吧)
const celsiusToFahrenheit = (celsius) => celsius * 9/5 + 32;
const fahrenheitToCelsius = (fahrenheit) => (fahrenheit - 32) * 5/9;
// Examples
celsiusToFahrenheit(15); // 59
celsiusToFahrenheit(0); // 32
celsiusToFahrenheit(-20); // -4
fahrenheitToCelsius(59); // 15
fahrenheitToCelsius(32); // 0
为元素添加on方法
Element.prototype.on = Element.prototype.addEventListener;
NodeList.prototype.on = function (event, fn) {、
[]['forEach'].call(this, function (el) {
el.on(event, fn);
});
return this;
};
为元素添加trigger方法
Element.prototype.trigger = function(type, data) {
var event = document.createEvent("htmlEvents");
event.initEvent(type, true, true);
event.data = data || {};
event.eventName = type;
event.target = this;
this.dispatchEvent(event);
return this;
};
NodeList.prototype.trigger = function(event) {
[]["forEach"].call(this, function(el) {
el["trigger"](event);
});
return this;
};
加入收藏夹
function addFavorite(sURL, sTitle) {
try {
window.external.addFavorite(sURL, sTitle);
} catch (e) {
try {
window.sidebar.addPanel(sTitle, sURL, "");
} catch (e) {
alert("加入收藏失败,请使用Ctrl+D进行添加");
}
}
}
提取页面代码中所有网址
var aa = document.documentElement.outerHTML
.match(
/(url\(|src=|href=)[\"\']*([^\"\'\(\)\<\>\[\] ]+)[\"\'\)]*|(http:\/\/[\w\-\.]+[^\"\'\(\)\<\>\[\] ]+)/gi
)
.join("\r\n")
.replace(/^(src=|href=|url\()[\"\']*|[\"\'\>\) ]*$/gim, "");
alert(aa);
动态加载脚本文件
function appendscript(src, text, reload, charset) {
var id = hash(src + text);
if (!reload && in_array(id, evalscripts)) return;
if (reload && $(id)) {
$(id).parentNode.removeChild($(id));
}
evalscripts.push(id);
var scriptNode = document.createElement("script");
scriptNode.type = "text/JavaScript";
scriptNode.id = id;
scriptNode.charset = charset
? charset
: BROWSER.firefox
? document.characterSet
: document.charset;
try {
if (src) {
scriptNode.src = src;
scriptNode.onloadDone = false;
scriptNode.onload = function() {
scriptNode.onloadDone = true;
jsLOADED[src] = 1;
};
scriptNode.onreadystatechange = function() {
if (
(scriptNode.readyState == "loaded" ||
scriptNode.readyState == "complete") &&
!scriptNode.onloadDone
) {
scriptNode.onloadDone = true;
jsLOADED[src] = 1;
}
};
} else if (text) {
scriptNode.text = text;
}
document.getElementsByTagName("head")[0].appendChild(scriptNode);
} catch (e) {}
}
确认是否是键盘有效输入值
function checkKey(iKey) {
if (iKey == 32 || iKey == 229) {
return true;
} /*空格和异常*/
if (iKey > 47 && iKey < 58) {
return true;
} /*数字*/
if (iKey > 64 && iKey < 91) {
return true;
} /*字母*/
if (iKey > 95 && iKey < 108) {
return true;
} /*数字键盘1*/
if (iKey > 108 && iKey < 112) {
return true;
} /*数字键盘2*/
if (iKey > 185 && iKey < 193) {
return true;
} /*符号1*/
if (iKey > 218 && iKey < 223) {
return true;
} /*符号2*/
return false;
}
全角半角转换
//iCase: 0全到半,1半到全,其他不转化
function chgCase(sStr, iCase) {
if (
typeof sStr != "string" ||
sStr.length <= 0 ||
!(iCase === 0 || iCase == 1)
) {
return sStr;
}
var i,
oRs = [],
iCode;
if (iCase) {
/*半->全*/
for (i = 0; i < sStr.length; i += 1) {
iCode = sStr.charCodeAt(i);
if (iCode == 32) {
iCode = 12288;
} else if (iCode < 127) {
iCode += 65248;
}
oRs.push(String.fromCharCode(iCode));
}
} else {
/*全->半*/
for (i = 0; i < sStr.length; i += 1) {
iCode = sStr.charCodeAt(i);
if (iCode == 12288) {
iCode = 32;
} else if (iCode > 65280 && iCode < 65375) {
iCode -= 65248;
}
oRs.push(String.fromCharCode(iCode));
}
}
return oRs.join("");
}
全角转换为半角函数
function toCDB(str) {
var result = "";
for (var i = 0; i < str.length; i++) {
code = str.charCodeAt(i);
if (code >= 65281 && code <= 65374) {
result += String.fromCharCode(str.charCodeAt(i) - 65248);
} else if (code == 12288) {
result += String.fromCharCode(str.charCodeAt(i) - 12288 + 32);
} else {
result += str.charAt(i);
}
}
return result;
}
半角转换为全角函数
function toDBC(str) {
var result = "";
for (var i = 0; i < str.length; i++) {
code = str.charCodeAt(i);
if (code >= 33 && code <= 126) {
result += String.fromCharCode(str.charCodeAt(i) + 65248);
} else if (code == 32) {
result += String.fromCharCode(str.charCodeAt(i) + 12288 - 32);
} else {
result += str.charAt(i);
}
}
return result;
}
版本对比
function compareVersion(v1, v2) {
v1 = v1.split(".");
v2 = v2.split(".");
var len = Math.max(v1.length, v2.length);
while (v1.length < len) {
v1.push("0");
}
while (v2.length < len) {
v2.push("0");
}
for (var i = 0; i < len; i++) {
var num1 = parseInt(v1[i]);
var num2 = parseInt(v2[i]);
if (num1 > num2) {
return 1;
} else if (num1 < num2) {
return -1;
}
}
return 0;
}
压缩css样式代码
function compresscss(s) {
//压缩代码
s = s.replace(/\/\*(.|\n)*?\*\//g, ""); //删除注释
s = s.replace(/\s*([\{\}\:\;\,])\s*/g, "$1");
s = s.replace(/\,[\s\.\#\d]*\{/g, "{"); //容错处理
s = s.replace(/;\s*;/g, ";"); //清除连续分号
s = s.match(/^\s*(\S+(\s+\S+)*)\s*$/); //去掉首尾空白
return s == null ? "" : s[1];
}
字符串长度截取
function cutstr(str, len) {
var temp,
icount = 0,
patrn = /[^\x00-\xff]/,
strre = "";
for (var i = 0; i < str.length; i++) {
if (icount < len - 1) {
temp = str.substr(i, 1);
if (patrn.exec(temp) == null) {
icount = icount + 1
} else {
icount = icount + 2
}
strre += temp
} else {
break;
}
}
return strre + "..."
}
时间日期格式转换
Date.prototype.format = function(formatStr) {
var str = formatStr;
var Week = ["日", "一", "二", "三", "四", "五", "六"];
str = str.replace(/yyyy|YYYY/, this.getFullYear());
str = str.replace(
/yy|YY/,
this.getYear() % 100 > 9
? (this.getYear() % 100).toString()
: "0" + (this.getYear() % 100)
);
str = str.replace(
/MM/,
this.getMonth() + 1 > 9
? (this.getMonth() + 1).toString()
: "0" + (this.getMonth() + 1)
);
str = str.replace(/M/g, this.getMonth() + 1);
str = str.replace(/w|W/g, Week[this.getDay()]);
str = str.replace(
/dd|DD/,
this.getDate() > 9 ? this.getDate().toString() : "0" + this.getDate()
);
str = str.replace(/d|D/g, this.getDate());
str = str.replace(
/hh|HH/,
this.getHours() > 9 ? this.getHours().toString() : "0" + this.getHours()
);
str = str.replace(/h|H/g, this.getHours());
str = str.replace(
/mm/,
this.getMinutes() > 9
? this.getMinutes().toString()
: "0" + this.getMinutes()
);
str = str.replace(/m/g, this.getMinutes());
str = str.replace(
/ss|SS/,
this.getSeconds() > 9
? this.getSeconds().toString()
: "0" + this.getSeconds()
);
str = str.replace(/s|S/g, this.getSeconds());
return str;
};
// 或
Date.prototype.format = function(format) {
var o = {
"M+": this.getMonth() + 1, //month
"d+": this.getDate(), //day
"h+": this.getHours(), //hour
"m+": this.getMinutes(), //minute
"s+": this.getSeconds(), //second
"q+": Math.floor((this.getMonth() + 3) / 3), //quarter
S: this.getMilliseconds() //millisecond
};
if (/(y+)/.test(format))
format = format.replace(
RegExp.$1,
(this.getFullYear() + "").substr(4 - RegExp.$1.length)
);
for (var k in o) {
if (new RegExp("(" + k + ")").test(format))
format = format.replace(
RegExp.$1,
RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
);
}
return format;
};
alert(new Date().format("yyyy-MM-dd hh:mm:ss"));
判断是否以某个字符串结束
String.prototype.endWith = function(s) {
var d = this.length - s.length;
return d >= 0 && this.lastIndexOf(s) == d;
};
返回脚本内容
function evalscript(s) {
if (s.indexOf("<script") == -1) return s;
var p = /<script[^\>]*?>([^\x00]*?)<\/script>/gi;
var arr = [];
while ((arr = p.exec(s))) {
var p1 = /<script[^\>]*?src=\"([^\>]*?)\"[^\>]*?(reload=\"1\")?(?:charset=\"([\w\-]+?)\")?><\/script>/i;
var arr1 = [];
arr1 = p1.exec(arr[0]);
if (arr1) {
appendscript(arr1[1], "", arr1[2], arr1[3]);
} else {
p1 = /<script(.*?)>([^\x00]+?)<\/script>/i;
arr1 = p1.exec(arr[0]);
appendscript("", arr1[2], arr1[1].indexOf("reload=") != -1);
}
}
return s;
}
格式化CSS样式代码
function formatCss(s) {
//格式化代码
s = s.replace(/\s*([\{\}\:\;\,])\s*/g, "$1");
s = s.replace(/;\s*;/g, ";"); //清除连续分号
s = s.replace(/\,[\s\.\#\d]*{/g, "{");
s = s.replace(/([^\s])\{([^\s])/g, "$1 {\n\t$2");
s = s.replace(/([^\s])\}([^\n]*)/g, "$1\n}\n$2");
s = s.replace(/([^\s]);([^\s\}])/g, "$1;\n\t$2");
return s;
}
获取cookie值
function getCookie(name) {
var arr = document.cookie.match(new RegExp("(^| )" + name + "=([^;]*)(;|$)"));
if (arr != null) return unescape(arr[2]);
return null;
}
获得URL中GET参数值
// 用法:如果地址是 test.htm?t1=1&t2=2&t3=3, 那么能取得:GET["t1"], GET["t2"], GET["t3"]
function getGet() {
querystr = window.location.href.split("?");
if (querystr[1]) {
GETs = querystr[1].split("&");
GET = [];
for (i = 0; i < GETs.length; i++) {
tmp_arr = GETs.split("=");
key = tmp_arr[0];
GET[key] = tmp_arr[1];
}
}
return querystr[1];
}
获取URL上的参数
// 获取URL中的某参数值,不区分大小写
// 获取URL中的某参数值,不区分大小写,
// 默认是取'hash'里的参数,
// 如果传其他参数支持取‘search’中的参数
// @param {String} name 参数名称
export function getUrlParam(name, type = "hash") {
let newName = name,
reg = new RegExp("(^|&)" + newName + "=([^&]*)(&|$)", "i"),
paramHash = window.location.hash.split("?")[1] || "",
paramSearch = window.location.search.split("?")[1] || "",
param;
type === "hash" ? (param = paramHash) : (param = paramSearch);
let result = param.match(reg);
if (result != null) {
return result[2].split("/")[0];
}
return null;
}
获取移动设备初始化大小
function getInitZoom() {
if (!this._initZoom) {
var screenWidth = Math.min(screen.height, screen.width);
if (this.isAndroidMobileDevice() && !this.isNewChromeOnAndroid()) {
screenWidth = screenWidth / window.devicePixelRatio;
}
this._initZoom = screenWidth / document.body.offsetWidth;
}
return this._initZoom;
}
检验URL链接是否有效
function getUrlState(URL) {
var xmlhttp = new ActiveXObject("microsoft.xmlhttp");
xmlhttp.Open("GET", URL, false);
try {
xmlhttp.Send();
} catch (e) {
} finally {
var result = xmlhttp.responseText;
if (result) {
if (xmlhttp.Status == 200) {
return true;
} else {
return false;
}
} else {
return false;
}
}
}
判断是否安卓移动设备访问
function isAndroidMobileDevice() {
return /android/i.test(navigator.userAgent.toLowerCase());
}
判断是否苹果移动设备访问
function isAppleMobileDevice() {
return /iphone|ipod|ipad|Macintosh/i.test(navigator.userAgent.toLowerCase());
}
判断是否移动设备
function isMobile() {
if (typeof this._isMobile === "boolean") {
return this._isMobile;
}
var screenWidth = this.getScreenWidth();
var fixViewPortsExperiment =
rendererModel.runningExperiments.FixViewport ||
rendererModel.runningExperiments.fixviewport;
var fixViewPortsExperimentRunning =
fixViewPortsExperiment && fixViewPortsExperiment.toLowerCase() === "new";
if (!fixViewPortsExperiment) {
if (!this.isAppleMobileDevice()) {
screenWidth = screenWidth / window.devicePixelRatio;
}
}
var isMobileScreenSize = screenWidth < 600;
var isMobileUserAgent = false;
this._isMobile = isMobileScreenSize && this.isTouchScreen();
return this._isMobile;
}
判断是否是移动设备访问
function isMobileUserAgent() {
return /iphone|ipod|android.*mobile|windows.*phone|blackberry.*mobile/i.test(
window.navigator.userAgent.toLowerCase()
);
}
判断鼠标是否移出事件
function isMouseout(e, handler) {
if (e.type !== "mouseout") {
return false;
}
var reltg = e.relatedTarget
? e.relatedTarget
: e.type === "mouseout"
? e.toElement
: e.fromElement;
while (reltg && reltg !== handler) {
reltg = reltg.parentNode;
}
return reltg !== handler;
}
判断是否Touch屏幕
function isTouchScreen() {
return (
"ontouchstart" in window ||
(window.DocumentTouch && document instanceof DocumentTouch)
);
}
判断是否为网址
function isURL(strUrl) {
var regular = /^\b(((https?|ftp):\/\/)?[-a-z0-9]+(\.[-a-z0-9]+)*\.(?:com|edu|gov|int|mil|net|org|biz|info|name|museum|asia|coop|aero|[a-z][a-z]|((25[0-5])|(2[0-4]\d)|(1\d\d)|([1-9]\d)|\d))\b(\/[-a-z0-9_:\@&?=+,.!\/~%\$]*)?)$/i;
if (regular.test(strUrl)) {
return true;
} else {
return false;
}
}
判断是否打开视窗
function isViewportOpen() {
return !!document.getElementById("wixMobileViewport");
}
加载样式文件
function loadStyle(url) {
try {
document.createStyleSheet(url);
} catch (e) {
var cssLink = document.createElement("link");
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
cssLink.href = url;
var head = document.getElementsByTagName("head")[0];
head.appendChild(cssLink);
}
}
替换地址栏
function locationReplace(url) {
if (history.replaceState) {
history.replaceState(null, document.title, url);
history.go(0);
} else {
location.replace(url);
}
}
解决offsetX兼容性问题
// 针对火狐不支持offsetX/Y
function getOffset(e) {
var target = e.target, // 当前触发的目标对象
eventCoord,
pageCoord,
offsetCoord;
// 计算当前触发元素到文档的距离
pageCoord = getPageCoord(target);
// 计算光标到文档的距离
eventCoord = {
X: window.pageXOffset + e.clientX,
Y: window.pageYOffset + e.clientY
};
// 相减获取光标到第一个定位的父元素的坐标
offsetCoord = {
X: eventCoord.X - pageCoord.X,
Y: eventCoord.Y - pageCoord.Y
};
return offsetCoord;
}
function getPageCoord(element) {
var coord = { X: 0, Y: 0 };
// 计算从当前触发元素到根节点为止,
// 各级 offsetParent 元素的 offsetLeft 或 offsetTop 值之和
while (element) {
coord.X += element.offsetLeft;
coord.Y += element.offsetTop;
element = element.offsetParent;
}
return coord;
}
打开一个窗体通用方法
function openWindow(url, windowName, width, height) {
var x = parseInt(screen.width / 2.0) - width / 2.0;
var y = parseInt(screen.height / 2.0) - height / 2.0;
var isMSIE = navigator.appName == "Microsoft Internet Explorer";
if (isMSIE) {
var p = "resizable=1,location=no,scrollbars=no,width=";
p = p + width;
p = p + ",height=";
p = p + height;
p = p + ",left=";
p = p + x;
p = p + ",top=";
p = p + y;
retval = window.open(url, windowName, p);
} else {
var win = window.open(
url,
"ZyiisPopup",
"top=" +
y +
",left=" +
x +
",scrollbars=" +
scrollbars +
",dialog=yes,modal=yes,width=" +
width +
",height=" +
height +
",resizable=no"
);
eval("try { win.resizeTo(width, height); } catch(e) { }");
win.focus();
}
}
将键值对拼接成URL带参数
export default const fnParams2Url = obj=> {
let aUrl = []
let fnAdd = function(key, value) {
return key + '=' + value
}
for (var k in obj) {
aUrl.push(fnAdd(k, obj[k]))
}
return encodeURIComponent(aUrl.join('&'))
}
去掉url前缀
function removeUrlPrefix(a) {
a = a
.replace(/:/g, ":")
.replace(/./g, ".")
.replace(///g, "/");
while (
trim(a)
.toLowerCase()
.indexOf("http://") == 0
) {
a = trim(a.replace(/http:\/\//i, ""));
}
return a;
}
替换全部
String.prototype.replaceAll = function(s1, s2) {
return this.replace(new RegExp(s1, "gm"), s2);
};
resize的操作
(function() {
var fn = function() {
var w = document.documentElement
? document.documentElement.clientWidth
: document.body.clientWidth,
r = 1255,
b = Element.extend(document.body),
classname = b.className;
if (w < r) {
//当窗体的宽度小于1255的时候执行相应的操作
} else {
//当窗体的宽度大于1255的时候执行相应的操作
}
};
if (window.addEventListener) {
window.addEventListener("resize", function() {
fn();
});
} else if (window.attachEvent) {
window.attachEvent("onresize", function() {
fn();
});
}
fn();
})();
设置cookie值
function setCookie(name, value, Hours) {
var d = new Date();
var offset = 8;
var utc = d.getTime() + d.getTimezoneOffset() * 60000;
var nd = utc + 3600000 * offset;
var exp = new Date(nd);
exp.setTime(exp.getTime() + Hours * 60 * 60 * 1000);
document.cookie =
name +
"=" +
escape(value) +
";path=/;expires=" +
exp.toGMTString() +
";domain=360doc.com;";
}
设为首页
function setHomepage() {
if (document.all) {
document.body.style.behavior = "url(#default#homepage)";
document.body.setHomePage("http://w3cboy.com");
} else if (window.sidebar) {
if (window.netscape) {
try {
netscape.security.PrivilegeManager.enablePrivilege(
"UniversalXPConnect"
);
} catch (e) {
alert(
"该操作被浏览器拒绝,如果想启用该功能,请在地址栏内输入 about:config,然后将项 signed.applets.codebase_principal_support 值该为true"
);
}
}
var prefs = Components.classes[
"@mozilla.org/preferences-service;1"
].getService(Components.interfaces.nsIPrefBranch);
prefs.setCharPref("browser.startup.homepage", "http://w3cboy.com");
}
}
按字母排序,对每行进行数组排序
function setSort() {
var text = K1.value
.split(/[\r\n]/)
.sort()
.join("\r\n"); //顺序
var test = K1.value
.split(/[\r\n]/)
.sort()
.reverse()
.join("\r\n"); //反序
K1.value = K1.value != text ? text : test;
}
清除脚本内容
function stripscript(s) {
return s.replace(/<script.*?>.*?<\/script>/gi, "");
}
时间个性化输出功能
/*
1、< 60s, 显示为“刚刚”
2、>= 1min && < 60 min, 显示与当前时间差“XX分钟前”
3、>= 60min && < 1day, 显示与当前时间差“今天 XX:XX”
4、>= 1day && < 1year, 显示日期“XX月XX日 XX:XX”
5、>= 1year, 显示具体日期“XXXX年XX月XX日 XX:XX”
*/
function timeFormat(time) {
var date = new Date(time),
curDate = new Date(),
year = date.getFullYear(),
month = date.getMonth() + 10,
day = date.getDate(),
hour = date.getHours(),
minute = date.getMinutes(),
curYear = curDate.getFullYear(),
curHour = curDate.getHours(),
timeStr;
if (year < curYear) {
timeStr = year + "年" + month + "月" + day + "日 " + hour + ":" + minute;
} else {
var pastTime = curDate - date,
pastH = pastTime / 3600000;
if (pastH > curHour) {
timeStr = month + "月" + day + "日 " + hour + ":" + minute;
} else if (pastH >= 1) {
timeStr = "今天 " + hour + ":" + minute + "分";
} else {
var pastM = curDate.getMinutes() - minute;
if (pastM > 1) {
timeStr = pastM + "分钟前";
} else {
timeStr = "刚刚";
}
}
}
return timeStr;
}
清除空格
String.prototype.trim = function() {
var reExtraSpace = /^\s*(.*?)\s+$/;
return this.replace(reExtraSpace, "$1");
};
// 清除左空格
function ltrim(s) {
return s.replace(/^(\s*| *)/, "");
}
// 清除右空格
function rtrim(s) {
return s.replace(/(\s*| *)$/, "");
}
随机数时间戳
function uniqueId() {
var a = Math.random,
b = parseInt;
return (
Number(new Date()).toString() + b(10 * a()) + b(10 * a()) + b(10 * a())
);
}
实现utf8解码
function utf8_decode(str_data) {
var tmp_arr = [],
i = 0,
ac = 0,
c1 = 0,
c2 = 0,
c3 = 0;
str_data += "";
while (i < str_data.length) {
c1 = str_data.charCodeAt(i);
if (c1 < 128) {
tmp_arr[ac++] = String.fromCharCode(c1);
i++;
} else if (c1 > 191 && c1 < 224) {
c2 = str_data.charCodeAt(i + 1);
tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = str_data.charCodeAt(i + 1);
c3 = str_data.charCodeAt(i + 2);
tmp_arr[ac++] = String.fromCharCode(
((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)
);
i += 3;
}
}
return tmp_arr.join("");
}
校验是否包含中文字符(包括中文标点符号)
function haveCNChars(str){
return /[\u4e00-\u9fa5]/.test(str);
}
清除所有中文字符
function clearCNCharsAndSpaces(str){
return str.replace(/[\u4e00-\u9fa5 ]/g,'');
}
生成一个带有随机数的列表
Array.from({ length: 1000 }, Math.random)
// [ 0.6163093133259432, 0.8877401276499153, 0.4094354756035987, ...] - 1000 items
生成一个带有数字的列表
Array.from({ length: 1000 }, (v, i) => i)
// [0, 1, 2, 3, 4, 5, 6....999]
RGB→转换为十六进制
const rgb2hex = ([r, g, b]) =>
`#${(1 << 24) + (r << 16) + (g << 8) + b}`.toString(16).substr(1);
rgb2hex([76, 11, 181]);
// #4c0bb5
转换十六进制→RGB
怎么把它转换回去?这是实现该目标的一种好方法。
const hex2rgb = hex =>
[1, 3, 5].map((h) => parseInt(hex.substring(h, h + 2), 16));
hex2rgb("#4c0bb5");
// [76, 11, 181]
奇数或偶数
使用 位 运算的方式:
const value = 232;
if (value & 1) console.log("odd");
else console.log("even");
// even
距离过去到现在时间表示
有时我们需要打印6分钟前的日期,但不希望很大的库来完成。这里有一个小片段可以做到这一点:
const fromAgo = (date) => {
const ms = Date.now() - date.getTime();
const seconds = Math.round(ms / 1000);
const minutes = Math.round(ms / 60000);
const hours = Math.round(ms / 3600000);
const days = Math.round(ms / 86400000);
const months = Math.round(ms / 2592000000);
const years = Math.round(ms / 31104000000);
switch (true) {
case seconds < 60:
return `${seconds} second(s) ago"`;
case minutes < 60:
return `${minutes} minute(s) ago"`;
case hours < 24:
return `${hours} hour(s) ago"`;
case days < 30:
return `${days} day(s) ago`;
case months < 12:
return `${months} month(s) ago`;
default:
return `${years} year(s) ago`;
}
};
const createdAt = new Date(2021, 0, 5);
fromAgo(createdAt); // 14 day(s) ago;
异步并发数限制
/**
* 关键点
* 1. new promise 一经创建,立即执行
* 2. 使用 Promise.resolve().then 可以把任务加到微任务队列,防止立即执行迭代方法
* 3. 微任务处理过程中,产生的新的微任务,会在同一事件循环内,追加到微任务队列里
* 4. 使用 race 在某个任务完成时,继续添加任务,保持任务按照最大并发数进行执行
* 5. 任务完成后,需要从 doingTasks 中移出
*/
function limit(count, array, iterateFunc) {
const tasks = []
const doingTasks = []
let i = 0
const enqueue = () => {
if (i === array.length) {
return Promise.resolve()
}
const task = Promise.resolve().then(() => iterateFunc(array[i++]))
tasks.push(task)
const doing = task.then(() => doingTasks.splice(doingTasks.indexOf(doing), 1))
doingTasks.push(doing)
const res = doingTasks.length >= count ? Promise.race(doingTasks) : Promise.resolve()
return res.then(enqueue)
};
return enqueue().then(() => Promise.all(tasks))
}
// test
const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i))
limit(2, [1000, 1000, 1000, 1000], timeout).then((res) => {
console.log(res)
})
异步串行 | 异步并行
// 字节面试题,实现一个异步加法
function asyncAdd(a, b, callback) {
setTimeout(function () {
callback(null, a + b);
}, 500);
}
// 解决方案
// 1. promisify
const promiseAdd = (a, b) => new Promise((resolve, reject) => {
asyncAdd(a, b, (err, res) => {
if (err) {
reject(err)
} else {
resolve(res)
}
})
})
// 2. 串行处理
async function serialSum(...args) {
return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))
}
// 3. 并行处理
async function parallelSum(...args) {
if (args.length === 1) return args[0]
const tasks = []
for (let i = 0; i < args.length; i += 2) {
tasks.push(promiseAdd(args[i], args[i + 1] || 0))
}
const results = await Promise.all(tasks)
return parallelSum(...results)
}
// 测试
(async () => {
console.log('Running...');
const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)
console.log(res1)
const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)
console.log(res2)
console.log('Done');
})()
vue reactive
// Dep module
class Dep {
static stack = []
static target = null
deps = null
constructor() {
this.deps = new Set()
}
depend() {
if (Dep.target) {
this.deps.add(Dep.target)
}
}
notify() {
this.deps.forEach(w => w.update())
}
static pushTarget(t) {
if (this.target) {
this.stack.push(this.target)
}
this.target = t
}
static popTarget() {
this.target = this.stack.pop()
}
}
// reactive
function reactive(o) {
if (o && typeof o === 'object') {
Object.keys(o).forEach(k => {
defineReactive(o, k, o[k])
})
}
return o
}
function defineReactive(obj, k, val) {
let dep = new Dep()
Object.defineProperty(obj, k, {
get() {
dep.depend()
return val
},
set(newVal) {
val = newVal
dep.notify()
}
})
if (val && typeof val === 'object') {
reactive(val)
}
}
// watcher
class Watcher {
constructor(effect) {
this.effect = effect
this.update()
}
update() {
Dep.pushTarget(this)
this.value = this.effect()
Dep.popTarget()
return this.value
}
}
// 测试代码
const data = reactive({
msg: 'aaa'
})
new Watcher(() => {
console.log('===> effect', data.msg);
})
setTimeout(() => {
data.msg = 'hello'
}, 1000)
uniapp 再次返回退出应用
let main = plus.android.runtimeMainActivity();
//为了防止快速点按返回键导致程序退出,所以重写quit方法改为隐藏至后台
plus.runtime.quit = function() {
main.moveTaskToBack(false);
};
//重写toast方法如果内容为 ‘再次返回退出应用’ 就隐藏应用,其他正常toast
plus.nativeUI.toast = (function(str) {
if (str =='再次返回退出应用') {
plus.runtime.quit();
} else {
uni.showToast({
title: '再次返回退出应用',
icon: 'none'
})
}
});
函数重载
- 定义
function addMethod(object, name, fn) {
const old = object[name]
object[name] = function (...args) {
if (args.length === fn.length) {
return fn.apply(this, args)
} else if (typeof old === 'function') {
return old.apply(this, args)
}
}
}
module.exports = addMethod
- 使用
const addMethod = require('./addMethod')
const search = {}
addMethod(search, 'find', () => {
console.log('查询所有用户')
})
addMethod(search, 'find', (name) => {
console.log('按照姓名查询用户')
})
addMethod(search, 'find', (firstName, lastName) => {
console.log('按照姓和名查询用户')
})
search.find('a', 'asdfa')
使用代理实现单例
export function singleton(className) {
let ins
return new Proxy(className, {
construct(target, args) {
if (!ins) {
ins = new target(...args)
}
return ins
}
})
}
ajax进度封装
export function request(options = {}) {
const { url, method = 'get', data = null, onProgress } = options
// TODO XHR
// return new Promise(resolve => {
// const xhr = new XMLHttpRequest()
// xhr.addEventListener('readystatechange', () => {
// if (xhr.readyState === xhr.DONE) resolve(xhr.responseText)
// })
// xhr.addEventListener("progress", e => {
// console.log(e.loaded, e.total)
// onProgress && onProgress({
// loaded: e.loaded,
// total: e.total
// })
// })
// xhr.upload.addEventListener('progress', e => {
// console.log(e.loaded, e.total)
// })
// xhr.open(method, url)
// xhr.send(data)
// })
// TODO FETCH
return new Promise(async resolve => {
const resp = await fetch(url, {
method,
body: data
})
const total = +resp.headers.get('content-length')
const decoder = new TextDecoder()
let body = ''
const reader = resp.body.getReader()
let loaded = 0
while(true) {
const { done, value } = await reader.read()
if (done) break
loaded += value.length
body += decoder.decode(value)
console.log(loaded, total)
onProgress && onProgress({
loaded,
total
})
}
resolve(body)
})
}
给fetch实现超时功能
function createFetch(timeout) {
return (resouce, options) => {
const controller = new AbortController()
options = options || {}
options.signal = controller.signal
setTimeout(() => {
controller.abort()
}, timeout)
return fetch(resouce, options)
}
}
// 使用
createFetch(100)('http://localhost:8080/api')
性能优化
// 不优化
for (let i = 0; i <= 100; i++) {
const el = document.createElement('div')
el.innerHTML = i
document.body.appendChild(el)
}
// 优化
const fragment = document.createDocumentFragment()
for (let i = 0; i <= 100; i++) {
const el = document.createElement('div')
el.innerHTML = i
fragment.appendChild(el)
}
document.body.appendChild(fragment)
手写memoize函数
function memoize(func) {
const memoized = function (...args) {
// 缓存是否存在
const key = JSON.stringify(args)
if (memoized.cache.has(key)) return memoized.cache.get(key)
const result = func.apply(this, args)
memoized.cache.set(key, result)
return result
}
memoized.cache = new Map()
return memoized
}
function getArea(r) {
console.log('执行了getArea');
return Math.PI * r * r
}
const getAreaUseMemoize = memoize(getArea)
const r1 = getAreaUseMemoize(15)
const r2 = getAreaUseMemoize(15)
const r3 = getAreaUseMemoize(15)
// 输出
// 执行了getArea
// 706.8583470577034
// 706.8583470577034
// 706.8583470577034
深拷贝
function deepClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port1.postMessage(obj)
port2.onmessage = (msg) => {
resolve(msg.data)
}
})
}
可组合的散列数值,权限判断
// TODO 定义权限
// 可新增
const CREATE = 0b0001
// 可删除
const DELETE = 0b0010
// 可修改
const UPDATE = 0b0100
// 可浏览详情
const DETAIL = 0b1000
// 赋值权限
const result = CREATE | DELETE // 新增删除权限
// 判断是否有修改权限
(result & UPDATE) === UPDATE
// 删除 删除权限
result ^ DELETE
图片转base64
base64: 26个小写字母 + 26个大写字母 + 10个数字 + 1个'+' + 1个'/'
<input type="file" />
<img src="" alt="" id="preview">
const inp = document.querySelector('input')
const img = document.querySelector('#preview')
inp.onchange = function() {
const reader = new FileReader()
reader.readAsDataURL(this.files[0])
reader.onload = function (e) {
img.src = e.target.result
}
}
不使用eval动态生成js
<script src="data:application/javascript,alert(1)"></script>
判断一个值是否是 Promise like
function isPromiseLike(value) {
return (
value !== null
(typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function'
)
}
立即执行函数(IIFE)提升性能
var addEvent = (function () {
if (ele.addEventListener) {
return function (ele, eventName, handler) {
ele.addEventListener(eventName, handler)
}
} else if (ele.attachEvent) {
return function (ele, eventName, handler) {
ele.attachEvent('on' + eventName, handler)
}
} else {
return function (ele, eventName, handler) {
ele['on' + eventName] = handler
}
}
})()
// 发出http请求,适配于浏览器和node环境
const request = (() => {
if (typeof window !== 'undefined') {
return (options) => {
// 浏览器ajax
}
} else {
return (options) => {
// node的http
}
}
})()
const createRemoveSpace = () => {
const reg = /\s/g,
replacement = ''
return str => str.replace(reg, replacement)
}
const createSpace = createRemoveSpace()
createSpace('dsdfaf dd dfdf')
createSpace('dsdfaf dd dfdf')
createSpace('dsdfaf dd dfdf')
createSpace('dsdfaf dd dfdf')
createSpace = null
消除异步的传染性
async function getUser() {
return await fetch('https://my-json-server.typicode.com/typicode/demo/profile').then(resp => resp.json())
}
async function m1() {
// other works
return await getUser()
}
async function m2() {
// other works
return await m1()
}
async function m3() {
// other works
return await m2()
}
function getUser() {
return fetch('https://my-json-server.typicode.com/typicode/demo/profile').then(resp => resp.json())
}
function m1() {
// other works
return getUser()
}
function m2() {
// other works
return m1()
}
function m3() {
// other works
return await m2()
}
function main() {
const user = m3()
console.log(user)
}
function run(func) {
let cache = []
let i = 0
const _originalFetch = window.fetch
window.fetch = (...args) => {
if (cache[i]) {
if (cache[i].status === 'fulfilled') {
return cache[i].data
} else if (cache[i].status === 'rejected') {
throw cache[i].err
}
}
const result = {
status: 'pending',
data: null,
err: null
}
cache[i++] = result
// 发送请求
const prom = _originalFetch(...args).then(resp => resp.json()).then(resp => {
result.status = 'fulfilled'
result.data = resp
}, err => {
result.status = 'rejected'
reesult.err = err
})
// 报错
throw prom
}
try {
func()
} catch (err) {
// 什么时候执行func
if (err instanceof Promise) {
const reRun = () => {
i = 0
func()
}
err.then(reRun, reRun)
}
}
}
并发任务控制
class SuperTask() {
constructor(parallelCount = 2) {
this.parallelCount = parallelCount // 并发数量
this.runningCount = 0 // 正在运行的任务数
this.tasks = []
}
add(task) {
return new Promise((resolve, reject) => {
this.tasks.push({
task,
resolve,
reject
})
this._run()
})
}
// 一次运行tasks队列的所有任务
_run() {
while(this.runningCout < this.parallelCout && this.tasks.length) {
const {task, resolve, reject} = this.tasks.shift()
task().then(resolve, reject).finally(() => {
this.runningCout--
this._run()
})
}
}
}
function timeout(time) {
return new Promise(resolve => {
setTimeout(() =>{
resolve()
}, time)
})
}
const superTask = new SuperTask()
function addTask(time, name) {
superTask
.add(() => timeout(time))
.then(() => {
console.log(`任务${name}完成`)
})
}
addTask(10000, 1) // 10000ms后输出 任务1完成
addTask(5000, 2) // 5000ms后输出 任务2完成
addTask(3000, 3) // 8000ms后输出 任务3完成
addTask(4000, 4) // 1200ms后输出 任务4完成
addTask(5000, 5) // 1500ms后输出 任务5完成
实现arrange函数
/**
* 实现一个 arrange 函数,可以进行时间和工作调度
* 注意,这里的 wait do 均可以无限调用
*
* - 具体功能参考下列示例
* - 在示例中调用到的方法都需要实现
* - 下面示例中 `>` 表示在控制台中输出 (console.log)
*
* --- 示例 ---
*
* 示例一:
* `arrange('William').execute();`
* > William is notified
*
* 示例二:
* `arrange('William').wait(5).do('commit').wait(5).do('push').execute();`
* > William is notified
* 等待 5s...
* > Start to commit
* 等待 5s
* > Start to push
*
*/
function arrange(str) {
return new Arrange(str);
}
class Arrange {
constructor(str) {
this.str = str
this.delay = 0
}
execute = function () {
console.log(this.str)
return this
}
wait = function (delay) {
this.delay += delay * 1000
return this
}
do = function (str2) {
setTimeout(() => {
console.log(str2)
}, this.delay);
return this
}
}
arrange('William').wait(5).do('commit').wait(5).do('push').execute();
生成随机颜色的两种方式
const generateRandomHexColor = () => {
return `#${Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0')}`
}
const generateRandomRGBA = () => {
const r = Math.floor(Math.random() * 256)
const g = Math.floor(Math.random() * 256)
const b = Math.floor(Math.random() * 256)
const a = Math.random().toFixed(2)
return `rgba(${[ r, g, b, a ].join(',')})`
}
复制内容到剪贴板的两种方式
const copyToClipboard = (text) => navigator.clipboard && navigator.clipboard.writeText && navigator.clipboard.writeText(text)
const copyToClipboard = (content) => {
const textarea = document.createElement("textarea")
textarea.value = content
document.body.appendChild(textarea)
textarea.select()
document.execCommand("Copy")
textarea.remove()
}
获取URL中的查询参数
const parseQuery = (name) => {
return new URL(window.location.href).searchParams.get(name)
}
Please wait for a while
const timeout = (timeout) => new Promise((rs) => setTimeout(rs, timeout))
打乱数组
const shuffle = (array) => array.sort(() => 0.5 - Math.random())
深拷贝一个对象
如何深拷贝一个对象?使用 structuredClone 使它变得非常容易。
const obj = {
name: 'fatfish',
node: {
name: 'medium',
node: {
name: 'blog'
}
}
}
const cloneObj = structuredClone(obj)
cloneObj.name = '1111'
cloneObj.node.name = '22222'
console.log(cloneObj)
/*
{
"name": "1111",
"node": {
"name": "22222",
"node": {
"name": "blog"
}
}
}
*/
console.log(obj)
/*
{
"name": "fatfish",
"node": {
"name": "medium",
"node": {
"name": "blog"
}
}
}
*/
确保一个元素在可见区域内
const isElInViewport = (el) => {
return new Promise(function(resolve) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.target === el) {
resolve(entry.isIntersecting)
}
})
})
observer.observe(el)
})
}
const inView = await isElInViewport(document.body)
console.log(inView) // true
获取当前选中的文本
const getSelectedContent = () => window.getSelection().toString()
获取所有浏览器cookie
const getAllCookies = () => {
return document.cookie.split(";").reduce(function(cookieObj, cookie) {
const cookieParts = cookie.split("=")
cookieObj[cookieParts[0].trim()] = cookieParts[1].trim()
return cookieObj
}, {})
}
删除指定名称的cookie
const clearCookie = (name) => {
document.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
}
将多维数组转换为一维数组
const flatten = (array) => {
return array.reduce((result, it) => {
return result.concat(Array.isArray(it) ? flatten(it) : it)
}, [])
}
使用代理实现单例
// 简单判断两个值是否相等
function isSame(value1, value2) {
if (value1.length !== value2.length) {
return false
}
for (let i = 0; i < value1.length; i++) {
if (value1[i] !== value2[i]) {
return false
}
}
return true
}
function singleton(className) {
let ins;
let parameters;
return new Proxy(className, {
constructor(target, ...args) {
if (!ins) {
ins = new ClassName(target, ...args)
parameters = args
}
if (!isSame(parameters, args)) {
throw new Error('不能创建单例')
}
return ins
}
})
}
系统取色器
async function sysColor() {
const dropper = new EyeDropper()
try {
const result = await dropper.open()
console.log(result)
} catch {
console.log('用户取消')
}
}