blog
blog copied to clipboard
Javascript utils
Javascript 工具函数整理
arrayToObject.js https://github.com/redux-utilities/redux-actions/blob/master/src/utils/arrayToObject.js
export default (array, callback) =>
array.reduce(
(partialObject, element) => callback(partialObject, element),
{}
);
camelCase.js https://github.com/redux-utilities/redux-actions/blob/master/src/utils/camelCase.js
import camelCase from 'lodash/camelCase';
const namespacer = '/';
export default type =>
type.indexOf(namespacer) === -1
? camelCase(type)
: type
.split(namespacer)
.map(camelCase)
.join(namespacer);
compose合并函数依次执行 - 来源redux
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
数字转为千分位字符
/**
* 数字转为千分位字符
* @param {Number} num
* @param {Number} point 保留几位小数,默认2位
*/
function parseToThousandth(num, point = 2) {
let [sInt, sFloat] = (Number.isInteger(num) ? `${num}` : num.toFixed(point)).split('.')
sInt = sInt.replace(/\d(?=(\d{3})+$)/g, '$&,')
return sFloat ? `${sInt}.${sFloat}` : `${sInt}`
}
带延时功能的链式调用
// 1) 调用方式
new People('whr').sleep(3000).eat('apple').sleep(5000).eat('durian')
// 2) 打印结果
(等待3s)--> 'whr eat apple' -(等待5s)--> 'whr eat durian'
// 3) 以下是代码实现
class People {
constructor(name) {
this.name = name
this.queue = Promise.resolve()
}
eat(food) {
this.queue = this.queue.then(() => {
console.log(`${this.name} eat ${food}`)
})
return this
}
sleep(time = 0) {
this.queue = this.queue.then(() => new Promise(res => {
setTimeout(() => {
res()
}, time)
}))
return this
}
}
一行代码实现简单模版引擎
function template(tpl, data) {
return tpl.replace(/{{(.*?)}}/g, (match, key) => data[key.trim()])
}
// 使用:
template('我是{{name}},年龄{{age}},性别{{sex}}', {name: 'aa', age: 18, sex: '男'})
// "我是aa,年龄18,性别男"
循环对象
buildSignString(responseData={page:1,appId:123456}){
let signArr
for (let key in responseData){
// console.log('key--',key) page,appId
// console.log('value--',responseData[key]) 1,123456
}
console.log('signArr---',signArr)
}
中文url编码
newString = encodeURI(string)
去掉字符串首位
const a='abcd'
const b=a.slice(1,-1)
console.log(b)
// bc
将一位数组分割成每三个一组
var data = ['法国','澳大利亚','智利','新西兰','西班牙','加拿大','阿根廷','美国','0','国产','波多黎各','英国','比利时','德国','意大利','意大利',];
var result = [];
for(var i=0,len=data.length;i<len;i+=3){
result.push(data.slice(i,i+3));
}
[['法国','澳大利亚','智利'],['新西兰','西班牙','加拿大'],['阿根廷','美国','0'],['国产','波多黎各','英国'],['比利时','德国','意大利'],['意大利'],]
数组浅拷贝
const newImages = state.list.slice(0)
JS获取URL中参数值(QueryString)
function getQueryString(name) {
let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
let r = window.location.search.substr(1).match(reg); //获取url中"?"符后的字符串并正则匹配
let context = "";
if (r != null)
context = r[2];
reg = null;
r = null;
return context == null || context == "" || context == "undefined" ? "" : context;
}
alert(getQueryString("q"));
检测是否为 PC 端浏览器
export const isPC = () => { //是否为PC端
const userAgentInfo = navigator.userAgent;
const Agents = ["Android", "iPhone",
"SymbianOS", "Windows Phone",
"iPad", "iPod"];
let flag = true;
for (let v = 0; v < Agents.length; v++) {
if (userAgentInfo.indexOf(Agents[v]) > 0) {
flag = false;
break;
}
}
return flag;
}
判断是否是在safari浏览器中打开
var issafariBrowser = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
横竖屏检测最佳实践
export const isHorizontalScreen = () => {
const clientWidth = document.documentElement.clientWidth;
const screenWidth = window.screen.width;
const screenHeight = window.screen.height;
// 2.在某些机型(如华为P9)下出现 srceen.width/height 值交换,所以进行大小值比较判断
const realScreenWidth = screenWidth < screenHeight ? screenWidth : screenHeight;
const realScreenHeight = screenWidth >= screenHeight ? screenWidth : screenHeight;
if (clientWidth == realScreenWidth) {
// 竖屏
return false
}
if (clientWidth == realScreenHeight) {
// 横屏
return true
}
}
image to dataurl
export function getDataUri(url, callback) {
let image = new Image();
image.onload = function () {
let canvas = document.createElement('canvas');
canvas.width = image.width; // or 'width' if you want a special/scaled size
canvas.height = image.height; // or 'height' if you want a special/scaled size
canvas.getContext('2d').drawImage(image, 0, 0);
// Get raw image data
//callback(canvas.toDataURL('image/png').replace(/^data:image\/(png|jpg);base64,/, ''));
// ... or get as Data URI
callback(canvas.toDataURL('image/png'));
};
image.src = url;
}
// Usage
getDataUri('/logo.png', function (dataUri) {
// Do whatever you'd like with the Data URI!
});
计时
console.time()和console.timeEnd()方法 Chrome等浏览器自带一个console.time()和console.timeEnd()方法,能够用更简单的代码实现上述功能。 当需要统计一段代码的执行时间时,可以使用console.time方法与console.timeEnd方法,其中console.time方法用于标记开始时间,console.timeEnd方法用于标记结束时间,并且将结束时间与开始时间之间经过的毫秒数在控制台中输出。这两个方法的使用方法如下所示。 console.time(label) console.timeEnd(label) 这两个方法均使用一个参数,参数值可以为任何字符串,但是这两个方法所使用的参数字符串必须相同,才能正确地统计出开始时间与结束时间之间所经过的毫秒数。
console.time(label)
console.timeEnd(label)
let start = window.performance.now();
...
let end = window.performance.now();
let time = end - start;
去掉当前页面的 hash 而不刷新页面
window.history.pushState(
{},
'',
window.location.href.slice(
0,
window.location.href.indexOf('#')
? window.location.href.indexOf('#')
: 0))
获取前N天的日期
方法一: 用setDate();
function getDate(index){
let date = new Date(); //当前日期
let newDate = new Date();
newDate.setDate(date.getDate() + index);//官方文档上虽然说setDate参数是1-31,其实是可以设置负数的
let time = newDate.getFullYear()+"-"+(newDate.getMonth()+1)+"-"+newDate.getDate();
return time;
}
console.log(getDate(7)); // 2019-7-10
console.log(getDate(-7)); // 2019-6-26
方法二: 时间戳进行转换
let date= new Date();
let newDate = new Date(date.getTime() - 7*24*60*60*1000);
let time = newDate.getFullYear()+"-"+(newDate.getMonth()+1)+"-"+newDate.getDate();
conosole.log(time);
// 获取近n天的日期列表
function getDateList(index) {
let list = []
if (index >= 0) {
for (let i = 0; i < index; i++) {
list.push(getDate(i))
}
} else {
for (let i = 0; i > index; i--) {
list.push(getDate(i))
}
list.reverse()
}
return list
}
console.log(getDateList(-2)) // ["2019-7-2", "2019-7-3"]
复制内容到剪贴板
function copy(content) {
const input = document.createElement('input');
input.setAttribute('readonly', 'readonly');
input.setAttribute('value', content);
document.body.appendChild(input);
input.setSelectionRange(0, 9999);
input.select()
if (document.execCommand('copy')) {
const result = document.execCommand('copy');
if (result) {
console.log('复制成功');
} else {
console.warn('复制失败')
}
}
document.body.removeChild(input);
}
URLSearchParams 查询参数
假设浏览器的url参数是 "?name=蜘蛛侠&age=16"
new URLSearchParams(location.search).get("name"); // 蜘蛛侠
classList
这是一个对象,该对象里封装了许多操作元素类名的方法:
<p class="title"></p>
let elem = document.querySelector("p");
// 增加类名
elem.classList.add("title-new"); // "title title-new"
// 删除类名
elem.classList.remove("title"); // "title-new"
// 切换类名(有则删、无则增,常用于一些切换操作,如显示/隐藏)
elem.classList.toggle("title"); // "title-new title"
// 替换类名
elem.classList.replace("title", "title-old"); // "title-new title-old"
// 是否包含指定类名
elem.classList.contains("title"); // false
online state
监听当前的网络状态变动,然后执行对应的方法:
window.addEventListener("online", xxx);
window.addEventListener("offline", () => {
alert("你断网啦!");
});
- deviceOrientation
陀螺仪,也就是设备的方向,又名重力感应,该API在IOS设备上失效的解决办法,将域名协议改成https;
从左到右分别为alpha、beta、gamma;
window.addEventListener("deviceorientation", event => {
let {
alpha,
beta,
gamma
} = event;
console.log(`alpha:${alpha}`);
console.log(`beta:${beta}`);
console.log(`gamma:${gamma}`);
});
使用场景:页面上的某些元素需要根据手机摆动进行移动,达到视差的效果,比如王者荣耀进入游戏的那个界面,手机转动背景图会跟着动😂
orientation
可以监听用户手机设备的旋转方向变化;
window.addEventListener("orientationchange", () => {
document.body.innerHTML += `<p>屏幕旋转后的角度值:${window.orientation}</p>`;
}, false);
也可以使用css的媒体查询:
/* 竖屏时样式 */
@media all and (orientation: portrait) {
body::after {
content: "竖屏"
}
}
/* 横屏时样式 */
@media all and (orientation: landscape) {
body::after {
content: "横屏"
}
}
使用场景:页面需要用户开启横屏来获得更好的体验,如王者荣耀里面的活动页😂
加密库
https://www.npmjs.com/package/crypto-js npm i --save crypto-js
import MD5 from 'crypto-js/md5'
import sha256 from 'crypto-js/sha256'
let stringWithMd5=MD5(stringWithKey).toString().toUpperCase()
时间
new Date()
//Sat Apr 28 2018 14:59:17 GMT+0800 (CST)
+new Date()
//1524898762153
Miment https://github.com/noahlam/Miment/blob/master/README-cn.md
npm i miment
import miment from 'miment'
miment().format('YYYY/MM/DD hh-mm-ss SSS') // 2018/04/09 23-49-36 568
字符串格式数字转换为数值类型
typeof(1)
"number"
typeof('1')
"string"
typeof(+'1')
"number"
注意
console.log(+'a1')
NaN
console.log(+'1a')
NaN
console.log(+'1')
1
匹配,替换,过滤出
var curry = require('lodash').curry;
var match = curry(function(what, str) {
return str.match(what);
});
var replace = curry(function(what, replacement, str) {
return str.replace(what, replacement);
});
var filter = curry(function(f, ary) {
return ary.filter(f);
});
var map = curry(function(f, ary) {
return ary.map(f);
});
match(/\s+/g, "hello world");
// [ ' ' ]
match(/\s+/g)("hello world");
// [ ' ' ]
var hasSpaces = match(/\s+/g);
// function(x) { return x.match(/\s+/g) }
hasSpaces("hello world");
// [ ' ' ]
hasSpaces("spaceless");
// null
filter(hasSpaces, ["tori_spelling", "tori amos"]);
// ["tori amos"]
var findSpaces = filter(hasSpaces);
// function(xs) { return xs.filter(function(x) { return x.match(/\s+/g) }) }
findSpaces(["tori_spelling", "tori amos"]);
// ["tori amos"]
var noVowels = replace(/[aeiou]/ig);
// function(replacement, x) { return x.replace(/[aeiou]/ig, replacement) }
var censored = noVowels("*");
// function(x) { return x.replace(/[aeiou]/ig, "*") }
censored("Chocolate Rain");
// 'Ch*c*l*t* R**n'
reverse 反转列表,head 取列表中的第一个元素;所以结果就是得到了一个 last 函数(即取列表的最后一个元素),虽然它性能不高。这个组合中函数的执行顺序应该是显而易见的。尽管我们可以定义一个从左向右的版本,但是从右向左执行更加能够反映数学上的含义
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
}
var head = function(x) { return x[0]; };
var reverse = reduce(function(acc, x){ return [x].concat(acc); }, []);
var last = compose(head, reverse);
last(['jumpkick', 'roundhouse', 'uppercut']);
//=> 'uppercut'
var initials = compose(join('. '), map(compose(toUpperCase, head)), split(' '));
initials("hunter stockton thompson");
// 'H. S. T'
HTML实体编码
// HTML实体编码
function escapeHtml(string) {
var entityMap = {
"&": "&",
"<": "<",
">": ">",
'"': '"',
"'": ''',
"/": '/'
}
return String(string).replace(/[&<>"'\/]/g, function (s) {
return entityMap[s]
})
}
var string5 = "<div class='div2' name='" + escapeHtml(str1) + "'>test2222</div>"
使用展开运算符后的数组对象
const arr = [...Array(100)].map((_, i) => i);
console.log(arr[0]);
循环对象
for (let optionItem in this.options){
console.log('toggleOptions---optionItem',optionItem)
}
日历
const caleandar={
month: new Date().getMonth()+1, // 本月月份
date: new Date().getDate(), // 今日日期
day: new Date().getDay(), // 今日周几
year: new Date().getFullYear(), // 年份
}
var mydate=new Date();
var myyear=mydate.getYear();
var mymonth=mydate.getMonth()+1 //注:月数从0~11为一月到十二月
var mydat=mydate.getDate()
var myhours=mydate.getHours()
var myminutes=mydate.getMinutes()
var myseconds=mydate.getSeconds()
var myday=mydate.getDay() //注:0-6对应为星期日到星期六
// 获取本月1日周几
const day=new Date(this.caleandar.year,this.caleandar.month-1,1).getDay()
// 获得某月天数
getDaysInOneMonth(year, month) {
month = parseInt(month, 10)
var d = new Date(year, month, 0)
return d.getDate()
}
// 计算该月日期集合
getDaysArray(day,days) {
console.log(day,'',days)
let daysArray=[]
let dayNumber=1
while (day){
daysArray.push('')
day--
}
while (days){
daysArray.push(dayNumber)
dayNumber++
days--
}
return daysArray
}
格式化时间戳
/**
* 格式化时间戳
*
* 2018/8/1 下午3:21:02
*
* @param timestamp
* @returns {string}
*/
export const formatTimestamp = (timestamp) => {
let unixTimestamp = new Date(timestamp * 1000)
return unixTimestamp.toLocaleString()
}
解析url参数
/**
* 解析url参数
* @param url
* @returns {{}}
*/
parseURL(url) {
let qs = url.split("?")
qs = qs[1] ? qs[1] : ""
let obj = {}
if ('string' !== typeof qs || qs.length === 0) {
return obj
}
let key = []
let eq = '='
let decode = decodeURIComponent
qs = qs.split("&")
let qsLen = qs.length
for (let i = 0; i < qsLen; ++i) {
let x = qs[i]
let idx = x.indexOf('=')
let k
let v
if (idx >= 0) {
k = decode(x.substring(0, idx))
v = decode(x.substring(idx + 1))
} else {
k = x
v = ''
}
if (key.indexOf(k) === -1) {
obj[k] = v
key.push(k)
} else if (obj[k] instanceof Array) {
obj[k].push(v)
} else {
obj[k] = [obj[k], v]
}
}
return obj
}
获取格林尼治时间
new Date().toGMTString()
var dt = new Date;
dt.setMinutes( dt.getMinutes() + dt.getTimezoneOffset() ); // 当前时间(分钟) + 时区偏移(分钟)
console.log( "格林尼治时间戳: ", dt.getTime() );
console.log( "用本地时间格式显示: ", dt.toLocaleString() );
JSON 转 formData
function buildFormParams(params) {
let formData = new FormData();
for (let name in params) {
formData.append(`${name}`, `${params[name]}`);
}
return formData;
}
fetch
const paramsStr = buildFormParams({});
fetch(CouponsApi, {
method: 'POST',
body: paramsStr,
headers: {},
})
.then((res) => {
res.text().then((responseText) => {
const response = JSON.parse(responseText);
console.log('response', response)
const code = response.user_code || ''
});
})
.catch((error) => {
console.warn('error', error)
});
校验
手机号格式验证
isMoblie(phone) {
return /^1(3|5|6|7|8|9)[0-9]{9}$/.test(phone)
}
匹配
将p标签转换为view标签
const brTagReg = /<br>|<br\/>/gi
const pLeftTagReg = /<p>/gi
const pRightTagReg = /<\/p>/gi
export const fromatPTag = (htmlContent) => {
let string = htmlContent.replace(brTagReg, '')
string = string.replace(pLeftTagReg, '<view>')
return string.replace(pRightTagReg, '</view>')
}
const a = '<p>test</p>111<br>222<br/>'
console.log(fromatPTag(a))
移动相关
滚动到页面指定元素 https://gist.github.com/WangShuXian6/e9e37ba8e2a540fcc5b1af7d69966c60 http://jsbin.com/sakayax/1/edit?html,output
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>滚动到页面指定元素</title>
<style type="text/css">
#target{
position: absolute;
top:2000px;
width: 100%;
background: #49a9ee;
}
#btn{
position: fixed;
top:0;
left:0;
padding:5px 20px;
background: #00a854;
}
</style>
</head>
<body>
<button type="button" id="btn">scroll</button>
<div id="target">Hello world</div>
<script>
let btn=document.getElementById('btn');
let target=document.getElementById('target');
function animateScroll(element,speed) {
let rect=element.getBoundingClientRect();
//获取元素相对窗口的top值,此处应加上窗口本身的偏移
let top=window.pageYOffset+rect.top;
let currentTop=0;
let requestId;
//采用requestAnimationFrame,平滑动画
function step(timestamp) {
currentTop+=speed;
if(currentTop<=top){
window.scrollTo(0,currentTop);
requestId=window.requestAnimationFrame(step);
}else{
window.cancelAnimationFrame(requestId);
}
}
window.requestAnimationFrame(step);
}
btn.onclick=function (e) {
animateScroll(target,50);
};
</script>
</body>
</html>
滚动到顶部
initScrollEvent()
function initScrollEvent() {
const goTopButton = document.getElementsByClassName('go-top')[0]
goTopButton.onclick = handleGoTop
}
function scrollToTop() {
if (document.body.scrollTop != 0 || document.documentElement.scrollTop != 0) {
window.scrollBy(0, -50);
timeOut = setTimeout('scrollToTop()', 10);
}
else clearTimeout(timeOut);
}
function handleGoTop(e) {
e.preventDefault()
scrollToTop()
console.log('ee')
}
DOM
查找元素
let doms=document.querySelectorAll("a[title='jump-to-ten']")
网络请求
<!-- ih_request.js -->
const request = require('request');
var ih_request = {};
module.exports = ih_request;
ih_request.get = async function(option){
var res = await req({
url: option.url,
method: 'get'
})
res.result?option.success(res.msg):option.error(res.msg);
}
const request = require('../script/ih_request');
await request.get({
url: 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=SECRET',
success: function(res){
console.log(res.access_token)
},
error: function(err){
console.log(err)
}
})
最佳实践
如果有更好的实现,尽量不要使用三元表达式
let score = val ? val : 0 // ✗ 错误
let score = val || 0 // ✓ 正确
add remove
const tabUtil= {
addTab: id => {
this.setState({
tabs: [...tabs, id],
});
},
removeTab: id => {
this.setState({
tabs: tabs.filter(currentId => currentId !== id),
});
},
},
使用字典格式重构多条件判断语句
export const DIRECTION = {
LEFT: 'LEFT',
RIGHT: 'RIGHT',
TOP: 'TOP',
BOTTOM: 'BOTTOM',
HORIZONTAL: 'HORIZONTAL',
VERTICAL: 'VERTICAL'
}
export const ARROW_DIRECTION = [
{isHorizontalScreen: true, isScrollVertical: false, direction: DIRECTION.HORIZONTAL},
{isHorizontalScreen: false, isScrollVertical: false, direction: DIRECTION.VERTICAL},
{isHorizontalScreen: false, isScrollVertical: true, direction: DIRECTION.HORIZONTAL},
{isHorizontalScreen: true, isScrollVertical: true, direction: DIRECTION.VERTICAL},
]
const arrowDirectionData = ARROW_DIRECTION.filter((item) => {
return item.isScrollVertical === this.config.canvas.isScrollVertical &&
item.isHorizontalScreen === isHorizontalScreen()
})
const renderType = arrowDirectionData.length ? arrowDirectionData[0].direction : 'undefind'
const html=(strings,...values)=>{
return values.reduce(
(s,v,i)=>s+String(v)+strings[i+1],strings[0]
)
}
格式化
将对象属性所有值转为字符串
function eventToStringAdapter(event={}){
if(typeof event !== 'object'){
return console.warn('事件不是对象:',event)
}
const keys=Object.keys(event)
const stringEvent=keys.reduce((accumulator, currentKey)=>{
const currentPram=event[currentKey]
if(typeof currentPram === 'object'){
const stringPram={[currentKey]:eventToStringAdapter(currentPram)}
return Object.assign({},accumulator,stringPram)
}
const stringPram={[currentKey]:String(currentPram)}
return Object.assign({},accumulator,stringPram)
},{})
return stringEvent
}
const demoAAA={
a:1,
b:{
A:2,
B:'3',
C:true,
D:false,
E:0
},
c:{
AA:2,
BB:{
EE:33,
DD:false
}
}
}
const demoBBBB=eventToStringAdapter(demoAAA)