js小方法
小技巧
if(value)
直接value表示判断是否有内容,默认为有;元素.checked=true
;复选框被选中在函数里面递归调用必须
return
,否则不走后面trim()
;常用于value;干掉左右空格;
判断value输入为空时,不让提交,如果value输入空格,也能提交,此时,需要
input.value.trim()
;使用trim方法可以清除value里面的空格并弹窗;,ie8不支持;
value写完后可以在按钮后面加一行代码:input.value = '';清空内容方便用户下次输入;
数组去重可以创建一个空的数组,
indexOf
查询没有的往里面放;调用一个方法,如果这个方法没有前缀,则
this
指向window
;有前缀指向调用的,所以谁调用的函数,这个this就指向谁document.onkeydown
;document页面直接执行按键输入;点的形式都可以使用中括号的形式
jslet obj = {name: 'hny'} console.log(obj.name) // or console.log(obj['name']) // 一般中括号都是用来放变量的, 如下 var s = 'name' console.log(obj[s])
当输出对象遇到[object object]时,使用JSON.stringify转字符串输出;
当在不使用模板字符换行时,可以使用折行转义字符
\
jsvar htmlSTring = "<div>\ This is a string.\ </div>";
一个页面不能有两个window.onscroll,可以使用绑定事件来做两个
jswindow.addEventListener('scroll',()=>{}); // 如果使用两个window.onscroll, 后一个则会覆盖前一个
给代码添加debugger可以测试bug;
debugger
可以查看我写的浏览器技巧 断点内容;Object.prototype.hasOwnProperty.call(obj,i)
相当于以下jsobj.hasOwnProperty(i)
数组删除假值
jsvar arr = [1, '', null, undefined, true, false, '2'] arr.filter(Boolean) // => [1, true, '2']
利用es6默认赋值语法,判断参数是否为空值
jsconst isRequired = () => throw new Error('param is required') const print = (num = isRequired()) => { console.log('print: ', num) }
判断语句省略写法
//判断;真就真
if(count === otherche.length){ //判断是否等于长度
allche.checked = true;
}else{
allche.checked = false;
} //因为就给allche.checked赋一个值,true或false;
//省略写法
allche.checked = (count === otherche.length); //将后面两个值赋给前面一个;
//判断真假 为真就为假
if(check[0].checked){ 如果为真,就为假
check[i].checked = false;
}else{ 如果为假,就为真
check[i].checked = true;
}
//省略写法:
check[i].checked = !check[0].checked; //取返
// 如果第二个为真,就变假给第一个,如果第二个为假,就变真给第一个;
// 只要是true或false的,都可以利用感叹号取非;
//按钮事件 真就真
if(allche.checked){ //如果被选中就全部选中
che[i].checked = true;
}else{
che[i].checked = false;
}
//省略写法
che[i].checked = allche.checked; //全部等于这个;
if(fn){
fn()
}
//省略写法
fn && fn(); 前面为真,才看后面的;
js基础技巧
计数器
js最基础的使用技巧
实例:多个复选框全选可以创建一个计数器,选中一个计数器+1,判断计数器是否和复选框的个数一样,如果一样,则让全选按钮被选中;
保存this指向
函数里面再创建一个函数时,这时的this指向就变了,可以创建一个变量,保存this指向,里面就可以随便使用该变量,也不用担心this的指向问题, 也可以使用箭头函数避免这个问题
// vue示例
methods: {
// 一种写法
msg () {
var that = this
function fn(){
console.log(that) // 该that指向vue实例, this指向调用他的this
}
},
// 另一种写法
msg () {
cont fn = () => {
console.log(this) // 该this指向vue实例
}
}
}
取名字技巧
设置的都用:set
开头
获取的都用:get
开头
卸载都用: un
函数:handle
创建块:demo
(演示),wrap
(包装),box
(盒子),panel
(面版)
定时器使用函数需要传参时
// 禁止写法
setInterval(timer(1), 1000) // 因为这种写法的函数直接执行了
// 正确写法
setInterval(function(){
timer(1)
}, 1000)
// 错误写法
div.onclick = change()
// 正确写法
div.onclick = function(){
change()
}
随机id
Math.random().toString(36).substr(3,6) //得到6位随机id
查看随机6位数;
Array.from({length:20}, i => Math.ceil(Math.random() * 6))
随机16进制颜色
const RandomColor = ()=> '#' + Math.floor(Math.random()*0xffffff).toString(16).padEnd(6,'0'); // 由于会出现5位数, 因此加上padEnd保险
合并多个对象
const o = {
name: 'hny'
}
const e = {
eat: 'banana'
}
const obj = {...o, ...e} // => {name: 'hny', eat: 'banana'}
const obj2 = Object.assign(o, e) // => {name: 'hny', eat: 'banana'}
模板标签替换
let text = '大家好,我的{{name}},今年{{age}}岁'
let obj = {name:'zs',age:18}
let result = text.replace(/{{(.*?)}}/g,e=>{
return obj[e.replace(/{|}/g,'')
}) // 大花括号也可以没有转义符;
const template = 'My name is ${name}, age ${age}, I am a ${job.name}'
const employee = {
name: 'fatfish',
age: 100,
job: {
name: 'front end development'
}
}
// ag: 1
String.prototype.render = function (obj) {
const template = this
const variableRegex = /\$\{([^${}]+)\}/g
const getVariableValue = (variable) => {
// [ 'name' ]、[ 'age' ]、[ 'job', 'name' ]
variable = variable.split('.')
let variableValue = obj
while (variable.length) {
variableValue = variableValue[ variable.shift() ]
}
return variableValue
}
const renderStr = template.replace(variableRegex, ($0, variable) => {
return getVariableValue(variable)
})
return renderStr
}
// ag: 2
String.prototype.render = function (obj) {
const template = this
// var { name, age, job } = obj
eval(`var {${Object.keys(obj).join(',')}} = obj`)
// `My name is ${name}, age ${age}, I am a ${job.name}`
const renderStr = eval('`' + template + '`')
return renderStr
}
// ag: 3
String.prototype.render = function (obj) {
with(obj) {
return eval('`' + this + '`')
}
}
参考:面试官:你能实现一个 JavaScript 模板引擎吗?
交换数组指定位置
[1,2,3,4,5] ===> [5,2,3,4,1]
function arrIndexExchange(array,x,y){
return array.splice(x-1,1,...array.splice(y-1,1,array[x-1]))
}
生成uuid
uuid的格式为:8-4-4-4-12
function S4(){
return (((1+Math.random())*0x10000|0)).toString(16).substr(0,4)
}
function uuid(){
return `${S4()}${S4()}-${S4()}-${S4()}-${S4()}-${S4()}${S4()}${S4()}`
}
鼠标禁止右键
<body oncontentmenu='return false'>
<div></div>
</body>
复制功能
const copyFun = cont => {
const textArea = document.createElement('textarea')
textArea.value = cont
document.body.appendChild(textArea)
textArea.select()
document.execCommand('copy') //开启复制;
document.body.removeChild(textArea)
}
将Unicode码转回英文
String.fromCharCode()
//括号填写数字,
例子:取随机26个小写英文字母,
在ASCII码中,26个小写英文字母在97-122之间
String.fromCharCode(97+Math.ceil(Math.random()*25)) //返回一个小写字母
将url转换成对象形式取值
- 使用while循环+正则
function formatSearch(search, o={}){
let reg = /(\w+)=(\w+)/ig
while(reg.exec(search)){
o[RegExp.$1] = RegExp.$2
}
return o;
}
// 将match传入即可;
- 使用api
let str = '?name=hny&age=18&sex=1'
Object.fromEntries(new URLSearchParams(str)) // {name:'hny',age:18,sex:1}
- 使用正则
function formatUrl(search, o={}){
search.replace(/([^?&=]+)=([^&]+)/g,(_,k,v)=>o[k]=v)
return o
}
// 传入match即可
简单封装qs
const qs = {
parse: str => Object.fromEntries(new URLSearchParams(str)),
stringify: obj => Object.keys(obj).reduce((pre,cur) => (pre+=`${cur}=${obj[cur]}&`,pre), '?').slice(0,-1)
}
实现图片的异步加载
img文档: https://www.w3school.com.cn/jsref/dom_obj_image.asp
// data-* 是html5的新特性
<img data-src='' src='' alt='' /> // 通过img.dataset.src可以获取到data-src的数据;
// 可以通过循环获取img对象来修改对象的src;
function loadImg(obj, url){
var img = new Image()
img.src = url
// 当图片加载完成时的事件
img.onload = function(){
obj.src = img.src // 当图片加载完成时,渲染页面的src;
}
// 当图片加载失败时的事件
img.onerror = function(){}
}
清除缓存技巧
数据不更新,本地有缓存,可以在网址后面加个时间戳
var t = Date.now()
var url = `?t=${t}`
临时使用$获取元素
当一个函数里面需要获取多个节点时,可以使用下面方法
;(function($){
$('div').scrollTop = $('.center').clientHeight
})(document.querySelector.bind(document))
检测移动端
isMobile () {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
解决谷歌翻译代码块的问题
将以下代码添加到一个新的书签,网址写以下代码
javascript:(function(){
// 不需要翻译的单词
var wordArr = ['options','node','postcss','gulp','grunt']
var reg = eval(`/${wordArr.join('|')}/ig`)
// 不需要翻译的标签名
var labelArr = ['pre','code']
var reg2 = eval(`/<${labelArr.join('>|<')}>/ig`)
document.documentElement.innerHTML = document.documentElement.innerHTML.replace(reg,val=>`<span class='notranslate'>${val}</span>`)
document.documentElement.innerHTML = document.documentElement.innerHTML.replace(reg2,val=>`<${val.slice(1,-1)} class='notranslate'>`)
})()
总数格式化
例如:1000,转换为1k
function formatStatistics (num) {
if (!num) {
return 0
} else if (num > 999) {
// 取小数点前后
let [dotBefore, dotAfter] = (num / 1000).toFixed(2).split('.')
// 解决1300出现1.30的情况
let dotAfterNum = dotAfter % 10 > 0 ? dotAfter : dotAfter[0]
return `${dotBefore}${dotAfter > 0 ? '.' + dotAfterNum : ''}k`
} else {
return num
}
}
对接url传输加密
encodeURIComponent
对链接加密,将url上面的符号添加%转码
decodeURIComponent
对加密的url进行解密
escape
:加密汉字
unescape
:解密
解决toFixed四舍五入的问题
export function toFixed(value, precision) {
if (typeof value === 'undefined') return ''
return new Intl.NumberFormat(undefined, {
useGrouping: false,
minimumFractionDigits: precision,
maximumFractionDigits: precision,
}).format(value)
}
arguments.callee
属性包含当前正在执行的函数
主要用途是由于函数内部需要调用自己,可以直接使用arguments.callee调用,如果直接写当前函数名,该函数就会紧紧耦合在一起,由于函数名只是一个包含指针的变量,因此将另一个函数名也指向同一个函数,则不可用了,因为调用自己的名字不一样了
调用arguments.callee.toString()
返回这个匿名函数;
arguments.callee
可以直接调用当前函数;
arguments.callee.caller
这个属性中保存着调用当前函数的函数的引用(可以查看红宝书的详细介绍115页)
常用对象的方法
判断对象的数据类型
const isType = type => target => `[object ${type}]` === Object.prototype.toString.call(target)
const isArray = isType('Array')
console.log(isArray([])) // true
//分解
function isType(type){
return function(target){
`[object ${type}]` === Object.prototype.toString.call(target)
}
}
精准判断类型:(类型需要大写)
'[object 类型]' === Object.prototype.toString.call(形参)
动态属性名
声明一个变量;使用中括号代替,也可以是对象变量属性;
// 动态属性名
let dys = 'email'
let user = {name:'join',[dys]:'john@qq.com'}
let flag = true
let user = {name: 'join',[flag?'c':'d']:2}
例子2
let obj = [{name:'lis',flag:'no'},{name:'zs',flag:'yes'}]
Array.from(obj,({name,flag})=>{
return { [name]:visi }
// 名字需要是引号的,所以需要中括号;
})
// [{lis:no},{zs:yes}]
有条件的象征对象
在对象里面使用...去使用布尔值;
function obj(flag){
return {
name: 'limin',
...flag && {age: 18}
}
}
obj(true) // {name:'',age:''}
obj(false) // {name: ''} // 如果不传入也是一个值;
// 解析:如果flag成立,则...{}将对象解构, 如果不成功则不解构
// 延伸 动态添加数组值;
let arr = [
{name: 'hh'},
...[...true ? [{name: 'ls'}] : ''],
{name: 'op'}
]
解构原始对象
提取两部分,分为两个对象;
let obj = {name:'lishi',age:18,city:'北京'}
let user = {},userOther={};
({name: user.name,...userOther} = obj)
设置REM
export const setRem = () => {
const minWidth = 320
const maxWidth = 640
let W = document.documentElement.clientWidth
W = W > maxWidth ? maxWidth : W < minWidth ? minWidth : W
document.documentElement.style.fontSize = W / 3.75 + 'px'
window.onresize = function () {
let W = document.documentElement.clientWidth
W = W > maxWidth ? maxWidth : W < minWidth ? minWidth : W
document.documentElement.style.fontSize = W / 3.75 + 'px'
}
}
判断touch事件滑动的方向
根据移动的值正负来判断
//滑动处理
var startX, startY, moveEndX, moveEndY, X, Y;
mybody.addEventListener('touchstart', function(e) {
e.preventDefault();
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
}, false);
mybody.addEventListener('touchmove', function(e) {
e.preventDefault();
moveEndX = e.changedTouches[0].pageX;
moveEndY = e.changedTouches[0].pageY;
X = moveEndX - startX;
Y = moveEndY - startY;
if ( Math.abs(X) > Math.abs(Y) && X > 0 ) {
alert("向右");
} else if ( Math.abs(X) > Math.abs(Y) && X < 0 ) {
alert("向左");
} else if ( Math.abs(Y) > Math.abs(X) && Y > 0) {
alert("向下");
}else if ( Math.abs(Y) > Math.abs(X) && Y < 0 ) {
alert("向上");
} else{
alert("没滑动");
}
});
判断两个对象是否相等
function isEqual(x, y) {
// 处理没有传入参数的情况
if(typeof x === 'undefined' || typeof y === 'undefined') return false
// 处理不是对象的情况, 如果两个基本类型则直接判断
if (x === y) return true
// 处理不是对象的情况
if (!(x instanceof Object) || !(y instanceof Object)) return false
// 必须是同一原型链
if(x.constructor !== y.constructor) return false
// 如果是数组进行比较
if(Array.isArray(x) && Array.isArray(y)) {
// 如果长度不同,则false
if(x.length !== y.length) return false
for(var i=0;i<x.length;i++){
// 如果相等则跳出
if(x[i] === y[i]) continue
// 判断不是对象的跳出
if(typeof x[i] !== 'object') return false
// 递归重新调用
if(!isEqual(x[i], y[i])) return false
}
return true
}
for (var p in x) {
if(x.hasOwnProperty(p)){
// 如果y里面没有p则false
if(!y.hasOwnProperty(p)) return false
// 如果基础类型相等, 则跳出该循环, 不相等的情况肯定又是对象
if(x[p] === y[p]) continue
// 数字、字符串、函数必须严格相等;
if(typeof x[p] !== 'object') return false
// 递归调用
if(!isEqual(x[p], y[p])) return false
}
}
for(p in y){
if(y.hasOwnProperty(p) && !x.hasOwnProperty(p)) return false
}
return true
}
10秒内不重新请求接口
场景:解决接口数据返回慢的问题
let fetchData = JSON.parse(sessionStorage.getItem('fetchData')) || {
lastUpdate: -1,
valid: false
}
if(+new Date() - lastUpdate > 10000 || !fetchData.valid) {
// 重新请求接口, 接口返回之后设置数据;
sessionStorage.setItem('fetchData', {
lastUpdate: +new Date(),
valid: true
})
}
执行字符串的语句
- eval
- new Function
- setTimeout
计算百分比
参数1:是要计算百分比的数组 参数2:是数组的下标 参数3:要保留几位小数
export function getPercentValue (valueList, idx, precision) {
// 判断是否为空
if (!valueList[idx]) {
return 0;
}
// 求和
var sum = valueList.reduce(function (acc, val) {
return acc + (isNaN(val) ? 0 : val);
}, 0)
if (sum === 0) {
return 0;
}
// 10的2次幂是100,用于计算精度。
var digits = Math.pow(10, precision);
// 扩大比例100,
var votesPerQuota = valueList.map(function (val) {
return (isNaN(val) ? 0 : val) / sum * digits * 100;
})
// 总数,扩大比例意味的总数要扩大
var targetSeats = digits * 100;
// 再向下取值,组成数组
var seats = votesPerQuota.map(function (votes) {
return Math.floor(votes);
})
// 再新计算合计,用于判断与总数量是否相同,相同则占比会100%
var currentSum = seats.reduce(function (acc, val) {
return acc + val;
}, 0)
// 余数部分的数组:原先数组减去向下取值的数组,得到余数部分的数组
var remainder = votesPerQuota.map(function (votes, idx) {
return votes - seats[idx];
})
// 给最大最大的余额加1,凑个占比100%;
while (currentSum < targetSeats) {
// 找到下一个最大的余额,给其加1
var max = Number.NEGATIVE_INFINITY;
var maxId = null;
for (var i = 0, len = remainder.length; i < len; ++i) {
if (remainder[i] > max) {
max = remainder[i];
maxId = i;
}
}
// 对最大项余额加1
++seats[maxId];
// 已经增加最大余数加1,则下次判断就可以不需要再判断这个余额数。
remainder[maxId] = 0;
// 总的也要加1,为了判断是否总数是否相同,跳出循环。
++currentSum;
}
// 这时候的seats就会总数占比会100%
return seats[idx] / digits
}
根据路径找对应值
function getPropByPath (obj, path, strict) {
if(!obj || !path) return undefined;
let tempObj = obj
path = path.replace(/\[(\w+)\]/g, '.$1')
path = path.replace(/^\./, '')
const keyArr = path.split('.')
let i = 0
for (let len = keyArr.length; i < len - 1; ++i) {
if (!tempObj && !strict) break
const key = keyArr[i]
if (key in tempObj) {
tempObj = tempObj[key]
} else {
if (strict) {
throw new Error('please transfer a valid prop path to form item!')
}
break
}
}
return {
o: tempObj,
k: keyArr[i],
v: tempObj ? tempObj[keyArr[i]] : null
}
};
获取Y轴最大值
/**
* echarts 获取y轴最大值
* @returns
*/
export function yAxisMax(maxValue, splitNumber = 5) {
if (Number.isNaN(maxValue / 1) || maxValue / 1 < 10) {
return 10;
}
const max = Math.ceil(maxValue);
const itemValue = `${Math.ceil(max / splitNumber)}`;
const mins = Math.ceil(+itemValue / 10 ** (itemValue.length - 1));
const item = mins * 10 ** (itemValue.length - 1);
// item 需要是5的整数倍
const res = Math.ceil(item / splitNumber) * splitNumber * splitNumber;
return res;
}