首页
留言
友链
关于
Search
1
思源笔记docker私有化部署及使用体验分享
2,879 阅读
2
windows11 远程提示:为安全考虑,已锁定该用户帐户,原因是登录尝试或密码更改尝试过多。
1,230 阅读
3
解决 nginxProxyManager 申请证书时的SSL失败问题
882 阅读
4
Pointer-Focus:一款功能强大的教学、录屏辅助软件
857 阅读
5
使用cspell对项目做拼写规范检查
720 阅读
Web前端
CSS
JavaScript
交互
Vue
小程序
后端
Java
运维
项目
生活
其他
转载
软件
职场
登录
Search
标签搜索
docker
DevOps
magic-boot
Linux
酷壳
frp
RabbitMQ
gitlab
Node
git
工具
MybatisPlus
clickhouse
Syncthing
规范
前端
产品
nginx
markdown
axios
朱治龙
累计撰写
153
篇文章
累计收到
10
条评论
首页
栏目
Web前端
CSS
JavaScript
交互
Vue
小程序
后端
Java
运维
项目
生活
其他
转载
软件
职场
页面
留言
友链
关于
搜索到
16
篇与
JavaScript
的结果
2023-09-03
超全的正则表达式速查手册
一、校验数字的表达式数字:^[0-9]*$n位的数字:^\d{n}$至少n位的数字:^\d{n,}$m-n位的数字:^\d{m,n}$零和非零开头的数字:^(0|[1-9][0-9]*)$非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$带1-2位小数的正数或负数:^(-)?\d+(.\d{1,2})?$正数、负数、和小数:^(-|+)?\d+(.\d+)?$有两位小数的正实数:^[0-9]+(.[0-9]{2})?$有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$非零的正整数:^[1-9]\d$ 或 ^([1-9][0-9]){1,3}$或 ^\+?[1-9][0-9]*$非零的负整数:^-[1-9][]0-9″$ 或 ^-[1-9]\d$ 非负整数:^\d+$ 或 ^[1-9]\d*|0$非正整数:^-[1-9]\d*|0$或 ^((-\d+)|(0+))$非负浮点数:^\d+(.\d+)?$或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$非正浮点数:^((-\d+(.\d+)?)|(0+(.0+)?))$或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$正浮点数:^[1-9]\d.\d|0.\d[1-9]\d$或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$负浮点数:^-([1-9]\d.\d|0.\d[1-9]\d)$或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$浮点数:^(-?\d+)(.\d+)?$或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$二、校验字符的表达式汉字:^[\u4e00-\u9fa5]{0,}$英文和数字:^[A-Za-z0-9]+$或 ^[A-Za-z0-9]{4,40}$长度为3-20的所有字符:^.{3,20}$由26个英文字母组成的字符串:^[A-Za-z]+$由26个大写英文字母组成的字符串:^[A-Z]+$由26个小写英文字母组成的字符串:^[a-z]+$由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$可以输入含有^%&’,;=?$” 等字符:[^%&’,;=?$\x22]+禁止输入含有~的字符: [^~\x22]+其它.匹配除 n 以外的任何字符。/[\u4E00-\u9FA5]/ 汉字/[\uFF00-\uFFFF]/ 全角符号/[\u0000-\u00FF]/ 半角符号三、特殊需求表达式Email 地址:1^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)$1域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?InternetURL:[a-zA-z]+://[^\s] 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=])?$手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$电话号码(“XXX-XXXXXXX”、”XXXX-XXXXXXXX”、”XXX-XXXXXXX”、”XXX-XXXXXXXX”、”XXXXXXX”和”XXXXXXXX):^((\d{3,4}-)|\d{3.4}-)?\d{7,8}$国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}身份证号(15位、18位数字):^\d{15}|\d{18}$短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.\d)(?=.[a-z])(?=.*[A-Z]).{8,10}$日期格式:^\d{4}-\d{1,2}-\d{1,2}一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$钱的输入格式有四种钱的表示形式我们可以接受:”10000.00” 和 “10,000.00”, 和没有 “分” 的 “10000” 和 “10,000”:^[1-9][0-9]*$这表示任意一个不以0开头的数字,但是,这也意味着一个字符”0”不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$必须说明的是,小数点后面至少应该有1位数,所以”10.”是不通过的,但是 “10” 和 “10.2” 是通过的:^[0-9]+(.[0-9]{2})?$这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$这样就允许用户只写一位小数.下面我们该考虑数字中的 逗号 了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$备注:这就是最终结果了,别忘了+可以用*替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+.[x|X][m|M][l|L]$中文字符的正则表达式:[\u4e00-\u9fa5]双字节字符:^\x00-\xff)空白行的正则表达式:\n\s*\r (可以用来删除空白行)HTML标记的正则表达式:<(\S?)[^>]>.?</\1>|<.? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)首尾空白字符的正则表达式:^\s|\s (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)腾讯QQ号:[1-9][0-9]{4,}(腾讯QQ号从10000开始)中国邮政编码:[1-9]\d{5}(?!\d)(中国邮政编码为6位数字)IP地址:\d+.\d+.\d+.\d+(提取IP地址时有用)IP地址:((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d).){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))IP-v4地址:\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b (提取IP地址时有用)校验IP-v6地址: (([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))子网掩码:((?:(?:25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d?\d))校验日期:^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$(“yyyy-mm-dd“ 格式的日期校验,已考虑平闰年。)抽取注释:<!–(.*?)–>查找CSS属性:^\s[a-zA-Z\-]+\s[:]{1}\s[a-zA-Z0-9\s.#]+[;]{1}提取页面超链接:(<a\s(?!.\brel=)[^>])(href=”https?:\/\/)((?!(?:(?:www\.)?’.implode(‘|(?:www\.)?’, $follow_list).’))[^” rel=”external nofollow” ]+)”((?!.\brel=)[^>])(?:[^>])>提取网页图片:\< [img][^\\>][src] = [\”\’]{0,1}([^\”\’\ >]*)提取网页颜色代码:^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$文件扩展名效验:^([a-zA-Z]\:|\\)\\([^\\]+\\)[^\/:?”<>|]+\.txt(l)?$判断IE版本:^.MSIE 5-8?(?!.Trident\/[5-9]\.0).*$
2023年09月03日
21 阅读
0 评论
0 点赞
2023-03-14
用*输出一个等腰三角形
缘起工作间隙,得到一个刚学编程时的题目,题目如下:用输出一个等腰三角形。提示用户输入一个整数 ,代表输出的等边三角形由n行组成。例:输入n=5。输出: * *** ***** ******* *********朋友提供的代码如下:public static void main(String[] args) { System.out.println("请输入需要的行数(不可输入0):"); Scanner scanner = new Scanner(System.in); int rows = scanner.nextInt(); int rowIndex = rows - 1; //最后一行总个数 int total = 1 + (rows - 1) * 2; // 每行*个数 int num = 1; // 打印标记 boolean flag = false; if (rows != 0) { for (int i = 0; i < rows; i++) { for (int j = 0, k = 0; j < total; j++) { // 倒数第几行就从第几个下标开始 if (j == rowIndex - i) { flag = true; } if (flag && k < num) { System.out.print("*"); // 开始打印计数 k++; } else { System.out.print(" "); // 结束打印 flag = false; } } num = num + 2; System.out.println(); } } }整段代码看下来实现方式有些复杂,感觉有些陷在循环里了,脑袋里不跟着一行一行的跑代码的话,不怎么能想出来结果输出是怎样的。于是,我从头开始梳理了下需要输出的东西,主要是要从中找寻规律:第一步:手工输出前几个结果,从结果中找规律2: * *** 3: * *** ***** 4: * *** ***** ******* 5: * *** ***** ******* ********* 6: * *** ***** ******* ********* *********** 7: * *** ***** ******* ********* *********** *************第二步:分析得出如下规律:令 行数为1开始,当前行的输出情况如下:星号数量 = 当前行数 * 2 - 1左侧空格数量 = 总行数 - 当前行数得出规律后,就能够很好的着手后面的代码工作了,以下是我提供的代码:JS版:/** * 输出三角形 * @param rows 三角形行数 */ let showTriangle = (rows) => { console.log(`输出${rows}等腰三角形:`) // 如果小于2的话不能构成等腰三角形 if (rows < 2) { console.error('必须大于1') return; } for (let row = 1; row <= rows; row++) { // 输出空格 let rowStr = '' for (let blankIndex = 0; blankIndex < rows - row; blankIndex++) { rowStr += ' ' } // 输出星号 for (let starIndex = 0; starIndex < row * 2 - 1; starIndex++) { rowStr += '*' } console.log(rowStr) } } // 测试输出: for (let i = 0; i < 15; i++) { showTriangle(i) }输出结果:Java 版由于使用了 String.repeat(), JDK 版本应 >= 11,若 JDK 低于 11 也可以用循环实现。import java.util.InputMismatchException; import java.util.Scanner; /** * @author zhuzl */ public class ShowTriangle { public static void main(String[] args) { System.out.println("请输入三角形的行数(需大于1):"); Scanner scanner = new Scanner(System.in); int rows = scanner.nextInt(); printTriangle(rows); } /** * 打印等腰三角形 * * @param rows 三角形行数 */ private static void printTriangle(int rows) { if (rows < 2) { throw new InputMismatchException("必须大于1"); } for (int row = 1; row <= rows; row++) { System.out.println(" ".repeat(rows - row) + "*".repeat(row * 2 - 1)); } } }输出结果如下:
2023年03月14日
20 阅读
0 评论
0 点赞
2022-10-14
20 个 JS 工具函数助力高效开发
前言日常开发中,面对各种不同的需求,我们经常会用到以前开发过的一些工具函数,把这些工具函数收集起来,将大大提高我们的开发效率。1、校验数据类型export const typeOf = function(obj) { return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase() } // Example typeOf('zhuzl') // string typeOf([]) // array typeOf(new Date()) // date typeOf(null) // null typeOf(true) // boolean typeOf(() => { }) // function 2、防抖export const debounce = (() => { let timer = null return (callback, wait = 800) => { timer&&clearTimeout(timer) timer = setTimeout(callback, wait) } })() // Example // 如 vue 中使用 methods: { loadList() { debounce(() => { console.log('加载数据') }, 500) } }3、节流export const throttle = (() => { let last = 0 return (callback, wait = 800) => { let now = +new Date() if (now - last > wait) { callback() last = now } } })() 4、手机号脱敏export const hideMobile = (mobile) => { return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2") }5、开启全屏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() } }6、关闭全屏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() } }7、大小写转换/** * 大小写转换 * @param str 待转换的字符串 * @param type 1-全大写 2-全小写 3-首字母大写 * @returns */ export const turnCase = (str, type) => { switch (type) { case 1: return str.toUpperCase() case 2: return str.toLowerCase() case 3: return str[0].toUpperCase() + str.substring(1).toLowerCase() default: return str } } // Example turnCase('vue', 1) // VUE turnCase('REACT', 2) // react turnCase('vue', 3) // Vue8、解析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 } // Example // 假设目前位于 https://****com/index?id=154513&age=18; getSearchParams(); // {id: "154513", age: "18"}9、判断手机是Andoird还是IOS/** * 1: ios * 2: android * 3: 其它 */ 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; }10、数组对象根据字段去重/** * 数组对象根据某字段去重 * @param arr 要去重的数组 * @param key 根据去重的字段名 * @returns */ 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 } // Example const responseList = [ { id: 1, name: '树哥' }, { id: 2, name: '黄老爷' }, { id: 3, name: '张麻子' }, { id: 1, name: '黄老爷' }, { id: 2, name: '张麻子' }, { id: 3, name: '树哥' }, { id: 1, name: '树哥' }, { id: 2, name: '黄老爷' }, { id: 3, name: '张麻子' }, ] uniqueArrayObject(responseList, 'id') // [{ id: 1, name: '树哥' },{ id: 2, name: '黄老爷' },{ id: 3, name: '张麻子' }] 11、滚动到页面顶部export const scrollToTop = () => { const height = document.documentElement.scrollTop || document.body.scrollTop; if (height > 0) { window.requestAnimationFrame(scrollToTop); window.scrollTo(0, height - height / 8); } }12、滚动到指定元素位置export const smoothScroll = element =>{ document.querySelector(element).scrollIntoView({ behavior: 'smooth' }) } // Example smoothScroll('#target') // 平滑滚动到 ID 为 target 的元素13、uuidexport 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) } // Example uuid() // d9ffece8-0a9d-4ca2-bbab-0af7bc0f71df14、金额格式化/** * 金额格式化 * @param number 要格式化的数字 * @param decimals 保留几位小数 * @param dec_point 小数点符号 * @param thousands_sep 千分位符号 * @returns */ 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) } // Example moneyFormat(10000000) // 10,000,000.00 moneyFormat(10000000, 3, '.', '-') // 10-000-000.00015、存储操作class MyCache { constructor(isLocal = true) { this.storage = isLocal ? localStorage : sessionStorage } setItem(key, value) { if (typeof (value) === 'object') value = JSON.stringify(value) this.storage.setItem(key, value) } getItem(key) { try { return JSON.parse(this.storage.getItem(key)) } catch (err) { return this.storage.getItem(key) } } removeItem(key) { this.storage.removeItem(key) } clear() { this.storage.clear() } key(index) { return this.storage.key(index) } length() { return this.storage.length } } const localCache = new MyCache() const sessionCache = new MyCache(false) export { localCache, sessionCache } // Example localCache.getItem('user') sessionCache.setItem('name','树哥') sessionCache.getItem('token') localCache.clear() 16、下载文件/** * 下载文件 * @param api API接口路径 * @param params 请求参数 * @param fileName 文件名 * @param type HTTP请求方式,默认为get */ 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); }) } // Example downloadFile('/api/resources/download', {id}, '文件名')17、时间操作关于时间操作,没必要自己再写一大串代码了,强烈推荐使用 [Day.js](https://dayjs.gitee.io/zh-CN/) > Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样。 > 如果您曾经用过 Moment.js, 那么您已经知道如何使用 Day.js 。18、深拷贝此方法存在一定局限性:一些特殊情况没有处理: 例如Buffer对象、Promise、Set、Map。如果确实想要完备的深拷贝,推荐使用 lodash 中的 cloneDeep 方法。export const clone = parent => { // 判断类型 const isType = (obj, type) => { if (typeof obj !== "object") return false const typeString = Object.prototype.toString.call(obj) let flag; switch (type) { case "Array": flag = typeString === "[object Array]" break; case "Date": flag = typeString === "[object Date]" break; case "RegExp": flag = typeString === "[object RegExp]" break; default: flag = false } return flag; }; // 处理正则 const getRegExp = re => { var flags = "" if (re.global) flags += "g" if (re.ignoreCase) flags += "i" if (re.multiline) flags += "m" return flags; }; // 维护两个储存循环引用的数组 const parents = [] const children = [] const _clone = parent => { if (parent === null) return null if (typeof parent !== "object") return parent let child, proto if (isType(parent, "Array")) { // 对数组做特殊处理 child = [] } else if (isType(parent, "RegExp")) { // 对正则对象做特殊处理 child = new RegExp(parent.source, getRegExp(parent)) if (parent.lastIndex) child.lastIndex = parent.lastIndex } else if (isType(parent, "Date")) { // 对Date对象做特殊处理 child = new Date(parent.getTime()) } else { // 处理对象原型 proto = Object.getPrototypeOf(parent) // 利用Object.create切断原型链 child = Object.create(proto) } // 处理循环引用 const index = parents.indexOf(parent) if (index != -1) { // 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象 return children[index] } parents.push(parent) children.push(child) for (let i in parent) { // 递归 child[i] = _clone(parent[i]) } return child; } return _clone(parent) }19、模糊搜索/** * 数组对象模糊搜索 * @param list 原数组对象 * @param keyWord 查询的关键词 * @param attribute 数组需要检索的对象属性,默认问name * @returns */ 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 } // Example const list = [ { id: 1, name: '树哥' }, { id: 2, name: '黄老爷' }, { id: 3, name: '张麻子' }, { id: 4, name: '汤师爷' }, { id: 5, name: '胡万' }, { id: 6, name: '花姐' }, { id: 7, name: '小梅' } ] fuzzyQuery(list, '树', 'name') // [{id: 1, name: '树哥'}]20、遍历树节点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) } } } // Example const treeData = [{ id: 1, label: '一级 1', children: [{ id: 4, label: '二级 1-1', children: [{ id: 9, label: '三级 1-1-1' }, { id: 10, label: '三级 1-1-2' }] }] }, { id: 2, label: '一级 2', children: [{ id: 5, label: '二级 2-1' }, { id: 6, label: '二级 2-2' }] }, { id: 3, label: '一级 3', children: [{ id: 7, label: '二级 3-1' }, { id: 8, label: '二级 3-2' }] }], // 假设我们要从树状结构数据中查找 id 为 9 的节点 let result foreachTree(data, (item) => { if (item.id === 9) { result = item } }) console.log('result', result) // {id: 9,label: "三级 1-1-1"} 关于本文作者:呛再首https://juejin.cn/post/7132714583399071758
2022年10月14日
28 阅读
0 评论
0 点赞
2022-08-31
正则表达式-元字符
特殊单字符空白符范围量词
2022年08月31日
31 阅读
0 评论
0 点赞
2022-06-15
前端导出Excel项目实践
背景介绍近期项目有个「导出作业详情」的需求,之前接触大部分导出需求均为后端获取数据生成Excel文件或将Excel文件的下载地址或文件流提供给前端进行下载。在本需求中对下载的文件有如下需求点:命名:用户名作业详单-起止日期格式:xlsx,下载内容CPU与GPU分别显示在两个工作表里技术调研在以前的项目中有=接触过前端解析 Excel 导入数据的功能,使用的是 [xlsx](https://www.npmjs.com/package/xlsx),功能超级强大,便先了解一下这个库是否能满足咱们的需求,经过稍加深入地调研,了解到这个库仅侧重在 Excel 解析,我们的需求是要能根据后端提供的数据动态生成 Excel 文件,可能这个库就不适应了。经过经一步调研,发现一款采用MIT开源授权, Star 数近 10k 的开源库:exceljs,看描述为:读取,操作并写入电子表格数据和样式到 XLSX 和 JSON 文件。这不就正是我所需要的嘛,接下来的问题就是验证工作了,根据我们的需求,主要需要验证的有如下事项:1、是否可支持生成多工作表的Excel文件2、是否支持自定义单元格样式:如背景色、边框、对齐方式、格式化3、测试Excel文件生成效率经过对相关API的深入了解及验证,发现完全能满足我们的需求。其中生成效率,一次性生成40000多条数据,不到15秒即可完成Excel生成工作,也能满足我们的实际要求:技术点梳理详细见代码及相关注释。仅用于说明相关功能点,非完整代码。// 引入 exceljs 依赖 import ExcelJS from 'exceljs' // 创建工作簿 const workbook = new ExcelJS.Workbook() // 在工作簿中添加两个工作表 const cpuSheet = workbook.addWorksheet('CPU作业详单') const gpuSheet = workbook.addWorksheet('GPU作业详单') // 添加表头 const cpuHeaderRow = cpuSheet.addRow(['作业ID', '作业名称', '超算中心', '超算账号', '队列', '运行时长', '核数', '消费核时', '消费金额', '提交时间', '开始时间', '结束时间', '提交账号', '付费账号']) // 设置表头样式 // 行高 cpuHeaderRow.height = 26.5 // 字体 cpuHeaderRow.font = { bold: true } // 设置表头单元格样式 cpuHeaderRow.eachCell((cell, rowNumber) => { // 设置边框 cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } } // 填充背景 cell.fill = { type: 'pattern', pattern: 'darkTrellis', fgColor: { argb: 'F1F1F1FF' }, bgColor: { argb: 'F1F1F1FF' } } // 对齐方式 cell.alignment = { vertical: 'middle', horizontal: cpuLeftAlignCols.includes(rowNumber) ? 'left' : 'center', wrapText: true } }) // 根据内容适当调整部分列宽度 cpuSheet.getColumn(2).width = 30 // 作业名称 cpuSheet.getColumn(10).width = 20 // 提交时间 cpuSheet.getColumn(11).width = 20 // 开始时间 cpuSheet.getColumn(12).width = 20 // 结束时间 cpuSheet.getColumn(13).width = 15 // 提交账号 cpuSheet.getColumn(14).width = 15 // 付费账号 // 生成相关数据行 for (let i = 0; i < cpuJobList.length; i++) { const job = cpuJobList[i] const rowData = [job.jobId, job.jobName, job.cluster, job.user, job.partition, ...] const dataRow = cpuSheet.addRow(rowData) // 设置数据行样式 dataRow.height = 26.5 dataRow.eachCell((cell, rowNumber) => { // 设置单元格边框 cell.border = { top: { style: 'thin' }, left: { style: 'thin' }, bottom: { style: 'thin' }, right: { style: 'thin' } } // 设置对齐方式 cell.alignment = { vertical: 'middle', horizontal: cpuLeftAlignCols.includes(rowNumber) ? 'left' : 'center', wrapText: true } // 金额格式化 if (rowNumber === 10) { cell.numFmt = '"¥"#,##.##' } }) } // 保存Excel文件 const buf = await workbook.xlsx.writeBuffer() const fileName = `${userForFileName}作业详单_${dayjs(startDate).format('YYYYMMDD')}-${dayjs(endDate).format('YYYYMMDD')}.xlsx` // 基于 file-saver 实现保存文件到本地 saveAs(new Blob([buf]), fileName)最终整合到应用中的效果扩展打印相关// 设置页面方向 workSheet.pageSetup.orientation = 'portrait' // portrait || landscape // 设置页边距 workSheet.pageSetup.margins = { left: 0.3, right: 0.3, top: 0.2, bottom: 0.2, header: 0.2, footer: 0.2 } workSheet.pageSetup.horizontalCentered = true workSheet.pageSetup.verticalCentered = false // 每页均显示的行 workSheet.pageSetup.printTitlesRow = '1:3' // 指定打印哪些列 workSheet.pageSetup.printTitlesColumn = 'A:G'解析 Excelconst ExcelJS = require('exceljs') const excelfile = './test.xlsx' var workbook = new ExcelJS.Workbook() workbook.xlsx.readFile(excelfile).then(function() { // 获取第一个worksheet var worksheet = workbook.getWorksheet(1) // 编辑worksheet worksheet.eachRow(function(row, rowNumber) { var rowSize = row.cellCount var numValues = row.actualCellCount console.log('单元格数量/实际数量:' + rowSize+'/' + numValues) row.eachCell(function(cell, colNumber) { // cell.type单元格类型:6-公式 ;2-数值;3-字符串 let cellValue = cell.value if(cell.type === 6) { cellValue = cell.result } console.log('cell',rowNumber, colNumber, cellValue, cell.numFmt) }) }) })数值格式化问题前期为满足跟前端列表展现业务逻辑保持一致,金额及数值格式化均使用 Intl.NumberFormat进行格式化,但是该类格式化后的数据是字符串,不便于导出后对数据进行排序及数值比较等操作。由于官方文档的单元格格式化说明信息较简单,Excel里对数值格式化的方式较丰富,如下图:为完整的复原格式化后的内容,可以先在Excel文件中对相关单元格设置好格式后,使用上面的 解析 Excel 章节的代码解析获取单元格的 numFmt数据。下面列举本工程中用到的数值格式化 numFmt 值:数值格式化:#,##0.00_ 货币格式化:"¥"#,##0.00;"¥"-#,##0.00
2022年06月15日
115 阅读
0 评论
0 点赞
2022-06-15
前端库推荐:JSZip
jszip是一个用于创建、读取和编辑.zip文件的JavaScript库,且API的使用也很简单链接官网:https://stuk.github.io/jszip/Github:https://hub.fastgit.xyz/Stuk/jszip示例var zip = new JSZip(); zip.file("Hello.txt", "Hello World\n"); var img = zip.folder("images"); img.file("smile.gif", imgData, {base64: true}); zip.generateAsync({type:"blob"}) .then(function(content) { // see FileSaver.js saveAs(content, "example.zip"); });兼容性OperaFirefoxSafariChromeInternet ExplorerNode.jsYesYesYesYesYesYesTested with the latest versionTested with 3.0 / 3.6 / latest versionTested with the latest versionTested with the latest versionTested with IE 6 / 7 / 8 / 9 / 10Tested with node.js 0.10 / latest version
2022年06月15日
18 阅读
0 评论
0 点赞
2022-04-13
Javascript设计模式-学习笔记
设计模式概念解读设计模式(Design pattern)是一套被反复使用、思想成熟、经过分类和无数 实战设计经验 的总结的。使用设计模式是为了让系统代码可重用、可扩展、可解耦、更容易被人理解且能保证代码可靠性。设计模式使代码开发真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。只有夯实地基搭好结构,才能盖好坚实的大楼。也是我们迈向高级开发人员必经的一步。单例模式文字解读单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。在JavaScript里,单例作为一个命名空间提供者,从全局命名空间里提供一个唯一的访问点来访问该对象。构造函数模式文字解读构造函数用于创建特定类型的对象——不仅声明了使用的对象,构造函数还可以接受参数以便第一次创建对象的时候设置对象的成员值。你可以自定义自己的构造函数,然后在里面声明自定义类型对象的属性或方法。在JavaScript里,构造函数通常是认为用来实现实例的,JavaScript没有类的概念,但是有特殊的构造函数。通过new关键字来调用自定义的构造函数,在构造函数内部,this关键字引用的是新创建的对象。拟物化解读代码示例一://1.用于创建特定类型的对象 //2.这样的函数名会被人笑话的 //3.js开发的时候写单引号 //4.js里构造函数比较特殊的地方new //5.其他的语言里比如PHP里人家实现有一个关键字A class //6.zaomen就是构造函数他又充当了类的概念 function zaomen (suo, huawen) { if (!(this instanceof zaomen)) { return new zaomen(...arguments); } this.suo = suo || '普通' this.huawen = huawen || '普通' this.create = function(){ return ' [锁头] '+this.suo +' [ 花纹] '+ this.huawen } } var xiaozhang = new zaomen('指纹锁', '高贵') alert('xiaozhang' + xiaozhang.create()) var xiaoli = zaomen('密码锁', '雕花') alert('xiaoli' + xiaoli.create()) 示例二:结合单例模式var Panpan = { zaomen: function (suo, huawen) { this.suo = suo || '普通' this.huawen = huawen || '普通' this.create = function(){ return ' 盼盼造门:[锁头] '+this.suo +' [ 花纹] '+ this.huawen } } } var Tubaobao = { zaomen: function (suo, huawen) { this.suo = suo || '普通' this.huawen = huawen || '普通' this.create = function(){ return ' 兔宝宝造门:[锁头] '+this.suo +' [ 花纹] '+ this.huawen } } } var xiaozhang = new Panpan.zaomen('指纹锁', '高贵') alert('xiaozhang' + xiaozhang.create()) var xiaoli = new Tubaobao.zaomen('密码锁', '雕花') alert('xiaoli' + xiaoli.create()) 工厂模式概念解读工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型(抽象工厂)。这个模式十分有用,尤其是创建对象的流程赋值的时候,比如依赖于很多设置文件等。并且,你会经常在程序里看到工厂方法,用于让子类类定义需要创建的对象类型。使用场景1.对象的构建十分复杂。2.需要依赖具体的环境创建不同实例。3.处理大量具有相同属性的小对象。注意事项:1.不能滥用工厂,有时候仅仅是给代码增加复杂度。代码实战简单工厂//这是一个简单工厂模式 var XMLHttpFactory = function() { } XMLHttpFactory.createXMLHttp = function() { var XMLHttp = null //XMLHttpFactory.createXMLHttp()这个方法根据当前环境的具体情况返回一个XHR对象。 if (window.XMLHttpReqyest){ XMLHttp = new XMLHttpRequest() } else if(window.ActiveXObject) { XMLHttp = new ActiveXOb ject("Microsof+.XMLHTTP") return XMLHttp } } var AjaxHander = function() { var XMLHttp = XMLHttpFactory.createXMLHttp(); /...具体的操作..*/ }抽象工厂//这是一个抽象工厂模式 var XMLHttpFactory = function() { } XMLHttpFactory.prototype = { //如果真的要调用这个无法会抛出一个错误,它不能被实例化,只能用来派生子类 createFactory: function(){ throw new Error('This is an abstract class') } } //派生子类,文章开始处有基础介绍那有讲解继承的模式,不明白可以去参考原理 var XHRHandler = function() { XMLHttpFactory.call(this) } XHRHandler.prototype = new XMLHttpFactory() XHRHandler.prototype.constructor = XHRHandler //重新定义createFactory方法 XHRHandler.prototype.createFactory = function() { var XMLHttp = null if (window.XMLHttpRequest){ XMLHttp = new XMLHttpRequest() }else if(window.ActiveXObject){ XMLHttp = new ActiveXObject('Microsoft.XMLHTTP') } return XMLHttp } 代理模式概念解读代理,顾名思义就是帮助别人做事,GoF对代理模式的定义如下:代理模式(Proxy) ,为其他对象提供一种代理以控制对这个对象的访问。代理模式使得代理对象控制具体对象的引用。代理几乎可以是任何对象:文件,资源,内存中的对象,或者是一-些难以复制的东西。使用场景1.远程代理(一个对象将不同空间的对象进行局部代理)。2.虚拟代理(根据需要创建开销很大的对象如渲染网页暂时用占位代替真图)。3.安全代理(控制真实对象的访问权限)。4.智能指引(调用对象代理处理另外一些事情如垃圾回收机制)。代码实战//代理模式需要3方 //1.买家 function maijiq(argument){ this.name = '小明' } //2.中介卖房 function zhongjie(){ } zhongjie.prototype.maifang = function() { new fangdong(new maijia().maifang('20万') } //3.房东坐等收钱 function fangdong(maijia){ this.mailjia_name = maijia.name this.maifang = function(money){ alert('收到了来自I ' + this.maijia_ name+' ]' + money+'人民币') } } (new zhongjie).maifang() AlloyStic HTML5骨骼动画建造者模式概念解读建造者模式可以将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。也就是说如果我们用了建造者模式,那么用户就需要指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。建造者模式实际,就是一个指挥者,-个建造者,一个使用指挥者调用具体建造者工作得出结果的客户。建造者模式主要用于“分步骤构建一个复杂的对象”,在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。适用场景1.分步创建一一个复杂的对象。2.解耦封装过程和具体创建的组件。3.无需关心组件如何组装。注意事项1.一定要一个稳定的算法进行支持。2.加工工艺是暴露的。代码实战// 1.产出的东西是房子 // 2.baogongtou调用工人进行开工而且他要很清除工人们具体的某一个大项 // 3.工人是盖房子的工人可以建卧室建客厅建厨房 // 4.包工头只是一个接口而已他不干活他只对外说我能盖房子 function Fangzi() { this.woshi = '' this.keting = '' this.chufang = '' } function Baogongtou() { this.gaifangzi = function(gongren1) { gongren1.jian_woshi() gongren1.jian_keting() gongren1.jian_chufang() } } function Gongren() { this.jian_woshi = function() { console.log('卧室盖好了') } this.jian_keting = function() { console.log('客厅建好了') } this.jian_chufang = function() { console.log('厨房建好了') } this.jiaogong = function() { var _fangzi = new Fangzi() _fangzi.woshi = 'ok' _fangzi.keting = 'ok' _fangzi.chufang = 'ok' return _fangzi } } var gongren = new Gongren() var baogongtou = new Baogongtou() baogongtou.gaifangzi(gongren) // 主人要房子 var myfangzi = gongren.jiaogong() console.log(myfangzi)命令模式概念解读命令模式(Command)的定义是:用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行。也就是说该模式旨在将函数的调用、请求和操作封装成一个单一的对象,然后对这个对象进行一系列的处理。它也可以用来消除调用操作的对象和实现操作的对象之间的耦合。这为各种具体的类的更换带来了极大的灵活性。模式作用1.将函数的封装、请求、调用结合为一体。2.调用具体的函数解耦命令对象与接收对象。3.提高程序模块化的灵活性。注意事项1.不需要接口一致,直接调用函数即可,以免造成浪费。代码实战var lian = {} lian.paobing = function(pao_num) { console.log(pao_num + '门炮开始战斗') } lian.bubing = function(bubing_num) { console.log(bubing_num + '个人开始战斗') } lian.lianzhang = function(mingling) { lian[mingling.type](mingling.num) } lian.lianzhang({ type: 'paobing', num: 100 }) lian.lianzhang({ type: 'bubing', num: 500 })观察者模式 / 发布订阅模式概念解读观察者模式又叫发布订阅模式(Publish/Subscribe) ,它定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。模式作用1.支持简单的广播通信,自动通知所有已经订阅过的对象。2.页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。3.目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。注意事项:1.监听要在触发之前。代码实战// 提前引入jQuery ~(function() { var $ = {} var o = $({}) $.jianting = function() { o.on.apply(a.arguments) } $.fabu = function() { o.trigger.apply(arguments) } $.qingchu = function() { o.off.apply(o, arguments) } })() $.jianting('/test/ls', function(e, a, b, c) { console.log(a + '||' + b + '||' + c) }) $.jianting('/test/ls', function(a, b, c) { console.log('ok') }) $.fabu('/test/ls', [1, 2, 3])适配器模式概念解读适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转换成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一起工作。模式作用1.使用一个已经存在的对象,但其方法或接口不符合你的要求。2.创建一个可复用的对象,该对象可以与其他不相关或不可见的对象协同工作。3.使用已经存在的一个或多个对象,但是不能进行继承已匹配它的接口。注意事项1.与代理模式的区别,代理模式是不改变原接口适配是原接口不符合规范。代码实战function pp() { this.test = function() { console.log('我是原test') } } pp.prototype.gogo = function() { console.log('我是原go') } function shipeiqi() { var s = new pp var aa = { test: function () { s.test() }, go: function() { s.gogo() } } return aa } var bb = shipeiqi() bb.test() bb.go() 责任链模式概念解读职责链模式是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理他为止。链中收到请求的对象要么亲自处理它,要么转发给下一个候选者。提交方并不明确有多少个对象会处理它,任一候选者都可以响应相应的请求,可以在运行时刻决定哪些候选者参与到链中。模式作用1.dom的冒泡有些类似职责链。2.nodejs 当controller中有很多负责操作逻辑的时候拆分中间件。3.解耦发送者和接受者。注意事项1.javascript中的每一次 「.」 是有代价的,要在必要的时候应用。代码实战// 职责链模式 function laoban(xiangmujingli) { if (xiangmujingli) { this.xiangmujingli = xiangmujingli } } laoban.prototype.write = function(code) { this.xiangmujingli.write(code) } function xiangmujingli(coder) { if (coder) { this.coder = coder } } xiangmujingli.prototype.write = function(code) { this.coder.write(code) } function coder() { } coder.prototype.write = function(code) { console.log('coding:' + code) } // 由begin发起,coder结束 var begin = new laoban(new xiangmujingli(new coder())) begin.write('php')迭代器模式概念解读迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示。jquery中我们经常会用到一个each函数就是迭代器模式。模式作用1.为遍历不同的集合结构提供一个统一 的接口, 从而支持同样的算法在不同的集合结构上进行操作。2.对于集合内部结果常常变化各异,我们不想暴露其内部结构的话,但又响让客户代码透明底访问其中的元素,这种情况下我们可以使用迭代器模式。注意事项1.一般的迭代,我们至少要有2个方法,hasNext()和Next(), 这样才做做到遍历所有对象。2.遍历的同时更改迭代器所在的集合结构可能会导致问题(比如C#的foreach里不允许修改item)。代码实战var arr = ['1', '2', '3', 'test', 20080708] var diedai = (function() { var length = arr.length var index = 0 return { hasNext: function() { return index < length }, next: function() { var data = arr[index] index = index + 1 return data }, reset: function() { index = 0 } } })() while(diedai.hasNext()) { console.log(diedai.next()) }外观模式概念解读外观模式(Facade) 为子系统中的一组接口提供了一个一致的界面,此模块定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式不仅简化类中的接口,而且对接口与调用者也进行了解耦。外观模式经常被认为开发者必备,它可以将一些复杂操作封装起来,并创建一个简单的接口用于调用。模式作用1.在设计初期,应该要有意识地将不同的两个层分离,比如经典的三层结构。2.在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,增加外观模式可以提供一个简单的接口,减少他们之间的依赖。3.在维护一个遗留的大型系统时,为系统开发一个外观Facade类,为设计粗糙和高度复杂的遗留代码提供比较清晰的接口,让新系统和Facade对象交互。注意事项外观模式被开发者连续使用时会产生一定的性能问题,因为在每次调用时都要检测功能的可用性。代码实战var stopEvent = function(e){ // 同时阻止时间默认行为和冒泡 e.stopPropagation() e.preventDefault() } // stopEvent 本身就是生产门面 $('#xxxx').click(function(e){ stopEvent(e) }) 策略模式概念解读策略模式定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化不会影响到使用算法的客户。模式作用1.所有的这些算法都是做相同的事情,只是实现不同。2.以相同的方式调用所有的方法,减少了各种算法类与使用算法类之间的耦合。3.单独定义算法类,也方便了单元测试。注意事项1.不仅可以封装算法,也可以用来封装几乎任何类型的规则,是要在分析过程中需要在不同时间应用不同的业务规则,就可以考虑是要策略模式来处理各种变化。代码实战var $input = $('#input') var util = { isEmpty: function() { return false }, isTel: function() { return true } } var isEmpty = util.isEmpty($input.val()) var isTel = util.isTel($input.val()) if (!isEmpty && isTel ) { console.log('通过校验') } // OR $.fn.validate $.input.vallidate({ isEmpty: false, isTel: true })中介者模式概念解读中介者模式(Mediator) ,用一个中介对象来封装一系列的对象交互。 中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。模式作用1.软件开发中,中介者是一个行为设计模式,通过提供一个统一的接口让系统的不同部分进行通信。一般,如果系统有很多子模块需要直接沟通,都要创建一个中央控制点让其各模块通过该中央控制点进行交互。中介者模式可以让这些子模块不需要直接沟通,而达到进行解耦的目的。注意事项1.当系统出现了多对多交互复杂的对象群时,先不要急于使用中介者模式,而是要思考一下是不是系统设计有问题。代码实战// 飞机 var feiji = function(name) { this.name = name } feiji.prototype.send = function(msg, to) { console.log(this.name + '发送了信息') tatai.send(msg, to) } feiji.prototype.jieshou = function(msg) { console.log(this.name + '接收到' + msg) } // 塔台 var tatai = { all: {}, zhuce: function(feiji) { this.all[feiji.name] = feiji }, send: function(msg, to) { this.all[to.name].jieshou(msg) } } var feiji1 = new feiji('feiji1') var feiji2 = new feiji('feiji2') tatai.zhuce(feiji1) tatai.zhuce(feiji2) feiji1.send('我马上降落,还有200米', feiji2)原型模式概念解读原型模式(prototype) 是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象。对于原型模式,可以利用JavaScript特有的原型继承特性去创建对象的方式,真正的原型继承是作为最新版的ECMAScript5标准提出的,使用0bject.create方 法来创建这样的对象,如Object.create(prototype, optionalDescriptorObjects)模式作用1.原型对象本身就是有效地利用了每个构造器创建的对象注意事项1.注意的依然是浅拷贝和深拷贝的问题,免得出现引用问题。2.现有的文献里查看原型模式的定义,没有针对JavaScript的,你可能发现很多讲解的都是关于类的,但是现实情况是基于原型继承的JavaScript完全避免了类(class)的概念。代码实战// 深拷贝克隆对象。。。 模板方法概念解读模板方法(TemplateMethod) 定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板方法是一种代码复用的基本技术,在类库中尤为重要,因为他们提取了类库中的公共行为。模板方法导致一种反向的控制结构,这种结构就是传说中的“好莱坞法则”,即“别找找我们,我们找你”,这指的是父类调用一个类的操作,而不是相反。具体体现是面向对象编程编程语言里的抽象类(以及其中的抽象方法),以及继承该抽象类(和抽象方法)的子类。模式作用1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现2.各子类中公共的行为应被提取出来并集中到一个公共父类中的避免代码重复,不同之处分离为新的操作,最后,用一个钓鱼这些新操作的模板方法来替换这些不同的代码3.控制子类扩展,模板方法只在特定点调用“hook” 操作,这样就允许在这些点进行扩展注意事项1.和策略模式不同,模板方法使用继承来改变算法的一部分,而策略模式使用委托来改变整个算法。代码实战/***示例一:造人的模板***/ function shangdi() {} shangdi.prototype.zaoren_yanjing = function() { console.log('眼睛') } shangdi.prototype.zaoren_bizi = function() { console.log('鼻子') } shangdi.prototype.zaoren_zuiba = function() { console.log('嘴巴') } shangdi.prototype.zaoren_yanjing = function() { throw new Error('我只是个钩子,需要你自己去探索') } // 小明 function xiaoming() { console.log('小明是上帝的子类') shangdi.call(this) } xiaoming.protype = new shangdi() xiaoming.prototype.aihao = function(){ console.log('小明爱讲笑话') } /***示例二:流程***/ function liucheng(){} liucheng.prototype.start = function() { confirm('您是否要进入游戏?') } liucheng.prototype.loading = function() { confirm('游戏加载中…………') } liucheng.prototype.out = function() { confirm('您是否要离开游戏?') } function xiaojigege(){} xiaojigege.prototype = new liucheng() xiaojigege.start() 装饰者模式概念解读装饰者提供比继承更有弹性的替代方案。装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数)。装饰者用于通过重载方法的形式添加新功能,该模式可以在被装饰者前面或者后面加上自己的行为以达到特定的目的。模式作用1.装饰者是-种实现继承的替代方案。当脚本运行时,在子类中增加行为会影响原有类所有的实例,而装饰者却不然。取而代之的是它能给不同对象各自添加新行为。2.添加辅助的额外功能。3.把类(函数)的核心职责和装饰功能区分开了。注意事项1.装饰的类跟被装饰的类,要求拥有相同的访问接口方法(功能)。2.装饰类的要有对被装饰类的引用,用于在装饰类的相应方法,调用相应被装饰类的方法,然后对其进行修饰。3.把每个要装饰的功能放在单独的函数里。代码实战var fangzi = function() {} fangzi.prototype.kongjian = function() { console.log('我是空的房子') } var zhuangshi = function(fangzi) { this.zfangzi = fangzi } zhuangshi.prototype.kongjian = function() { this.zfangzi.kongjian() console.log('我添加了一个家具') } var _fangzi = new fangzi() var _zhuangshi = new zhuangshi(_fangzi) _zhuangshi.kongjian() 组合模式概念解读组合模式(Composite) 将对象组合成树形结构以表示“ 部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。模式作用1.你想表示对象的部分整体层次结构时。2.你希望用户忽略组合对象和单个对象的不同,用户将统一地使用组合结构中的所有对象(方法)注意事项1.该模式经常和装饰者一起使用,它们通常有一个公共的父类(也就是原型)因此装饰必须支持具有add、remove、 getChild操作的 component接口。代码实战var zhengti() {} zhengti.prototype.kafei = function() { throw new Error('不能直接使用') } zhengti.prototype.mianbao = function() { throw new Error('不能直接使用') } function guke() {} guke.prototype.kafei = function() {} guke.prototype.mianbao = function() {} guke.prototype.diancan = function() { this.kafei() this.mianbao() }
2022年04月13日
89 阅读
0 评论
0 点赞
2022-02-04
《你不知道的JavaScript》阅读笔记
判断this现在我们可以根据优先级来判断函数在某个调用位置应用的是哪条规则。可以按照下面的顺序来进行判断:1、函数是否在new中调用(new 绑定) ?如果是的话 this 绑定的是新创建的对象。var bar = new foo()2、函数是否通过call、apply (显式绑定)或者硬绑定调用?如果是的话,this 绑定的是指定的对象。var bar = foo.call(obj2)3、函数是否在某个上下文对象中调用(隐式绑定) ?如果是的话,this 绑定的是那个上下文对象。var bar = obj1.foo()4、如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。var bar = foo()可计算属性名ES6中增加了可计算属性名,可以在文字形式中使用[]包裹一个表达式来当作属性名:var prefix = 'zhuzl' var content = { [prefix + '_name']: 'zhuzl', [prefix + '_age']: 34 } content['zhuzl_name'] // zhuzl属性描述符从ES5开始,所有的属性都具备了属性描述符。var myObject = {} Object.defineProperty(myObject, 'a', { value: 2, writable: true, // 是否可以修改值 configurable: true, // 属性描述符是否可配置,该值为单向操作,无法撤消。即便该值为false,还是可以把writable修改为false,但是无法改为true。若值为false,该值不可delete enumerable: true // 控制该属性是否出现在对象的属性枚举列表中。比如说for...in 循环 }) 对象不变性对象常量 结合writable:false和configurable:false可以创建一个真正的常量属性(不可修改、重定义和删除)var myObject = {} Object.defineObject(myObject, 'FAVORITE_NUMBER', { value: 42, writable: false, configurable: false })禁止扩展 如果想禁止一个对象添加新属性并且保留已有属性,可以使用Object.preventExtensions(...)密封 Object.seal(...) 会创建一个“密封”的对象,这个方法实际上会调用Object.preventExtensions(...) 并把所有现有属性标记为 configurable: false。所以密封之后不仅不能添加新属性,也不能重新配置或删除任何现有属性。冻结 Object.freeze(...)会创建一个冻结对象,这个方法实际上会在一个对象上调用Object.seal(...)并把所有“数据访问”属性标记为writable: false。这个方法是应用在对象上的级别最高的不可变性。setter 和 getter通常getter 和 setter 是成对出现的,只定义一个的话通常会产生意料之外的行为。var myObject = {} Object.defineProperty(myObject, 'b', { get: function () {}, set: function () {} }) // OR var myObject = { get a() { return this._a_ }, set a(val) { this._a_ = val * 2 } }遍历for(var k in obj):用来遍历对象的可枚举属性列表,包括prototype原型链上的属性。for(var v of arr):用于遍历数组的值,理论上是通过调用数组内置的@@iterator迭代器对象,然后通过调用迭代器对象的next()方法来手动遍历数组,原理如下:var myArray = [1, 2, 3] for (var v of myArray) { console.log(v) } // 1 // 2 // 3 // ----实际工作模式---- var myArray = [1, 2, 3] var it = myArray[Symbol.iterator]() it.next() // {value: 1, done: false} it.next() // {value: 2, done: false} it.next() // {value: 3, done: false} it.next() // {done: true}数组forEach():会遍历数组中的所有值并忽略函数的返回值数组every():会一直运行直到回调函数返回false(或“假”值)数组some():会一直运行直到回调函数返回true(或“真”值)原型继承function Foo(name) { this.name = name } Foo.prototype.myName = function () { return this.name } function Bar (name, label) { Foo.call(this, name) this.label = label } // 我们创建了一个新的Bar.prototype对象并关联到Foo.prototype // 注意:现在没有Bar.prototype.constructor 了,如果需要这个属性的话可以手工修复一下 Bar.prototype = Object.create(Foo.prototype) Bar.prototype.myLabel = function () { return this.label } var a = new Bar('a', 'obj a') a.myName() // "a" a.myLabel() // "obj a" WARNING:下面这两种方式是常见的错误左房,实际上他们都存在一些问题:// 和你想要的机制不一样。该方式并不会创建一个关联到Bar.prototype的新对象,他只是让Bar.prototype 直接引用Foo.prototype对象。因此当执行类似Bar.prototype.myLable = xxx 的赋值语句时,是直接修改Foo.prototype对象本身。 Bar.prototype = Foo.prototype // 基本上满足需求,但是可能会产生一些副作用。该方式的确会创建一个关联到Foo.prototype的新对象。但是他使用的Foo(...)的“构造函数调用”,如果函数Foo有一些副作用(比如写日志、修改状态、注册到其他对象、给this添加数据属性等),就会影响到Bar()的“后代”。 Bar.prototype = new Foo()TIPS:ES6添加了辅助函数Object.setPrototypeOf(...),可以作为标准且可靠的方式来修改对象关联:// ES6之前使用Bar.prototype = Object.create(Foo.prototype) Bar.prototype = Object.create(Foo.prototype) // ES6 开始可以直接使用修改现有的Bar.prototype Object.setPrototypeOf(Bar.prototype, Foo.prototype)整数的安全范围能够被“安全”呈现的最大整数是2^53-1,即9007199254740991,在ES6中被定义为Number.MAX_SAFE_INTEGER。最小整数是-9007199254740991,在ES6中被定义为Number.MIN_SAFE_INTEGER。toString基本类型值得字符串化规则null -> "null"undefined -> "undefined"true -> "true"普通对象除非自定义,否则toString() (Object.prototype.toString())返回内部属性[[class]]的值,如“[object Object]”如果对象有自己的toString方法,字符串化时会调用该方法并使用其返回值。数组的默认toString()方法经过了重新定义,将返回单元字符串化以后在用“,”链接起来JSON字符串化:JSON.stringify(val, replacer, space),在将对象序列化为字符串时也用到了toString不安全的JSON值:undefined,function,symbol和包含循环引用的对象,JSON.stringify(...)在对象中遇到undefined、function和symbol时会自动将其忽略,在数组中则会返回null(以保证单元位置不变)。如果对象中定义了toJSON()方法,JSON字符串化时会首先调用该方法,然后用它的返回值来进行序列化。toJSON()应该“返回一个能够被字符串化的安全的JSON值”,而不是“返回一个JSON字符串”toNumber非数字值转换数字逻辑:true -> 1false -> 0undefined -> NaNnull -> 0toBoolean以下这些值都是假值:undefinednullfalse0、+0、-0和NaN""除以上假值列表的数据外均为真值~ 运算符(即字位操作“非” ) 和 |(即字位操作“或” )字位运算符只适用于32位整数,运算符会强制操作数使用32位格式,这是通过抽象操作toInt322来实现的。~x 大致等同于 -(x+1), 如 ~42 = ~(42+1) ==> -43。在-(x+1)中唯一能得到0的x值是-1,也就是说如果x为 -1 时,~x会返回假值0,其他情况下则返回真值。根据该规则可以将~和indexOf一起将结果强制转换为真/假值:if (~a.indexOf('xxx')) { // true // 找到匹配的! }~~x: 截除数字值的小数部分
2022年02月04日
14 阅读
0 评论
0 点赞
2021-12-07
Typescript的14个基础语法
一.Ts是什么首先,强类型不允许随意的隐式类型转换,而弱类型是允许的。JavaScript就是经典的弱类型语言。而Typescript可以说是JavaScript的超集,在JS的基础上新增了许多语法特性,使得类型不再可以随意转换,能大大减少开发阶段的错误。二. 基本语法1.声明原始数据类型在变量后面指定一个关键字表示其只能为什么类型。// string类型: const a: string = 'auroras' // number类型: const b: number = 666 // 包括 NAN Infinity // boolean类型: const c: boolean = true // null类型: const d: null = null // undefined类型: const e: undefined = undefined // symbol类型: const h: symbol = Symbol()2.声明Object类型首先,object类型不单单可以指定对象,还可以指定数组或函数:const foo1: object = {}; const foo2: object = []; const foo3: object = function(){};如果只想指定为对象,如下,对象属性都要提前声明好类型:const obj: {name: string,age: number} = { name: '北极光', age:18 }3.1声明数组类型可以指定声明Array且通过<>指定元素类型,比如指定声明元素都为数字的数组:const arr: Array<number> = [1,2,3] // OR const arr: number[] = [1,2,3]3.2声明元组类型就是要提前指定数组里每个元素的类型,严格一一对应:const tuple: [number,string,boolean] = [666,'auraros',true]4.声明枚举类型通过关键字enum声明一个枚举类型,如:enum Status { pedding = 1, resolve = 2, reject = '3' } //访问 console.log(Status.pedding);如果全不写值,默认值为从0开始递增。如果第一个元素为字符类型,就必须全部定义值。如果第一个元素指定为一个数字,后面元素不写值,那值为第一个元素值按位置大小递增的结果。5.函数参数与返回类型函数声明式:指定函数传入参数类型,指定返回值类型,调用时传入参数个数与类型都必须相同:括号里指定每个参数类型,括号右边指定返回值的类型。function fun (name:string,age:number):string{ return 'sss' } fun('auroras',18);如果传入参数不确定传不传,那么可以给参数加个‘?’表明它是可选的:function fun (name:string,age?:number):string{ return 'sss' } fun('auroras');或者给参数添加默认值,那也会成为可选参数:function fun (name:string,age:number=666):string{ return 'sss' } fun('auroras');如果参数个数不确定,可以用扩展运算符加解构赋值表示,当然要传入与指定类型一致的:function fun (name:string,age:number=666,...res:number[]):string{ return 'sss' } fun('auroras',1,2,3);函数表达式:const fun2:(name:string,age:number)=>string = function(name:string,age:number){ return 'sss' }6.任意类型通过指定any关键字表示任意类型,跟原来 js 一样,可以任意赋不同类型的值:let num:any = 1; num = 'a'; num = true;7.类型断言类型断言就是明确的告诉typescript这个变量就是某种类型的,百分之百确定。不用typescript在一些情况下要自己推断某些没有明确定义或者多变的场景是什么类型。可以通过 as+类型 断言它就是某种类型的:const res = 1; const num = res as number;也可以通过 <类型> 形式断言(不推荐):const res = 1; const num = <number>res8.接口基本使用接口可以理解为一种规范,一种契约。可以约束一个对象里应该有哪些成员,这些成员都是怎么样的。通过interface定义一个Post接口,这个接口是一个对象,规则为有一个name属性类型为string,age属性类型为number。interface Post { name:string; age:number }然后比如有一个函数 printPost ,它的参数 post 使用我们定义的 Post 接口的规则,那么调用此函数传参时要传入符合 Post 接口规则的数据。interface Post { name:string; age:number } function printPost(post: Post){ console.log(post.name); console.log(post.age); } printPost({name:'asd',age:666})当然,函数传参时可能有些参数是可选的,那么我们可以给接口也定义可选的成员,通过属性后加一个‘ ? ’指定 可选成员 :interface Post { name:string; age:number; sex?:string; } const auroras: Post = { name:'asd', age: 18 }如果用 readonly 修饰成员,那么这个成员属性在初始化后便不可修改:interface Post { name:string; age:number; sex?:string; readonly like:string } const auroras: Post = { name:'asd', age: 18, like: 'natrue' } auroras.name = 'aaaa'; //保错 auroras.like = 'wind';如果连成员属性名称都不确定,那么可以声明 动态成员 ,要指定成员名字类型与成员值的类型,如:interface Post { [prop:string]:string } const auroras: Post = { name:'asd', like: 'natrue' }9.类基本使用描述一类具体事物的抽象特征。ts增强了es6中class类的相关语法。首先,类的属性使用前必须提前声明好:class Person { name: string; age: number; constructor(name:string,age:number){ this.name = name; this.age = age; } sayHi(msg:string):void { console.log(`hi,${msg},i am ${this.name}`); } }10.类的访问修饰符private 修饰私有属性,只能在类内部访问。 public 修饰公用属性(默认),外部也可访问:class Person { public name: string; private age: number; constructor(name:string,age:number){ this.name = name; this.age = age; } sayHi(msg:string):void { console.log(`hi,${msg},i am ${this.name}`); console.log(this.age); } } const jack = new Person('jack',20); //Person类公有属性可以访问 console.log(jack.name); //Person类私有属性不可以访问 console.log(jack.age);protected 修饰为受保护的,外部也不可访问。但与 private 的区别是若是继承的子类是可以访问的。class Person { public name: string; private age: number; // protected protected gender: boolean; constructor(name:string,age:number){ this.name = name; this.age = age; this.gender = true; } sayHi(msg:string):void { console.log(`hi,${msg},i am ${this.name}`); console.log(this.age); } } class children extends Person{ constructor(name:string,age:number){ super(name,age,); //可以访问 console.log(this.gender); } }11.类只读属性给属性设置 readonly 则为只读属性,该属性初始化后便不可再修改。class Person { public name: string; private age: number; // readonly protected readonly gender: boolean; constructor(name:string,age:number){ this.name = name; this.age = age; this.gender = true; } sayHi(msg:string):void { console.log(`hi,${msg},i am ${this.name}`); console.log(this.age); } }12.类与接口一些类与类之间有些许共同的特征,这些共同的特征可以抽象成为接口。比如 Person 类和 Animal 类,虽然是不同类,但是人和动物都会吃东西和走路等,这些共同的特征可以由接口定义。最后一个特征就定义一个接口。//吃接口 interface Eat { eat(food:string):void } //行进接口 interface Run { run(behavior:string):void } //人 class People implements Eat,Run { eat(food:string){ console.log(`在餐桌上吃${food}`); } run(behavior:string){ console.log(`站着${behavior}`); } } //动物 class Animal implements Eat,Run { eat(food:string){ console.log(`在地上上吃${food}`); } run(behavior:string){ console.log(`爬着${behavior}`); } }13.抽象类约束子类必须有某些成员,有点类似接口,不同的是抽象类可以包含一些具体的实现。比如动物类应该为一个抽象类,它的子类有猫,狗,熊猫等。它们都是动物,也有一些共同的特征。定义一个类为抽象类后,就不能再new实例了,只能被其子类继承。其中 abstract 定义抽象类,类里用 abstract 定义一个抽象方法,子类必须实现抽象方法。abstract class Animal { eat(food:string){ console.log(`在地上吃${food}`); } abstract run (behavior:string):void } //猫 class Dog extends Animal{ run(behavior:string):void{ console.log(behavior); } } const d1 = new Dog(); d1.eat('骨头') d1.run('四脚爬行') //兔子 class rabbit extends Animal{ run(behavior:string):void{ console.log(behavior); } } const r1 = new rabbit(); d1.eat('萝卜') d1.run('蹦蹦跳跳') 14.泛型泛型就是在定义函数,接口或者类的时候没有指定具体类型,等到使用时才指定具体类型。极大程度的复用代码。比如有一个 identity 函数,这个函数会返回任何传入它的值,且传入的类型与返回的类型应该是相同的。如果传入数字,不用泛型的话,这个函数可能是下面这样: function identity(arg:number):number{ return arg }如果传入字符串,这个函数可能是下面这样: function identity(arg:string):string{ return arg }这样的话太麻烦,所以可以使用泛型,一般用大写 T 表示泛型,它可以适用于多个类型,且传入类型与返回类型是相同的。 function identity<T>(arg:T):T{ return arg }
2021年12月07日
152 阅读
0 评论
0 点赞
2021-11-30
简单理解 Typescript 的配置文件 tsconfig.json
TS 使用 tsconfig.json 作为其配置文件,他主要包括两块内容:指定待编译的文件定义编译选项一般来说, tsconfig.json 文件所处的路径就是当前 TS 项目的根路径。tsconfig.json 的配置项众多并且复杂。所有的选项可以参考官方文档:https://www.typescriptlang.org/zh/tsconfig 这里我们分析一个简单示例:{ "compilerOptions": { "module": "commonjs", "noImplicitAny": true, "sourceMap": true, "declaration": true }, "files": [ "app.ts", "foo.ts", ] }其中, compilerOptions 用来配置编译选项, files 用来指定待编译文件。这里的待编译文件是指入口文件,任何被入口文件依赖的文件。也可以使用 include 和 exclude 来指定和排除待编译文件:{ "include": [ "src/**/*" ], "exclude": [ "node_modules", "**/*.spec.ts" ] }下面简述 compilerOptions 中的选项配置,这些配置简单来说就是影响整个编译的过程和编译的结果。module - 生成的 Javascript 模块形式:none、commonjs、amd、system、umd、es6、es2015 或 esnextnoImplicitAny - 存在隐式 any 时抛错 (默认为 false)sourceMap - 生成 map 文件 (默认为 false)declaration - 生成对应的 .d.ts 文件 (默认为 false)
2021年11月30日
307 阅读
0 评论
0 点赞
2021-09-06
如何防止他人恶意调试你的web程序
前言看到社区很多都在讨论 如何调试,如何高级的调试,以及一些调试的奇技淫巧 ,今天我想和大家聊聊, 怎么禁止调试,禁止他人调试我们的程序 为什么会有这篇文章呢,源自一次我寻找盗版电影的遭遇,一次好奇心的驱使下,由于很多这种平台都是只做搬运,不做存储,因为存储盗版电影向他人提供是违法的,特别是那种刚出的新电影! 当时好奇想通过看某站的控制台,想了解一下他们是怎么是通过啥接口,怎么请求,请求来的格式啥样的,抱着这样的好奇心,开始了我的奇妙之旅... 看完本篇文章你将学会我无法断定你能学到什么,但是以下是我希望你能从本篇文章中学到的:1.如何简单的防止你的程序被他人恶意调试2.逆向思维学会如何更好的调试具体实现防止调试的方法,这里我们主要是通过不断 debugger 的方法来疯狂输出断点,让控制台打开后程序就无法正常执行我们都知道 debugger 只有在控制台被打开的时候才会执行,所以后面的所有方法都是围绕着这一特性来进行,废话不多说,我将通过以下几个案例向你们展示道高一尺魔高一丈的道理,先上代码:方法一:(() => { function block() { setInterval(() => { debugger; }, 50); } try { block(); } catch (err) {} })(); 通过上方的代码我们可以看到,在页面中打开控制台后,会有以下结果:需要在这里说明以下几点:程序被 debugger 阻泄了,我们无法像以往一样在 Source Tab 中的对应 js 代码处添加断点调试,无法调试程序的执行逻辑.在程序异常复杂且被混淆后的代码是异常难读的!通常我们会在 source 的左边加上 breakpoint 来让程序每次走到加点的地方停下来,以便让我们查看一些变量的值或是步骤的流程逻辑(如下图所示)我们都知道,第一次打开控制台是看不到 Network tab 中的任何请求的,所以我们想通过 Network tab 来查看网页都做了哪些请求,也是看不到的,当我们打开控制台就会出 debugger 阻挡我们,我们可以通过下面的解决方法来处理,或者是用抓包工具来查看具体的请求大家可以先不看解决方法,想想如果是你,这个时候怎么突破这个屏障呢? 第一次遇到这种情况我也是很懵,不知道咋处理,后面发现问题简直不要太简单,我们可以带着疑问来看: 对于第一个示例,我们如何解决?(绕过它) 答案是: 禁止断点 可以看到很简单,在 Chrome 控制台的 Source Tab 页点击 Deactivate breakpoints 按钮或者按下 Ctrl + f8(如下图所示)。但是对于控制台不熟悉的小伙伴,很难会想到这里去.但是,难道这篇文章就这样结束了?那我可顶不住小伙伴们的 "就这?" 其实,上面的解决方法并没有帮我们解决根本问题,我们需要做的是调试,上面虽然把debugger都去掉了,但是我们也无法在通过点击每一行代码左边的行号添加 breakpoint 了,所以根本性的问题,并没有解决,只是去除了那碍眼的疯狂 debugger,我们还是得另辟蹊径方法二:对对应的代码行,通过添加 logpoint 为 false,然后按回车后刷新网页,发现成功跳过无限 debugger,于是我们就可以愉快的自由调试了~对应的还有一种方法即通过 add script ignore list 来添加需要忽略执行代码行或文件可以看到,我们也可以通过删除 script ignore list 里已添加的忽略代码,恢复初始状态但是,你这么聪明,那人家不得想想对策?对于上面的第一个方法将setInterval(() => {debugger;}, 50);写在一行中,你即使通过添加logpoint为 false,也没用,仍然是疯狂 debugger,即使你可能想到,通过左下角的代码格式化,来格式一下setInterval(() => {debugger;}, 50);将它变成多行的,也是没用的,仍然会在刷新后重新弹 debugger(() => { function block() { setInterval(() => {debugger;}, 50); } try { block(); } catch (err) {} })();对于第二个方法,我们对代码进行如下改造(() => { function block() { setInterval(() => { Function("debugger")(); }, 50); } try { block(); } catch (err) {} })();我们可以通过将debugger改写成Function("debugger")();的形式,来应对;Function 构造器生成的 debugger 会在每一次执行时开启一个临时 js 文件,哈哈~对方表示好无奈于是会有以下结果 这无限套娃,真够狠的,我们要坚信正义最后总会胜利,不能给想非法调试我们程序的人机会,所以我们要把各种情况都考虑周全,可以说这种方法是最恨的,但是这还不算完~ (好家伙~ 想非法调试我程序,那你就得战胜我)强化以上方法 上面的代码由于没有加密混淆,多少可能还是会被别人读一些,那么我们加密混淆看看是啥样的好家伙,你这咋读?eval(function(c,g,a,b,d,e){d=String;if(!"".replace(/^/,String)){for(;a--;)e[a]=b[a]||a;b=[function(f){return e[f]}];d=function(){return"\\w+"};a=1}for(;a--;)b[a]&&(c=c.replace(new RegExp("\\b"+d(a)+"\\b","g"),b[a]));return c}('(()=>{1 0(){2(()=>{3("4")()},5)}6{0()}7(8){}})();',9,9,"block function setInterval Function debugger 50 try catch err".split(" "),0,{}));格式化后的样子我们继续对代码进行改造,让对方尽量的难以识别我们的代码将 Function("debugger").call() 改成 (function(){return false;})["constructor"]("debugger")["call"](); 并且,添加条件,当窗口外部宽高,和内部宽高的差值大于一定的值,我把 body 里的内容全部清空掉,看你还能不能操作我的按钮啊啥的~哈哈哈需要特别说明的是 : 像 toG 的项目或者是一些为了保护自己的版权又或者是一些比较敏感的项目,出于安全的考虑在部署到生产环境后最好是不让别人调试的,当然,前端所做的也就那么一些,需要前后端一起配合,便可以很好的对项目或者数据进行私密的保护 最后: 附上这份未混淆的来之不易的的代码(记得混淆后使用哦~) 一定要记得点赞加关注~原创太不容易了.你可以把它当作你的工具函数,在需要不让别人轻易调试的项目中引用 (当然它仍然有很多漏洞,但是我们可以用类似的逻辑方法,变化出很多更复杂的限制,最暴力的甚至可以当控制台打开后就立马通过window.close();来关闭调试窗口),欢迎大家在评论区留下更好玩的方法~共同学习(() => { function block() { if ( window.outerHeight - window.innerHeight > 200 || window.outerWidth - window.innerWidth > 200 ) { document.body.innerHTML = "检测到非法调试,请关闭后刷新重试!"; } setInterval(() => { (function () { return false; } ["constructor"]("debugger") ["call"]()); }, 50); } try { block(); } catch (err) {} })();推荐一个调试页面的小技巧说了那么多的防止被人调试,那么最后也说一个本人觉得眼前一亮的调试样式的方法通过给 style 标签添加 style="display: block" , contenteditable 两个属性实现在页面中便捷的调试样式复制下方代码到你的 html 文件中,玩一下~<!DOCTYPE html> <body> <div>来调试我吧~</div> <style style="display: block" contenteditable> body { background-color: rgb(140, 209, 230); color: white; } div { background-color: green; width: 300px; height: 300px; line-height: 300px; text-align: center; } </style> </body> 最后我所知道的禁止调试的方法就只有如上所述,但是肯定还有很多好玩的,小伙伴们可以在评论区留言,一起共同学习~最后抛出一个问题, 如何监测控制台是否被打开 (我上面提到的通过window内外高宽的差值其实可以通过独立调试窗口的方法绕过),网上找了一些方法,貌似都没用,感兴趣且有头绪,或者已经有方法的小伙伴可以小伙伴可以在评论下方说说自己的想法,{card-describe title="引用自"}作者:荣顶链接:https://juejin.cn/post/7000784414858805256来源:掘金{/card-describe}
2021年09月06日
62 阅读
0 评论
0 点赞
2021-06-02
33个JavaScript常用函数封装方法汇总
这是我在实际开发中常用的一些js函数方法,今天总结了一下,以便以后查询,有需要的小伙伴可以参考下。有些人或许会觉得忘了百度就完事儿,No no no!这事儿我真的亲自实践过好多次,百度一次记住了还好,记不住下次碰着了还得找度娘简直是拉低工作效率。1、加载js || css || style const loadRes = function(name, type, fn) { // 加载js || css || style let ref if (type === 'js') { // 外部js ref = document.createElement('script') ref.setAttribute('type', 'text/JavaScript') ref.setAttribute('src', name) } else if (type === 'css') { // 外部css ref = document.createElement('link') ref.setAttribute('rel', 'stylesheet') ref.setAttribute('type', 'text/css') ref.setAttribute('href', name) } else if (type === 'style') { // style ref = document.createElement('style') ref.innerhtml = name } if (typeof ref !== 'undefined') { document.getElementsByTagName('head')[0].appendChild(ref) ref.onload = function() { // 加载完成执行 typeof fn === 'function' && fn() } } }2、获取url参数const getUrlParam = function(name) { // 获取url参数 let reg = new RegExp('(^|&?)' + name + '=([^&]*)(&|$)', 'i') let r = window.location.href.substr(1).match(reg) if (r != null) { return decodeURI(r[2]) } return undefined }3、本地存储const store = { // 本地存储 set: function(name, value, day) { // 设置 let d = new Date() let time = 0 day = (typeof(day) === 'undefined' || !day) ? 1 : day // 时间,默认存储1天 time = d.setHours(d.getHours() + (24 * day)) // 毫秒 localStorage.setItem(name, JSON.stringify({ data: value, time: time })) }, get: function(name) { // 获取 let data = localStorage.getItem(name) if (!data) { return null } let obj = JSON.parse(data) if (new Date().getTime() > obj.time) { // 过期 localStorage.removeItem(name) return null } else { return obj.data } }, clear: function(name) { // 清空 if (name) { // 删除键为name的缓存 localStorage.removeItem(name) } else { // 清空全部 localStorage.clear() } } }4、cookie操作【set,get,del】const cookie = { // cookie操作【set,get,del】 set: function(name, value, day) { let oDate = new Date() oDate.setDate(oDate.getDate() + (day || 30)) document.cookie = name + '=' + value + ';expires=' + oDate + "; path=/;" }, get: function(name) { let str = document.cookie let arr = str.split('; ') for (let i = 0; i < arr.length; i++) { let newArr = arr[i].split('=') if (newArr[0] === name) { return newArr[1] } } }, del: function(name) { this.set(name, '', -1) } }5、Js获取元素样式【支持内联】const getRealStyle = function(obj, styleName) { // Js获取元素样式【支持内联】 var realStyle = null if (obj.currentStyle) { realStyle = obj.currentStyle[styleName] } else if (window.getComputedStyle) { realStyle = window.getComputedStyle(obj, null)[styleName] } return realStyle }6、时间格式化const formatDate = function(fmt, date) { // 时间格式化 【'yyyy-MM-dd hh:mm:ss',时间】 if (typeof date !== 'object') { date = !date ? new Date() : new Date(date) } var o = { 'M+': date.getMonth() + 1, // 月份 'd+': date.getDate(), // 日 'h+': date.getHours(), // 小时 'm+': date.getMinutes(), // 分 's+': date.getSeconds(), // 秒 'q+': Math.floor((date.getMonth() + 3) / 3), // 季度 'S': date.getMilliseconds() // 毫秒 } if (/(y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) } for (var k in o) { if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) } } return fmt }7、原生ajax操作const ajax = function(conf) { // ajax操作 let url = conf.url, data = conf.data, senData = [], // 封装后的数据 async = conf.async !== undefined ? conf.async : true, // ture为异步请求 type = conf.type || 'get', // 默认请求方式get dataType = conf.dataType || 'json', contenType = conf.contenType || 'application/x-www-form-urlencoded', success = conf.success, error = conf.error, isForm = conf.isForm || false, // 是否formdata header = conf.header || {}, // 头部信息 xhr = '' // 创建ajax引擎对象 if (data == null) { senData = '' } else if (typeof data === 'object' && !isForm) { // 如果data是对象,转换为字符串 for (var k in data) { senData.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k])) } senData = senData.join('&') } else { senData = data } try { xhr = new ActiveXObject('microsoft.xmlhttp') // IE内核系列浏览器 } catch (e1) { try { xhr = new XMLHttpRequest() // 非IE内核浏览器 } catch (e2) { if (error != null) { error('不支持ajax请求') } } }; xhr.open(type, type !== 'get' ? url : url + '?' + senData, async) if (type !== 'get' && !isForm) { xhr.setRequestHeader('content-type', contenType) } for (var h in header) { xhr.setRequestHeader(h, header[h]) } xhr.send(type !== 'get' ? senData : null) xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { if (dataType === 'json' && success != null) { let res = '' try { res = eval('(' + xhr.responseText + ')') } catch (e) { console.log(e) } success(res) // 将json字符串转换为js对象 }; } else { if (error != null) { error('通讯失败!' + xhr.status) } } } } }8、fetch请求的封装const fetch = function(url, setting) { // fetch请求的封装 let opts = { // 设置参数的初始值 method: (setting.method || 'GET').toUpperCase(), // 请求方式 headers: setting.headers || {}, // 请求头设置 credentials: setting.credentials || true, // 设置cookie是否一起发送 body: setting.body || {}, mode: setting.mode || 'no-cors', // 可以设置 cors, no-cors, same-origin redirect: setting.redirect || 'follow', // follow, error, manual cache: setting.cache || 'default' // 设置 cache 模式 (default, reload, no-cache) } let dataType = setting.dataType || 'json' // 解析方式 let data = setting.data || '' // 参数 let paramsFormat = function(obj) { // 参数格式 var str = '' for (var i in obj) { str += `${i}=${obj[i]}&` } return str.split('').slice(0, -1).join('') } if (opts.method === 'GET') { url = url + (data ? `?${paramsFormat(data)}` : '') } else { setting.body = data || {} } return new Promise((resolve, reject) => { fetch(url, opts).then(async res => { let data = dataType === 'text' ? await res.text() : dataType === 'blob' ? await res.blob() : await res.json() resolve(data) }).catch(e => { reject(e) }) }) }9、设备判断:android、ios、webconst isDevice = function() { // 判断是android还是ios还是web var ua = navigator.userAgent.toLowerCase() if (ua.match(/iPhone\sOS/i) === 'iphone os' || ua.match(/iPad/i) === 'ipad') { // ios return 'iOS' } if (ua.match(/Android/i) === 'android') { return 'Android' } return 'Web' }10、判断是否为微信const isWx = function() { // 判断是否为微信 var ua = window.navigator.userAgent.toLowerCase() if (ua.match(/MicroMessenger/i) === 'micromessenger') { return true } return false }11、文本复制功能const copyTxt = function(text, fn) { // 复制功能 if (typeof document.execCommand !== 'function') { console.log('复制失败,请长按复制') return } var dom = document.createElement('textarea') dom.value = text dom.setAttribute('style', 'display: block;width: 1px;height: 1px;') document.body.appendChild(dom) dom.select() var result = document.execCommand('copy') document.body.removeChild(dom) if (result) { console.log('复制成功') typeof fn === 'function' && fn() return } if (typeof document.createRange !== 'function') { console.log('复制失败,请长按复制') return } var range = document.createRange() var div = document.createElement('div') div.innerhtml = text div.setAttribute('style', 'height: 1px;fontSize: 1px;overflow: hidden;') document.body.appendChild(div) range.selectNode(div) var selection = window.getSelection() console.log(selection) if (selection.rangeCount > 0) { selection.removeAllRanges() } selection.addRange(range) document.execCommand('copy') typeof fn === 'function' && fn() console.log('复制成功') }12、判断是否是一个数组const isArray = function(arr) { // 判断是否是一个数组 return Object.prototype.toString.call(arr) === '[object Array]' }13、判断两个数组是否相等const arrayEqual = function(arr1, arr2) { //判断两个数组是否相等 if (arr1 === arr2) return true; if (arr1.length != arr2.length) return false; for (let i = 0; i < arr1.length; ++i) { if (arr1[i] !== arr2[i]) return false; } return true; }14、时间与时间戳转换const stamp = { // 时间,时间戳转换 getTime: function(time) { // 时间转10位时间戳 let date = time ? new Date(time) : new Date() return Math.round(date.getTime() / 1000) }, timeToStr: function(time, fmt) { // 10位时间戳转时间 return new Date(time * 1000).pattern(fmt || 'yyyy-MM-dd') } }15、常用正则验证const checkStr = function(str, type) { // 常用正则验证,注意type大小写 switch (type) { case 'phone': // 手机号码 return /^1[3|4|5|6|7|8|9][0-9]{9}$/.test(str) case 'tel': // 座机 return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str) case 'card': // 身份证 return /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/.test(str) case 'pwd': // 密码以字母开头,长度在6~18之间,只能包含字母、数字和下划线 return /^[a-zA-Z]\w{5,17}$/.test(str) case 'postal': // 邮政编码 return /[1-9]\d{5}(?!\d)/.test(str) case 'QQ': // QQ号 return /^[1-9][0-9]{4,9}$/.test(str) case 'email': // 邮箱 return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str) case 'money': // 金额(小数点2位) return /^\d*(?:\.\d{0,2})?$/.test(str) case 'URL': // 网址 return /(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/.test(str) case 'IP': // IP return /((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))/.test(str) case 'date': // 日期时间 return /^(\d{4})\-(\d{2})\-(\d{2}) (\d{2})(?:\:\d{2}|:(\d{2}):(\d{2}))$/.test(str) || /^(\d{4})\-(\d{2})\-(\d{2})$/.test(str) case 'number': // 数字 return /^[0-9]$/.test(str) case 'english': // 英文 return /^[a-zA-Z]+$/.test(str) case 'chinese': // 中文 return /^[\u4E00-\u9FA5]+$/.test(str) case 'lower': // 小写 return /^[a-z]+$/.test(str) case 'upper': // 大写 return /^[A-Z]+$/.test(str) case 'HTML': // HTML标记 return /<("[^"]*"|'[^']*'|[^'">])*>/.test(str) default: return true } }16、是否为PC端const isPC = function() { // 是否为PC端 let userAgentInfo = navigator.userAgent let 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 }17、去除字符串空格const trim = function(str, type) { // 去除空格, type: 1-所有空格 2-前后空格 3-前空格 4-后空格 type = type || 1 switch (type) { case 1: return str.replace(/\s+/g, '') case 2: return str.replace(/(^\s*)|(\s*$)/g, '') case 3: return str.replace(/(^\s*)/g, '') case 4: return str.replace(/(\s*$)/g, '') default: return str } }18、字符串大小写转换const changeCase = function(str, type) { // 字符串大小写转换 type: 1:首字母大写 2:首页母小写 3:大小写转换 4:全部大写 5:全部小写 type = type || 4 switch (type) { case 1: return str.replace(/\b\w+\b/g, function(word) { return word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase() }) case 2: return str.replace(/\b\w+\b/g, function(word) { return word.substring(0, 1).toLowerCase() + word.substring(1).toUpperCase() }) case 3: return str.split('').map(function(word) { if (/[a-z]/.test(word)) { return word.toUpperCase() } else { return word.toLowerCase() } }).join('') case 4: return str.toUpperCase() case 5: return str.toLowerCase() default: return str } }19、过滤html代码const filterTag = function(str) { // 过滤html代码(把<>转换) str = str.replace(/&/ig, '&') str = str.replace(/</ig, '<') str = str.replace(/>/ig, '>') str = str.replace(' ', ' ') return str }20、生成随机数范围const random = function(min, max) { // 生成随机数范围 if (arguments.length === 2) { return Math.floor(min + Math.random() * ((max + 1) - min)) } else { return null } }21、阿拉伯数字转中文大写数字const numberToChinese = function(num) { // 将阿拉伯数字翻译成中文的大写数字 let AA = new Array('零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十') let BB = new Array('', '十', '百', '仟', '萬', '億', '点', '') let a = ('' + num).replace(/(^0*)/g, '').split('.') let k = 0 let re = '' for (let i = a[0].length - 1; i >= 0; i--) { switch (k) { case 0: re = BB[7] + re break case 4: if (!new RegExp('0{4}//d{' + (a[0].length - i - 1) + '}$').test(a[0])) { re = BB[4] + re } break case 8: re = BB[5] + re BB[7] = BB[5] k = 0 break } if (k % 4 === 2 && a[0].charAt(i + 2) !== 0 && a[0].charAt(i + 1) === 0) { re = AA[0] + re } if (a[0].charAt(i) !== 0) { re = AA[a[0].charAt(i)] + BB[k % 4] + re } k++ } if (a.length > 1) { // 加上小数部分(如果有小数部分) re += BB[6] for (let i = 0; i < a[1].length; i++) { re += AA[a[1].charAt(i)] } } if (re === '一十') { re = '十' } if (re.match(/^一/) && re.length === 3) { re = re.replace('一', '') } return re }22、原生dom操作const dom = { $: function(selector) { let type = selector.substring(0, 1) if (type === '#') { if (document.querySelecotor) return document.querySelector(selector) return document.getElementById(selector.substring(1)) } else if (type === '.') { if (document.querySelecotorAll) return document.querySelectorAll(selector) return document.getElementsByClassName(selector.substring(1)) } else { return document['querySelectorAll' ? 'querySelectorAll' : 'getElementsByTagName'](selector) } }, hasClass: function(ele, name) { /* 检测类名 */ return ele.className.match(new RegExp('(\\s|^)' + name + '(\\s|$)')) }, addClass: function(ele, name) { /* 添加类名 */ if (!this.hasClass(ele, name)) ele.className += ' ' + name }, removeClass: function(ele, name) { /* 删除类名 */ if (this.hasClass(ele, name)) { let reg = new RegExp('(\\s|^)' + name + '(\\s|$)') ele.className = ele.className.replace(reg, '') } }, replaceClass: function(ele, newName, oldName) { /* 替换类名 */ this.removeClass(ele, oldName) this.addClass(ele, newName) }, siblings: function(ele) { /* 获取兄弟节点 */ console.log(ele.parentNode) let chid = ele.parentNode.children, eleMatch = [] for (let i = 0, len = chid.length; i < len; i++) { if (chid[i] !== ele) { eleMatch.push(chid[i]) } } return eleMatch }, getByStyle: function(obj, name) { /* 获取行间样式属性 */ if (obj.currentStyle) { return obj.currentStyle[name] } else { return getComputedStyle(obj, false)[name] } }, domToStirng: function(htmlDOM) { /* DOM转字符串 */ var div = document.createElement('div') div.appendChild(htmlDOM) return div.innerHTML }, stringToDom: function(htmlString) { /* 字符串转DOM */ var div = document.createElement('div') div.innerHTML = htmlString return div.children[0] } }23、判断图片加载完成const imgLoadAll = function(arr, callback) { // 图片加载 let arrImg = [] for (let i = 0; i < arr.length; i++) { let img = new Image() img.src = arr[i] img.onload = function() { arrImg.push(this) if (arrImg.length == arr.length) { callback && callback() } } } }24、音频加载完成操作const loadAudio = function(src, callback) { // 音频加载 var audio = new Audio(src) audio.onloadedmetadata = callback audio.src = src }25、光标所在位置插入字符const insertAtCursor = function(dom, val) { // 光标所在位置插入字符 if (document.selection) { dom.focus() let sel = document.selection.createRange() sel.text = val sel.select() } else if (dom.selectionStart || dom.selectionStart == '0') { let startPos = dom.selectionStart let endPos = dom.selectionEnd let restoreTop = dom.scrollTop dom.value = dom.value.substring(0, startPos) + val + dom.value.substring(endPos, dom.value.length) if (restoreTop > 0) { dom.scrollTop = restoreTop } dom.focus() dom.selectionStart = startPos + val.length dom.selectionEnd = startPos + val.length } else { dom.value += val dom.focus() } }26、图片地址转base64const getBase64 = function(img) { //传入图片路径,返回base64,使用getBase64(url).then(function(base64){},function(err){}); let getBase64Image = function(img, width, height) { //width、height调用时传入具体像素值,控制大小,不传则默认图像大小 let canvas = document.createElement("canvas"); canvas.width = width ? width : img.width; canvas.height = height ? height : img.height; let ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); let dataURL = canvas.toDataURL(); return dataURL; } let image = new Image(); image.crossOrigin = ''; image.src = img; let deferred = $.Deferred(); if (img) { image.onload = function() { deferred.resolve(getBase64Image(image)); } return deferred.promise(); } }27、base64图片下载功能const downloadFile = function(base64, fileName) { //base64图片下载功能 let base64ToBlob = function(code) { let parts = code.split(';base64,'); let contentType = parts[0].split(':')[1]; let raw = window.atob(parts[1]); let rawLength = raw.length; let uInt8Array = new Uint8Array(rawLength); for (let i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], { type: contentType }); }; let aLink = document.createElement('a'); let blob = base64ToBlob(base64); //new Blob([content]); let evt = document.createEvent("HTMLEvents"); evt.initEvent("click", true, true); //initEvent不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为 aLink.download = fileName; aLink.href = URL.createObjectURL(blob); aLink.click(); }28、浏览器是否支持webP格式图片const isSupportWebP = function() { //判断浏览器是否支持webP格式图片 return !![].map && document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp') == 0; }29、url参数转对象const parseQueryString = function(url) { //url参数转对象 url = !url ? window.location.href : url; if (url.indexOf('?') === -1) { return {}; } let search = url[0] === '?' ? url.substr(1) : url.substring(url.lastIndexOf('?') + 1); if (search === '') { return {}; } search = search.split('&'); let query = {}; for (let i = 0; i < search.length; i++) { let pair = search[i].split('='); query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || ''); } return query; }30、对象序列化【对象转url参数】const stringfyQueryString = function(obj) { //对象序列化【对象转url参数】 if (!obj) return ''; let pairs = []; for (let key in obj) { let value = obj[key]; if (value instanceof Array) { for (let i = 0; i < value.length; ++i) { pairs.push(encodeURIComponent(key + '[' + i + ']') + '=' + encodeURIComponent(value[i])); } continue; } pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key])); } return pairs.join('&'); }31、H5软键盘缩回、弹起回调const h5Resize = function(downCb, upCb) { //当软件键盘弹起会改变当前 window.innerHeight,监听这个值变化 [downCb 当软键盘弹起后,缩回的回调,upCb 当软键盘弹起的回调] var clientHeight = window.innerHeight; downCb = typeof downCb === 'function' ? downCb : function() {} upCb = typeof upCb === 'function' ? upCb : function() {} window.addEventListener('resize', () => { var height = window.innerHeight; if (height === clientHeight) { downCb(); } if (height < clientHeight) { upCb(); } }); }32、函数防抖const debounce = function(func, wait, immediate) { //函数防抖[func 函数,wait 延迟执行毫秒数,immediate true 表立即执行,false 表非立即执行,立即执行是触发事件后函数会立即执行,然后n秒内不触发事件才能继续执行函数的效果] let timeout; return function() { let context = this; let args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { var callNow = !timeout; timeout = setTimeout(() => { timeout = null; }, wait) if (callNow) func.apply(context, args) } else { timeout = setTimeout(function() { func.apply(context, args) }, wait); } } }33、函数节流const throttle = function(func, wait ,type) { //函数节流 [func 函数 wait 延迟执行毫秒数 type 1 表时间戳版,2 表定时器版] if(type===1){ let previous = 0; }else if(type===2){ let timeout; } return function() { let context = this; let args = arguments; if(type===1){ let now = Date.now(); if (now - previous > wait) { func.apply(context, args); previous = now; } }else if(type===2){ if (!timeout) { timeout = setTimeout(() => { timeout = null; func.apply(context, args) }, wait) } } } }整理至此,请用心记!!!!以上方法可通过该工具自动获取生成哦:http://www.fly63.com/tool/code/来源 | http://www.fly63.com/article/detial/10362
2021年06月02日
161 阅读
0 评论
0 点赞
2020-07-04
JS小技巧
数字转字符串/字符串转数字let num = 15 let s = num + '' // number to string let n = +s // string to number填充数组let filledArray = new Array(10).fill(null).map((item, idx)=> ({'name' : 'value' + idx})) // filledArray = [{"name":"value0"},{"name":"value1"}]对象的动态属性 let dynamic = "value" let user = { id: 1, [dynamic]: "other value" } // user = {id: 1, value: 'other value'}删除重复项let array = [100, 23, 23, 23, 23, 67, 45] let outputArray = Array.from(new Set(array)) //outputArray = [100, 23, 67, 45]数组转对象let arr = ["value1", "value2", "value3"] let arrObject = {...arr} // arrObject = {0: 'value1', 1: 'value2', 2: 'value3'}对象转数组 let n umber = { one: 1, two: 2, } let key = Object.keys(numbers) // key = [ 'one', 'two' ] let value = Object.values(numbers) // value = [ 1, 2 ] let entry = Object.entries(numbers) // entry = [['one' : 1], ['two' : 2]]使用 ^ 检查数字是否不相等let a = 1234 if(a^123) { console.log('not equal') } // 输出:not equal循环对象 const userObj = { shiyu: 32, zhuzl: 36 }; // 方法一 - 获取对象 keys 后循环 Object.keys(userObj).forEach(key => userObj[key]++) console.log(userObj) // { shiyu: 33, zhuzl: 37 } // 方法二:实用 for ..in 循环 for(let key in userObj){ userObj[key]++ } console.log(userObj) // { shiyu: 34, zhuzl: 38 }对象键按插入顺序存储 const obj = { name: "zhuzl", age: 36, address: "湖南省长沙市", profession: "前端开发工程师" } console.log(Object.keys(obj)) // name, age, address, profession
2020年07月04日
16 阅读
0 评论
0 点赞
2018-04-29
简易版DOM封装
在无障碍工具项目的实际开发中,为了方便的获取页面元素,对页面DOM操作进行了轻量级封装,虽没有jQuery强大,但也可满足大部分使用场景了。相关核心代码如下所示: var getDom = function (el) { if (!el) { return el } if (typeof el === 'string') { return document.getElementById(el.replace(/^#/, '')) } if (!el.nodeName && el.length && el[0].nodeName) { // 如果是一个jquery对象 return el[0] } return el } var Dom = {} Dom.hasClass = function (elem, className) { var el = getDom(elem) className = className.replace(/\-/, '\\-') return new RegExp('(?:^|\\s)' + className + '(?:\\s|$)').test(el.className) } Dom.addClass = function (elem, className) { var el = getDom(elem) if (!Dom.hasClass(el, className)) { el.className = el.className ? el.className + ' ' + className : className } return el } Dom.removeClass = function (elem, className) { var el = getDom(elem) className = className.replace(/\-/, '\\-') var classToRemove = new RegExp('(^|\\s)' + className + '(?=\\s|$)', 'i') el.className = el.className .replace(classToRemove, '') .replace(/^\s+|\s+$/g, '') return el } Dom.events = {} Dom.idSeed = 0 Dom.addEvent = function (el, type, className, handler) { var id = el.id || (el.id = 'id' + ++Dom.idSeed) if (typeof className === 'function' && !handler) { handler = className className = undefined } var delegate = function (evt) { return handler.call(el, evt) } if (className) { className = className.replace(/\./, '') delegate = function (evt) { evt = evt || window.event var node = evt.target || evt.srcElement while (node && node != el) { if (Dom.hasClass(node, className)) { return handler.call(node, evt) } node = node.parentNode } } } delegate.origHandler = handler delegate.delegateSelector = className if (!Dom.events[id]) { Dom.events[id] = {} } if (!Dom.events[id][type]) { Dom.events[id][type] = [] var callFuncList = function (evt) { if (Dom.events[id][type]) { for (var i = 0; i < Dom.events[id][type].length; i++) { var fn = Dom.events[id][type][i] fn(evt) } } } if (el.addEventListener) { el.addEventListener(type, callFuncList) } else { el.attachEvent('on' + type, callFuncList) } } Dom.events[id][type].push(delegate) } Dom.removeEvent = function (el, type, className, handler) { var id = el.id || (el.id = 'id' + ++Dom.idSeed) if (typeof className === 'function' && !handler) { handler = className className = undefined } if (!Dom.events[id]) { return false } if (type && !Dom.events[id][type]) { return false } if (!handler && !className) { if (!type) { Dom.events[id] = {} } else { Dom.events[id][type] = [] } return true } for (var i = Dom.events[id][type].length - 1; i >= 0; i--) { var fn = Dom.events[id][type][i] if ( fn.origHandler == handler && (!className || fn.delegateSelector == className) ) { Dom.events[id][type].splice(i, 1) } } } Dom.attr = function (el, name, value) { if (arguments.length === 1) { var map = {} var attributes = el.attributes var aLength = attributes.length for (var a = 0; a < aLength; a++) { map[attributes[a].name.toLowerCase()] = attributes[a].value } return map } if (arguments.length === 2) { return el.getAttribute(name) } el.setAttribute(name, value) }
2018年04月29日
149 阅读
0 评论
0 点赞
2016-06-07
IE6/7下的console.log()
相信很多人和我一样,在firefox下常常用console.log()输出一些对象或变量的值,在ie6/7里常常用alert()弹出一些对象或变量的值,来作调试。有时候忘记了删除console.log()语句(或alert语句),结果在ie6/7(或没有开启firebug控制台的firefox下也会)下弹出了错误信息。 我常常在自己的common.js里加入以下代码来容错:if(!window.console){ window.console={} window.console.log=function(){return;} } 当然,能够在ie下使用console.log的调试功能当然是最好的,哪怕只是最简单的一个变量值的输出也比alert友好一些,于是有了下面这个简易版的console.log<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>ie下的console.log</title> <style> body {background: #ffffff; color: #444;} a{ color: #08e; text-decoration: none; border: 0; background-color: transparent;} body,div,q,iframe,form,h5{ margin: 0; padding: 0;} a img,fieldset { border: none 0; } body,td,textarea { word-break: break-all; word-wrap: break-word; line-height:1.5;} body,input,textarea,select,button { margin: 0; font-size: 12px; font-family: Tahoma, SimSun, sans-serif;} div,p,table,th,td { font-size:1em; font-family:inherit; line-height:inherit;} </style> <script type="text/javascript"> var debug=true; var $id = function (id) {//避免与jQuery的$函数冲突 return typeof id == "string" ? document.getElementById(id) : id; }; if(!$){var $=$id;} if(!window.console){ window.console={} window.console.cache=[]; window.console.constr=function(_value){ if(!_value)return; var result = []; if (_value instanceof Function){ result.push(_value); }else if(_value!=undefined&&Boolean(_value.nodeName)&&Boolean(_value.nodeType)){ result.push(_value.nodeName.toLowerCase()); result.push(_value.getAttribute("id")?"id="+_value.getAttribute("id"):""); result.push(_value.getAttribute("className")?"class="+_value.getAttribute("className"):""); return "<"+result.join(" ")+">"; }else if(_value instanceof Array){ for(var i=0; i< _value.length; i++) result.push(_value[i]); return "["+result.join(", ")+"]"; }else if(typeof _value == "object"){ for (var p in _value){ if(_value.hasOwnProperty(p) && p!='prototype'){ result.push("'"+p+"':"+_value[p]); } }; return "{"+result.join(", ")+"}"; }else if(typeof _value == 'string'){ return "\""+_value+"\""; }else if(typeof _value == 'number' && isFinite(_value)){ result.push(_value); }else{ result.push(_value); } return result.join(""); } window.console.log=function(outputValue){ if(!debug)return; if(!outputValue)return null; var bgColor=bgColor||"#fff"; consoleDiv =$id("_console"); if(!consoleDiv){ consoleDiv=document.createElement("div"); consoleDiv.id="_console"; consoleDiv.style.cssText="position:absolute; z-index:9999; left:0%;top:"+Math.max(document.documentElement.scrollTop, document.body.scrollTop)+"px; width:62%; background-color:#fff; border:1px solid #359; opacity:0.9; filter:alpha(opacity=90); padding:4px;" consoleDiv.innerHTML='<div id="_consoleHead" style="background-color:#cde; height:20px; color:#000; font-size:12px; line-height:20px; cursor:move;"><a style="color:#123; float:right; text-decoration:none; margin:1px 2px 0;" href="javascript:$id(\'_console\').style.display=\'none\';void(0);">[关闭]</a><a style="color:#123; float:right; text-decoration:none; margin:1px 2px 0;" href="javascript:$id(\'_consoleBody\').innerHTML=\'\';void(0);">[清空]</a></div>'; consoleDivBody=document.createElement("div"); consoleDivBody.id="_consoleBody"; consoleDivBody.style.cssText="font-size:12px; line-height:1.5;color:#333; width:100%; max-height:150px; overflow:auto;" consoleDivBody.innerHTML=''; consoleDiv.appendChild(consoleDivBody); document.getElementsByTagName("BODY")[0].appendChild(consoleDiv); if(Drag) Drag.init(consoleDiv,consoleDiv);//注册拖拽方法,可以使用自己的拖拽方法来代替,以减少代码量 } consoleDiv.style.display=""; var consoleDivTop=consoleDiv.style.top.replace(/\D/gi,""); if(consoleDivTop<Math.max(document.documentElement.scrollTop, document.body.scrollTop)||consoleDivTop>Math.max(document.documentElement.scrollTop, document.body.scrollTop)+(document.compatMode == "BackCompat"?document.body.clientHeight:document.documentElement.clientHeight)) consoleDiv.style.top=Math.max(document.documentElement.scrollTop, document.body.scrollTop)+"px"; var newItem=document.createElement("div"); newItem.style.cssText="border-top:1px solid #cde; padding:3px;font-family:'Courier New'; font-size:13px; background-color:"+bgColor; var content = []; for(var i=0, len=arguments.length; i<len; i++){ content.push( window.console.constr(arguments[i]) ); } newItem.innerHTML= content.join(" "); $id("_consoleBody").appendChild(newItem); $id("_consoleBody").scrollTop=9999; }; } /***小巧的拖拽类***/ var Drag={ "obj":null, "init":function(handle, dragBody, e){ if (e == null) { handle.onmousedown=Drag.start; } handle.root = dragBody; if(isNaN(parseInt(handle.root.style.left)))handle.root.style.left="0px"; if(isNaN(parseInt(handle.root.style.top)))handle.root.style.top="0px"; handle.root.onDragStart=new Function(); handle.root.onDragEnd=new Function(); handle.root.onDrag=new Function(); if (e !=null) { var handle=Drag.obj=handle; e=Drag.fixe(e); var top=parseInt(handle.root.style.top); var left=parseInt(handle.root.style.left); handle.root.onDragStart(left,top,e.pageX,e.pageY); handle.lastMouseX=e.pageX; handle.lastMouseY=e.pageY; document.onmousemove=Drag.drag; document.onmouseup=Drag.end; } }, "start":function(e){ var handle=Drag.obj=this; e=Drag.fixEvent(e); var top=parseInt(handle.root.style.top); var left=parseInt(handle.root.style.left); //alert(left) handle.root.onDragStart(left,top,e.pageX,e.pageY); handle.lastMouseX=e.pageX; handle.lastMouseY=e.pageY; document.onmousemove=Drag.drag; document.onmouseup=Drag.end; return false; }, "drag":function(e){ e=Drag.fixEvent(e); var handle=Drag.obj; var mouseY=e.pageY; var mouseX=e.pageX; var top=parseInt(handle.root.style.top); var left=parseInt(handle.root.style.left); if(document.all){Drag.obj.setCapture();}else{e.preventDefault();};//作用是将所有鼠标事件捕获到handle对象,对于firefox,以用preventDefault来取消事件的默认动作: var currentLeft,currentTop; currentLeft=left+mouseX-handle.lastMouseX; currentTop=top+(mouseY-handle.lastMouseY); handle.root.style.left=currentLeft +"px"; handle.root.style.top=currentTop+"px"; handle.lastMouseX=mouseX; handle.lastMouseY=mouseY; handle.root.onDrag(currentLeft,currentTop,e.pageX,e.pageY); return false; }, "end":function(){ if(document.all){Drag.obj.releaseCapture();};//取消所有鼠标事件捕获到handle对象 document.onmousemove=null; document.onmouseup=null; Drag.obj.root.onDragEnd(parseInt(Drag.obj.root.style.left),parseInt(Drag.obj.root.style.top)); Drag.obj=null; }, "fixEvent":function(e){//格式化事件参数对象 var sl = Math.max(document.documentElement.scrollLeft, document.body.scrollLeft); var st = Math.max(document.documentElement.scrollTop, document.body.scrollTop); if(typeof e=="undefined")e=window.event; if(typeof e.layerX=="undefined")e.layerX=e.offsetX; if(typeof e.layerY=="undefined")e.layerY=e.offsetY; if(typeof e.pageX == "undefined")e.pageX = e.clientX + sl - document.body.clientLeft; if(typeof e.pageY == "undefined")e.pageY = e.clientY + st - document.body.clientTop; return e; } }; /***测试***/ function sometext(ele,n){ var strArr=["可","以","清","心","也"]; var writeStr="" for(i=0;i<n;i++){ index=parseInt(Math.random()*5); for(j=0;j<5;j++){ str=index+j>4?index+j-5:index+j; writeStr+=strArr[str]; } } $(ele).innerHTML=writeStr; } function test1(){ console.log("这是一个js对象:",{w:100,h:50,area:function(){return this.w*this.h}}) } function test2(){ console.log("这是一个dom对象:",$id("div1")) } function test3(){ var arr=function(w,h){return w*h;} console.log("这是一个js函数:",arr) } function test4(){ var arr=[1,2.2,"a","abc",true,false,new Date(),/<[^>]*>/,function(){alert(0);}] console.log("这是一个数组:",arr) } </script> </head> <body> <div id="div1" class="style1"></div> <p><input type="button" value="返回一个object" onclick="test1()" /> <input type="button" value="返回一个element" onclick="test2()" /> <input type="button" value="返回一个function" onclick="test3()" /> <input type="button" value="返回一个arrary" onclick="test4()" /> </p> <div id="div2" class="style1"></div> <script>sometext("div1",200);sometext("div2",200);</script> </body> </html>如果不要自带的drag方法(大家的js库里一般都有自己的drag方法吧)的话,代码大约60行,方便调试,也不会让你的js库变得臃肿。
2016年06月07日
53 阅读
0 评论
0 点赞
2016-05-29
Web前端常用的JavaScript方法封装
1、输入一个值,返回其数据类型function type(para) { return Object.prototype.toString.call(para) }2、数组去重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; }3、字符串去重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') }4、深拷贝 浅拷贝//深克隆(深克隆不考虑函数) 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));5、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; }6、圣杯模式的继承function inherit(Target, Origin) { function F() {}; F.prototype = Origin.prototype; Target.prototype = new F(); Target.prototype.constructor = Target; // 最终的原型指向 Target.prop.uber = Origin.prototype; }7、找出字符串中第一次只出现一次的字母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; } } }8、找元素的第n级父元素function parents(ele, n) { while (ele && n) { ele = ele.parentElement ? ele.parentElement : ele.parentNode; n--; } return ele; }9、返回元素的第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; }10、封装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; }11、判断元素有没有子元素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; }12、我一个元素插入到另一个元素的后面Element.prototype.insertAfter = function (target, elen) { var nextElen = elen.nextElenmentSibling; if (nextElen == null) { this.appendChild(target); } else { this.insertBefore(target, nextElen); } }13、返回当前的时间(年月日时分秒)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 + "秒" }14、获得滚动条的滚动距离function getScrollOffset() { if (window.pageXOffset) { return { x: window.pageXOffset, y: window.pageYOffset } } else { return { x: document.body.scrollLeft + document.documentElement.scrollLeft, y: document.body.scrollTop + document.documentElement.scrollTop } } }15、获得视口的尺寸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 } } } }16、获取任一元素的任意属性function getStyle(elem, prop) { return window.getComputedStyle ? window.getComputedStyle(elem, null)[prop] : elem.currentStyle[prop] }17、绑定事件的兼容代码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; } }18、解绑事件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; } }19、取消冒泡的兼容代码function stopBubble(e) { if (e && e.stopPropagation) { e.stopPropagation(); } else { window.event.cancelBubble = true; } }20、检验字符串是否是回文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; }21、检验字符串是否是回文function isPalindrome(str) { str = str.replace(/\W/g, '').toLowerCase(); console.log(str) return (str == str.split('').reverse().join('')) }22、兼容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; }23、运动函数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) }24、弹性运动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); }25、封装自己的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) } }26、封装自己的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; }27、数组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; }28、数组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; }29、数组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; }30、获取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; }31、数组排序// 快排 [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; }32、遍历Dom树// 给定页面上的DOM元素,将访问元素本身及其所有后代(不仅仅是它的直接子元素) // 对于每个访问的元素,函数讲元素传递给提供的回调函数 function traverse(element, callback) { callback(element); var list = element.children; for (var i = 0; i < list.length; i++) { traverse(list[i], callback); } }33、原生js封装ajaxfunction 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); } }34、异步加载scriptfunction 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); }35、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; } }36、实现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; }37、实现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; }38、实现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; }39、防抖function debounce(handle, delay) { var timer = null; return function () { var _self = this, _args = arguments; clearTimeout(timer); timer = setTimeout(function () { handle.apply(_self, _args) }, delay) } }40、节流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; } } }41、requestAnimFrame兼容性方法window.requestAnimFrame = (function () { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000 / 60); }; })();42、cancelAnimFrame兼容性方法window.cancelAnimFrame = (function () { return window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || function (id) { window.clearTimeout(id); }; })();43、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); }44、获取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] || ''; } }45、格式化时间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] }); }46、验证邮箱的正则表达式function isAvailableEmail(sEmail) { var reg = /^([\w+\.])+@\w+([.]\w+)+$/ return reg.test(sEmail) }47、函数柯里化是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术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; }48、大数相加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+/, ''); }49、单例模式function getSingle(func) { var result; return function () { if (!result) { result = new func(arguments); } return result; } }50、前端随机字符串var uuid = Date.now().toString(36) + Math.random().toString(36).substr(4)51、检测浏览器是否支持transformlet isSuportsTransform = false; (function () { if (typeof CSS === 'object' && typeof CSS.supports === 'function') { isSuportsTransform = CSS.supports('transform:scale(1.2)'); } else if('transform' in document.body.style){ isSuportsTransform = true } })();52、获取当前js文件的路径 var getCurrentScript = function (base) { if (document.currentScript) { return document.currentScript.src // FF,Chrome } var stack try { a.b.c() // 强制报错,以便捕获e.stack } catch (e) { // safari的错误对象只有line,sourceId,sourceURL stack = e.stack if (!stack && window.opera) { // opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取 stack = (String(e).match(/of linked script \S+/g) || []).join(' ') } } if (stack) { stack = stack.split(/[@ ]/g).pop() // 取得最后一行,最后一个空格或@之后的部分 stack = stack[0] === '(' ? stack.slice(1, -1) : stack.replace(/\s/, '') // 去掉换行符 return stack.replace(/(:\d+)?:\d+$/i, '') // 去掉行号与或许存在的出错字符起始位置 } var nodes = (base ? document : head).getElementsByTagName('script') // 只在head标签中寻找 for (var i = nodes.length, node; (node = nodes[--i]);) { if (node.readyState === 'interactive') { return node.src } } var node = nodes[nodes.length - 1] return node.hasAttribute ? node.src : node.getAttribute('src', 4) } var jsPath = getCurrentScript(true) var cssPath = jsPath.substr(0, jsPath.lastIndexOf('/')) + '/xxxx.css' // 获取同目录的css文件
2016年05月29日
14 阅读
0 评论
0 点赞