javaScript4
sh f1.js标准内置对象
1)Proxy
详细说明 https://www.jianshu.com/p/81eb68ae5eb1
Proxy 对象用于创建一个对象的代理(在目标对象的外层搭建了一层拦截),从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
const p = new Proxy(target, handler)
target
使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler
一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
2.面向对象替代switch、多分支条件判断
//点击底部按钮组
handelClick(clickType) {
if ( this .tableData.multipleSelection.length < 1) {
return this .$message({
type: 'warning' ,
message: '请先勾选需要处理的数据' ,
showClose: true ,
})
}
const types = {
relation: this .relationBatch,
move: this .moveBatch,
copy: this .copyBatch,
unbunding: this .unbundingBatch,
}
return types[clickType]()
},
3.去掉string中的html标签
str..replace(/<[^>]+>/g, '' )
4.优雅删除对象无用key
1.封装函数方法
要删除很多key的时候可用
//要删除的key
const delKeys = ['key1','key2','key3']
delKeysFn(obj)
//封装函数
delKeysFn(obj){
delKeys.formEach((item)=>{
delete obj[item]
})
}
2. Lodash 的 omit 方法
移除不需要的属性
安装lodash
const object = { 'a': 1, 'b': '2', 'c': 3 };
const result = _.omit(object, ['a', 'c']);
3. pick 方法
只留下需要的属性
const object = { 'a': 1, 'b': '2', 'c': 3 };
const result = _.pick(object, ['a', 'c']);
4.自己实现一个 omit
// 中规中矩式
const omit = (obj, uselessKeys) =>
Object.keys(obj).reduce((acc, key) =>
return uselessKeys.includes(key) ?
acc :
{...acc, [key]: acc[key]}
}, {});
// 投机取巧式
const omit = (obj, uselessKeys) =>
uselessKeys.reduce((acc, key) => {
return {...acc, [key]: undefined}
}, obj)
// 粗暴式
const omit = (obj, uselessKeys) => {
uselessKeys.forEach(key => {
delete obj[key]
})
return obj
}
5.reduce的n种应用
使用语法:
array.reduce(function(prev, current, currentIndex, arr), initialValue)
参数说明:
prev:函数传进来的初始值或上一次回调的返回值
current:数组中当前处理的元素值
currentIndex:当前元素索引
arr:当前元素所属的数组本身
initialValue:传给函数的初始值
1.累积和累和
let arr = [1, 2, 3, 4, 5]
arr.reduce((prev,cur)=>prev+cur) //15
arr.reduce((prev,cur)=>prev*cur) //120
2.求最大和最小值
let arr = [1,2,3,4,5]
arr.reduce((prev,cur)=>Math.max(prev,cur)) //5
arr.reduce((prev,cur)=>Math.min(prev,cur)) //1
3.数组去重,数组对象去重
let arr = [1,2,3,1,1,2,3,3,4,3,5]
let res = arr.reduce((prev,cur)=>{
!prev.includes(cur)&&prev.push(cur)
return prev
},[])
//数组对象去重
var arr = [{a:1,b:2},{a:1,b:4},{a:3,b:5}]
const keys = []
const newArr = arr.reduce((pre,next)=>{
if(!keys.includes(next.a)){
keys.push(next.a)
pre.push(next)
}
return pre
},[])
4.实现map函数
map函数接收一个函数作为参数,作为参数的函数接收三个参数值,分别是遍历数组的每一项元素,元素的索引和数组本身,这三个参数刚好和reduce函数接收的第一个函数参数的第2,3,4个参数是对应的。
实现思路是,将每次遍历的元素,作为传入函数的参数,并将函数执行的结果放到新的数组中。
let arr = [1,2,3]
Array.prototype._map = function(cb){
if(typeof cd !== 'function ') return throw new Error(cb + 'is not function')
let arr = this; //this--调用_map方法的当前数组对象
return arr.reduce((prev,item,index,array)=>{
prev.push(cb(item,index,array))
return prev
},[])
}
let res = arr._map(val=>val*2) //[2.4.6]
5.实现filter函数
Array.prototype._filter = function (cb){
if(typeof cb === 'function'){
let arr = this
return arr.reduce((prev,item,index,array)=>{
cb(item,index,array)?prev.push(item):null
return prev
},[])
}else{
throw new Error(cb+'is not function')
}
}
6.实现compose
compose是函数编程的核心,简单说就是将若干个函数合成一个函数来执行,并且每个函数的执行结果都作为下一个函数的参数(从右到左执行)。
假设目前有两个函数:
function toUpperCase(str){
return str.toUpperCase()
}
function add(str){
return str += '!'
}
//一般使用
let str = 'hello world'
let res = toUpperCase(str)
res = add(res)
//使用compose后,执行fn相当于,依次执行了toUpperCase和add
const fn = compose(add,toUpperCase)
fn(str)
//使用reduce实现compose
function compose(){
//将arguments转化为数组
let args = [].slice.call(arguments)
return function(x){
//因为compose()接收的函数参数,是从右往左走顺次执行
//所以这里使用reduceRight,用法和reduce一致,只不过是从右到左遍历数组
return args.reduceRight((prev,cur)=>{
return cur(prev)
},x)
}
}
//[].slice.call() 原理分析
[].slice.call() 常用来将类数组转化为真正的数组
call 调用函数并改变指向
slice()默认返回所有下标的元素并返回新数组
var array = [].slice.call(arrayLike);
array; // ['a', 'b', 'c']
7.数组扁平化
let arr = [1, 2, '3js', [4, 5, [6], [7, 8, [9, 10, 11], null, 'abc'], {age: 12}, [13, 14]], '[]'];
function flatten(arr) {
if (Array.isArray(arr)) {
return arr.reduce((prev, cur) => {
// 如果遍历的当前项是数组,再迭代展平
return Array.isArray(cur) ? prev.concat(flatten(cur)) : prev.concat(cur)
}, [])
} else {
throw new Error(arr + ' is not array')
}
}
8.求对象数组中指定属性的和
const arr = [{x:1},{x:2},{x:3}]
const arr2 = arr.reduce((prev,cur)=>prev+cur.x)
arr2 // 6
9.将数组中每个元素出现的次数统计,以对象形式输出
const arr = ['A','B','A']
const result = arr.reduce((prev,cur)=>{
if(cur in prev){
prev[cur]++
}else{
prev[cur]=1
}
return prev
},{})
result // {A:2,B:1}
10.结合展开运算符使用
const arr = [
{
name:'a',
fruits:[1,2,3]
},
{
name:'b',
fruits:[4,5,6]
}
]
const result = arr.reduce((prev,cur)=>[...prev,...cur.fruits],[7,8])
result //[1,2,3,4,5,6,7,8]
12.获取状态码400的返回值
.catch(error=>{
console.log(error.response.data); //data是错误信息的全部值
console.log(error.response.status);
console.log(error.response.headers);
console.log('Error', error.message);
console.log(error.config);
})
13.对象转数组
const obj = { foo: 'bar', baz: 42 }; console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
14.对象与mp间的转化
从对象转为 Map :new Map(Object.entries(obj))
从 Map 转为对象:Object.fromEntries(map.entries())
15.清除所有定时器
let end = setInterval(function () { }, 10000);
for (let i = 1; i <= end; i++) {
clearInterval(i);
}
16.判断对象是否包含某个key
!("key" in obj) //结果为false,表示不包含;否则表示包含
obj.hasOwnProperty("key") //obj表示对象,结果为false表示不包含;否则表示包含
17.身份证信息脱敏
function noPassByIdcard(str) {
if (null != str && str != undefined) {
var pat = /^(.{4})(?:\w+)(.{3})$/
return str.replace(pat, '$1**********$2')
} else {
return ''
}
}
18.vue的防抖 立即执行
imDebounce(fn, time) {
this.timer && clearTimeout(this.timer)
if (this.flag) {
return fn(), (this.flag = false)
}
this.timer = setTimeout(() => {
this.flag = true
}, time)
},
19.多个promise 管理
async beforeRouteEnter(to, from, next) {
;[pre.dept, pre.noType] = await Promise.all([
deptChooseList({ tree: false }),
noTypes(),
])
next()
},
20.判断对象中是否拥有该key
//hasOwnProperty表示是否有自己的属性。这个方法会查找一个对象是否有某个属性,但是不会去查找它的原型链。
21.脚本命令决定启动模式 获取命令行参数
需求场景:本地开发、测试环境、生产环境可能需要不同的服务地址。测试环境和生产环境打包地址有所区分,每次都需要修改地址后再进行打包稍微有些麻烦。通过npm run build:xxx 以命令行添加参数的方式来决定打包地址刚好可以解决此需求。
实现前先了解几个知识点:
1)process :process对象是一个全局变量,它提供当前 Node.js 进程的有关信息,以及控制当前 Node.js 进程。 因为是全局变量,所以无需使用 require()。
2)process.argv 属性返回一个数组,这个数组包含了启动node.js进程时的命令行参数。
实现分为以下几个步骤
// 1.确定参数名字,package.json中更改启动及打包命令
"dev": "vue-cli-service serve --env dev",
"build:jwtest": "vue-cli-service build --env jwtest",
"build:jwpro": "vue-cli-service build --env jwpro",
//2.vue.config.js 获取命令行参数,取最后一个。
const env_mode = process.argv.pop() //运行模式
console.log(env_mode, 'mode')
//3.vue.config.js module.exports 中添加配置
configureWebpack: {
// provide the app's title in webpack's name field, so that
// it can be accessed in index.html to inject the correct title.
name: name,
plugins: [
new webpack.DefinePlugin({ //定义全局变量
'process.env': {
MODE: JSON.stringify(env_mode), //之后任意文件读取process.env.MODE
},
}),
],
},
//4.创建配置文件xxx.js
const wss_urls = {
dev: 'wss://xxx',
jwtest: 'wss://xxx',
jwpro: 'wss://xxx',
}
const access_urls = {
dev: 'https://xxx',
jwtest: 'https://xxx',
jwpro: 'https://xxx',
}
export const wss_url = wss_urls[process.env.MODE]
export const access_url = access_urls[process.env.MODE]
//5.使用
import { access_url } from '@/utils/http-url'
22.created与activated的区别
使用<keep-alive>会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务。
被包含在 <keep-alive> 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
activated:在组件被激活时调用,在组件第一次渲染时也会被调用,之后每次keep-alive激活时被调用。
deactivated:在组件被停用时调用。
注意:只有组件被 keep-alive 包裹时,这两个生命周期才会被调用,如果作为正常组件使用,是不会被调用,以及在 2.1.0 版本之后,使用 exclude 排除之后,就算被包裹在 keep-alive 中,这两个钩子依然不会被调用!另外在服务端渲染时此钩子也不会被调用的。
什么时候获取数据?
当引入keep-alive 的时候,页面第一次进入,钩子的触发顺序created-> mounted-> activated,退出时触发deactivated。当再次进入(前进或者后退)时,只触发activated。
我们知道 keep-alive 之后页面模板第一次初始化解析变成HTML片段后,再次进入就不在重新解析而是读取内存中的数据,即,只有当数据变化时,才使用VirtualDOM进行diff更新。有需要的话,页面进入的数据获取应该在activated中也放一份。数据下载完毕手动操作DOM的部分也应该在activated中执行才会生效。
所以,有需要的话,应该activated中留一份数据获取的代码,或者不要created部分,直接将created中的代码转移到activated中。
23.foreach不能保证内部异步按照循环顺序执行,需要更换为for of
24.charAt 截取字符串某一项 传入索引
25..匹配任意字符 str.replace(/./g,1)
replaceAll的兼容性太差,可以用replace和正则相结合来代替replaceAll的使用。(小程序安卓不支持)
//全部替换为1
26.获取页面所有标签数量(全部元素)
方法一:
http://t.zoukankan.com/zjmx-p-11866167.html
[...document.querySelectorAll('*')].map(v=>v.tagName).reduce((res,a)=>{res[a]=(res[a] || 0)+1 ; return res;},{})
方法二:
function fold(node){
var map = new Map();
map.set(node.tagName,1);
[].reduce.call(node.children,(acc,child)=>{
deal(acc,fold(child));
return acc
},map)
return map
}
function deal(srcMap,tarMap){
tarMap.forEach((value,tagName)=>{
var newV = value + ~~srcMap.get(tagName);
srcMap.set(tagName,newV);
})
}
27.vue生成带logo的二维码
<qrcode-vue
id="qrcodeBox"
:size="qrcodeVue.size"
:value="item.idCard"
:logo="qrcodeVue.logo"
:bgColor="qrcodeVue.bgColor"
:fgColor="qrcodeVue.fgColor"
></qrcode-vue>
import qrcodeVue from "qrcode-vue";
qrcodeVue: {
size: 50,
bgColor: "#fff",
fgColor: "#000",
// value: "https://www.baidu.com/", //二维码地址
logo: require("@/static/red-cross.png"), //logo图片
},
//生成二维码
// getQRcode() {
// this.qrcodeVue.value = "www.baidu.com"; // 二维码内容
// },
28.时分秒补0 补零
str.padStart(2, "0");
ES2017 引入了字符串补全长度的功能。
如果某个字符串不够指定长度,会在头部或尾部补全。
padStart()用于头部补全,padEnd()用于尾部补全。
29.创建window全局变量
public文件创建config.js
html文件引入 <script src="./config.js"></script>
window.config = {
acticityBaseUrl: 'https://miapp.chuntaoyisheng.com/xz/#/',
templates: {
'template_01.jpg': 'pages/recommend/detailTemplateBase',
'template_02.jpg': 'pages/recommend/detailTemplateOne',
'template_03.jpg': 'pages/recommend/detailTemplateTwo',
'template_04.jpg': 'pages/recommend/detailTemplateThree',
'template_05.jpg': 'pages/recommend/detailTemplateBase',
},
}
//使用方法
子页面:window.config.acticityBaseUrl
30.闭包缓存,传入同样参数时取缓存。
const getRes = (function () {
const obj = {}
return function () {
const params = [...arguments]
const key = JSON.stringify(params)
if (key in obj) {
console.log('缓存取值')
return obj[key]
}
const result = eval(params.join('+'))
obj[key] = result
console.log('计算取值')
return result
}
})()
31.class缓存,传入同样参数时取缓存。
class getRes {
map
constructor() {
this.map = new Map()
}
getResult() {
const params = [...arguments]
const key = JSON.stringify(params)
if (this.map.has(key)) {
console.log('缓存取值', this.map.get(key))
return this.map.get(key)
}
const result = eval(params.join('+'))
this.map.set(key, result)
console.log('计算取值', result)
return result
}
}
const getResClass = new getRes()
const getResult = (...params) => getResClass.getResult(...params)
getResult(1, 2, 3)
getResult(1, 2, 3)
getResult(1, 2)
getResult(1, 2, 3, 4)
getResult(1, 2, 3, 4)

32.常用缓存方式
1.localStorage
2.sessionStorage
3.cookie
4.闭包
5.class
33.全局关闭项目console.log
mainjs里调用
const isDebug = true; // 控制是否屏蔽全局console.log 日志;isDebug设为false即可屏蔽
console.log = (function (oldLogFunc) {
return function () {
if (isDebug) {
oldLogFunc.apply(this, arguments);
}
}
})(console.log);
34.滚动元素到底部
scrollTop 确定垂直滚动的像素数 scrollHeight 是元素的高度(可见和不可见部分)
el.scrollTop = el.scrollHeight;
35.调用浏览器下载功能
dowload(fileName, url) {
let x = new XMLHttpRequest()
x.open('GET', url, true)
x.responseType = 'blob'
x.onload = function (e) {
let blob = x.response
if ('msSaveOrOpenBlob' in navigator) {
//IE导出
window.navigator.msSaveOrOpenBlob(blob, fileName)
} else {
let a = document.createElement('a')
a.download = fileName
a.href = URL.createObjectURL(blob)
document.querySelector('body').appendChild(a)
a.click()
document.querySelector('body').removeChild(a)
}
}
x.send()
},
36.打印单个window元素
1.借用iframe功能 2.生成网页的方式
printCode() {
const iframe = document.createElement('iframe')
iframe.style.height = 0
iframe.style.visibility = 'hidden'
iframe.style.width = 0
// 设置 iframe 内容
iframe.setAttribute('srcdoc', '<html><body></body></html>')
document.body.appendChild(iframe)
iframe.addEventListener('load', function () {
// 克隆页面的图片元素
const image = document.getElementById('codeRef').cloneNode()
image.style.maxWidth = '100%'
// 把克隆的图片元素添加到 iframe 内
const body = iframe.contentDocument.body
body.style.textAlign = 'center'
body.appendChild(image)
image.addEventListener('load', function () {
iframe.contentWindow.print()
})
})
iframe.contentWindow.addEventListener('afterprint', function () {
// 通过父级页面删除 iframe 自己
iframe.parentNode.removeChild(iframe)
})
},
2.生成网页
doPrint() {
var head_str = '<html><head><title></title></head><body>' //先生成头部
var foot_str = '</body></html>' //生成尾部
var older = document.body.innerHTML
var new_str = document.getElementById('wrapper').innerHTML //获取指定打印区域
// var new_str = document.getElementsByClassName('blog-content-box')[0].innerHTML;//获取指定打印区域
var old_str = document.body.innerHTML //获得原本页面的代码
document.body.innerHTML = head_str + new_str + foot_str //构建新网页
window.print() //打印刚才新建的网页
document.body.innerHTML = older //将网页还原
return false
},
3.插件
https://blog.csdn.net/weixin_45224906/article/details/127762534
37.在线生成二维码的api
支持 https 的二维码 api 服务并不多,这里是经过精心筛选后剩下的结果。推荐日常使用搜狐视频提供的二维码 api 接口,稳定性很好,站长已使用多年。
使用方法很简单,替换 https://www.abc.com 为想要生成的文字或链接即可。
搜狐视频提供的二维码 api,已稳定五年以上。
https://my.tv.sohu.com/user/a/wvideo/getQRCode.do?text= https://www.abc.com
搜狐快站提供的二维码 api,已稳定两年以上。
https://www.kuaizhan.com/common/encode-png?large=true&data= https://www.abc.com
qrserver 提供的二维码 api,国外服务,已稳定五年以上。
https://api.qrserver.com/v1/create-qr-code/?size=150×150&data= https://www.abc.com
qrcoder 提供的二维码 api,国外服务,已稳定五年以上。
https://www.qrcoder.co.uk/api/v1/?text= https://www.abc.com
p= 二维码尺寸,可选范围 1-40 已稳定有两年左右。
https://api.isoyu.com/qr/?m=0&e=L&p=10&url= https://www.abc.com
38.判断两个范围是否存在交集
a<end&&b>start
39.文件流创建本地url
url = window.URL.createObjectURL(blob)
40.下载文件流
import request from "./request";
export default function(config) {
return request({
...config,
responseType: "blob",
}).then(({ data, headers }) => {
let filename = "";
if (config.name) {
//机构端批量导入下载
filename = config.name;
} else {
if (headers["content-disposition"]) {
filename = headers["content-disposition"].match(
/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
)[1];
filename = decodeURIComponent(filename);
} else {
filename = decodeURIComponent(headers.attachment);
}
}
downloadFile(filename, data);
});
}
function downloadFile(fileName, content) {
const blob = new Blob([content]);
if ("msSaveOrOpenBlob" in window.navigator) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
const a = document.createElement("a");
a.download = fileName;
a.href = URL.createObjectURL(blob);
a.click();
URL.revokeObjectURL(blob);
}
}
42.导出数组表格数据为excel 文件
https://github.com/SheetJS/js-xlsx (工具)
https://zhuanlan.zhihu.com/p/215713923
yarn add xlsx
import * as XLSX from 'xlsx';
exportExcel(){
// 构造数据
let _data = [
['id', 'name', 'value'], //表头
[1, 'sheetjs', 7262], // 每行数据
[2, 'js-xlsx', 6969], // 每行数据
];
let ws = XLSX.utils.aoa_to_sheet(_data); // 加到sheet 里面
ws['!cols'] = [{ wch: 60 }, { wch: 20 }]; // 设置每列宽度
/* generate workbook and add the worksheet */
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Sheet1'); // sheet 加到文件里面
let wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'binary' }); //转换为二进制
// 定义 应该二进制转 blob 的方法
const s2ab = (s: any) => {
let buf = new ArrayBuffer(s.length); //convert s to arrayBuffer
let view = new Uint8Array(buf); //create uint8array as viewer
for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xff; //convert to octet
return buf;
};
// 创建一个a 标签 好下载
const fileURL = window.URL.createObjectURL(
new Blob([s2ab(wbout)], { type: 'application/octet-stream' }),
);
const fileLink = document.createElement('a');
fileLink.href = fileURL;
fileLink.setAttribute('download', `test.xlsx`);
document.body.appendChild(fileLink);
fileLink.click();
}