🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 共享onload事件 ``` function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { oldonload(); func(); } } } ``` ## 在现有的元素后插入一个新元素 ``` function insertAfter(newElement,targetElement){ var parent = targetElement.parentNode; if (parent.lastChild == targetElement) { parent.appendChild(newElement); } else { parent.insertBefore(newElement,targetElement.nextSubling) } } ``` ## 查找下一个元素节点 ``` function getNextElement(node){ if (node.nodeType == 1) { return node ; } if (node.nextSibling) { return getNextElement(node.nextSibling); } return null ; } ``` ## 利用JS去更新某个元素的class属性 ``` function addClass(element,value) { if (!element.className) { element.className = value; } else { newClassName = element.className; newClassName+= " "; newClassName+= value; element.className = newClassName; } } ``` ## ||短路符设置默认值 ``` let person = { name: '张三', age: 38 } let name = person.name || '佚名' ``` ## 字符串拼接使用`${}` ``` let person = { name: 'LiMing', age: 18 } // good function sayHi (person) { console.log(`大家好,我叫${person.name},我今年${person.age}了`) } // best function sayHi ({name, age}) { console.log(`大家好,我叫${name},我今年${age}了`) } ``` ## .函数使用箭头函数 ``` let arr [18, 19, 20, 21, 22] // bad function findStudentByAge (arr, age) { return arr.filter(function (num) { return num === age }) } // good let findStudentByAge = (arr, age)=> arr.filter(num => num === age) ``` ## 函数参数校验 ``` // good let checkoutType = () => { throw new Error('参数不能为空') } let findStudentByAge = (arr, age = checkoutType()) => arr.filter(num => num === age) ``` ## if 判断的优化 ``` let commodity = { phone: '手机', computer: '电脑', television: '电视', gameBoy: '游戏机', } function price(name) { if (name === commodity.phone) { console.log(1999) } else if (name === commodity.computer) { console.log(9999) } else if (name === commodity.television) { console.log(2999) } else if (name === commodity.gameBoy) { console.log(3999) } } price('手机') // 9999 ``` 缺点:代码太长了,维护和阅读都很不友好 好一点的方法:`Switch` ``` let commodity = { phone: '手机', computer: '电脑', television: '电视', gameBoy: '游戏机', } const price = (name) => { switch (name) { case commodity.phone: console.log(1999) break case commodity.computer: console.log(9999) break case commodity.television: console.log(2999) break case commodity.gameBoy: console.log(3999) break } } price('手机') // 9999 ``` 更优的方法: 策略模式 策略模式利用组合、委托和多态等技术和思想,可以有效地避免多重条件选择语句。它提供了对开放—封闭原则的完美支持,将算法封装在独立的 strategy 中,使得它们易于切换,易于理解,易于扩展。 ``` const commodity = new Map([ ['phone', 1999], ['computer', 9999], ['television', 2999], ['gameBoy', 3999], ]) const price = (name) => { return commodity.get(name) } price('phone') // 1999 ``` ## includes 的优化 `includes`是 ES7 新增的 API,与`indexOf`不同的是`includes`直接返回的是`Boolean`值,`indexOf`则 返回的索引值, 数组和字符串都有`includes`方法。 需求:我们来实现一个身份认证方法,通过传入身份 Id 返回对应的验证结果 ``` //old function verifyIdentity(identityId) { if (identityId == 1 || identityId == 2 || identityId == 3 || identityId == 4) { return '你的身份合法,请通行!' } else { return '你的身份不合法' } } //new function verifyIdentity(identityId) { if ([1, 2, 3, 4].includes(identityId)) { return '你的身份合法,请通行!' } else { return '你的身份不合法' } } ``` ## for 循环 在 JavaScript 中,我们可以使用`for()`,`while()`,`for(in)`,`for(of)`几种循环,事实上,这三种循环中`for(in)`的效率极差,因为他需要查询散列键,所以应该尽量少用。 for 循环是最传统的语句,它以变量 i 作为索引,以跟踪访问的位置,对数组进行操作。 ``` var arr = ['a', 'b', 'c'] for (var i = 0, length = arr.length; i < length; i++) { console.log(arr[i]) //结果依次a,b,c } ``` 此时`arr.length`只需要计算一次,优化了性能。 ## 数组去重 利用 ES6 的`Set`方法。 `Set`本身是一个构造函数,用来生成`Set`数据结构。`Set`函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。`Set`对象允许你存储任何类型的值,无论是原始值或者是对象引用。它类似于数组,但是成员的值都是唯一的,没有重复的值。 ``` function unique4(arr) { return [...new Set(arr)] } console.log(unique4([1, 1, 2, 3, 5, 3, 1, 5, 6, 7, 4])) // [1, 2, 3, 5, 6, 7, 4] ``` ## 箭头函数 箭头函数表达式的语法比函数表达式更简洁。所以在开发中更推荐使用箭头函数。特别是在`vue`项目中,使用箭头函数不需要在更`this`重新赋一个变量。 ``` // old 使用functions var arr = [5, 3, 2, 9, 1] var arrFunc = arr.map(function (x) { return x * x }) console.log(arrFunc) // 使用箭头函数 var arr = [5, 3, 2, 9, 1] var arrFunc = arr.map((x) => x * x) ``` 要注意的是,箭头函数不绑定`arguments`,取而代之用`rest`参数…解决。 ``` // 不能使用 arguments let fun1 = (b) => { console.log(arguments) } fun1(2, 92, 32, 32) // Uncaught ReferenceError: arguments is not defined // 使用rest 参数 let fun2 = (...c) => { console.log(c) } fun2(3, 82, 32, 11323) // [3, 82, 32, 11323] ``` ## Dom 的创建 创建多个 dom 元素时,先将元素`append`到`DocumentFragment`中,最后统一将`DocumentFragment`添加到页面。 常规方法; ``` for (var i = 0; i < 1000; i++) { var el = document.createElement('p') el.innerHTML = i document.body.appendChild(el) } ``` 使用`DocumentFragment`优化多次`append` ``` var frag = document.createDocumentFragment() for (var i = 0; i < 1000; i++) { var el = document.createElement('p') el.innerHTML = i frag.appendChild(el) } document.body.appendChild(frag) ``` 更优的方法:使用一次`innerHTML`赋值代替构建 dom 元素 ``` ar html = [] for (var i = 0; i < 1000; i++) { html.push('<p>' + i + '</p>') } document.body.innerHTML = html.join('') ``` ## 防抖与节流 在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如`resize`、`scroll`、`mousemove`等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。这时候就用到防抖与节流。 案例 1:远程搜索时需要通过接口动态的获取数据,若是每次用户输入都接口请求,是浪费带宽和性能的。 ``` <Select :remote-method="remoteMethod"> <Option v-for="item in temoteList" :value="item.value" :key="item.id">{{item.label}}</Option> </Select> <script> function debounce(fn, wait) { let timeout = null return function () { if (timeout !== null) clearTimeout(timeout) timeout = setTimeout(fn, wait) } } export default { methods:{ remoteMethod:debounce(function (query) { // to do ... }, 200), } } <script> ``` 案例 2:持续触发`scroll`事件时,并不立即执行`handle`函数,当 1000 毫秒内没有触发`scroll`事件时,才会延时触发一次`handle`函数。 ``` function debounce(fn, wait) { let timeout = null return function () { if (timeout !== null) clearTimeout(timeout) timeout = setTimeout(fn, wait) } } function handle() { console.log(Math.random()) } window.addEventListener('scroll', debounce(handle, 1000)) ``` ***** ## 异步加载 js 默认情况下,浏览器是同步加载 js 脚本,解析 html 过程中,遇到`<script>`标签就会停下来,等脚本下载、解析、执行完后,再继续向下解析渲染。 如果 js 文件体积比较大,下载时间就会很长,容易造成浏览器堵塞,浏览器页面会呈现出“白屏”效果,用户会感觉浏览器“卡死了”,没有响应。此时,我们可以让 js 脚本异步加载、执行。 ``` <script src="path/to/home.js" defer></script> <script src="path/to/home.js" async></script> ``` 上面代码中,`<script>`标签分别有`defer`和`async`属性,浏览器识别到这 2 个属性时 js 就会异步加载。也就是说,浏览器不会等待这个脚本下载、执行完毕后再向后执行,而是直接继续向后执行 defer 与 async 区别: * defer:DOM 结构完全生成,以及其他脚本执行完成,才会执行(渲染完再执行)。有多个`defer`脚本时,会按照页面出现的顺序依次加载、执行。 * async:一旦下载完成,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染(下载完就执行)。有多个`async`脚本时,不能保证按照页面出现顺序加载、执行 ## import() ~~~js const btn = document.getElementById("btn"); btn.onclick = () => { import("./hello.js").then(module => { module.hello(); }); }; ~~~ ## 可选链操作符(?.) ~~~js const data = { name: "张三", age: 18, sex: "男" }; console.log(data?.friend?.name); ~~~ ## 空值合并运算符(??) ~~~js let a = 0; let b = a || "aaa"; let c = a ?? "aaa"; console.log("b的值是 " + b);//b的值是 aaa console.log("c的值是 " + c);//c的值是 0 ~~~ ## 箭头函数 箭头函数看起来会更加的简洁,因为它允许你使用更短的语法来书写函数: ~~~js const myFunction = function() { //... } ~~~ 到 ~~~js const myFunction = () => { //... } ~~~ 如果函数体中只包含一条语句,你甚至可以省略大括号并直接书写这条语句: ~~~js const myFunction = () => doSomething() ~~~ 参数在括号中传递: ~~~js const myFunction = (param1, param2) => doSomething(param1, param2) ~~~ 如果该函数**只有一个**参数,那么可以省略掉括号: ~~~js const myFunction = param => doSomething(param) ~~~ 由于这种简短的语法,使得我们可以更便捷的使用**比较简短的函数** ## Using `let` `let` 是ES2015中引入的新功能,它本质上是具有块级作用域的 `var` 。它可以被当前作用域(函数以及块级作用域)以及子级作用域访问到。 现代 JavaScript 开发者在 `let` 和 `var` 的选择中可能会更倾向于前者。 ## Using `const` 使用变量 `var` 或 `let` 声明的变量可以被重新赋值。 使用 `const` 声明的变量一经初始化,它的值就永远不能再改变,即不可重新被赋值。 ~~~js const a = 'test' ~~~ 我们不能再为 `a` 进行赋值操作。然而,`a` 如果它是一个具有属性或者方法的对象,那么我们可以改变它的属性或者方法。 `const` 并不意味着具有不可变性,只是保证用 `const` 声明的变量的引用地址不被变更。 ## 隐式返回 箭头函数支持隐式返回:可以正常的 `return` 一个返回值但是可以不使用 `return` 关键字。 隐式返回只在函数体内只包含一条语句的情况下生效: ~~~js const myFunction = () => 'test' myFunction() //'test' ~~~ 需要注意的一种情况,当返回一个对象时,记得将大括号括在括号中以避免产生歧义,误将其(大括号)解析为函数体的大括号。 ~~~js const myFunction = () => ({ value: 'test' }) myFunction() //{value: 'test'} ~~~ ## class 定义 如下是一个 class 的例子: ~~~js class Person { constructor(name) { this.name = name } hello() { return 'Hello, I am ' + this.name + '.' } } ~~~ 在这个类的对象实例上调用: ~~~js const flavio = new Person('Flavio') flavio.hello() ~~~ ## Class 继承 一个子类可以 extend 另一个类,通过子类实例化出来的对象可以继承这两个类的所有方法。 如果子类中的方法与父类中的方法名重复,那么子类中的同名方法优先级更高: ~~~js class Programmer extends Person { hello() { return super.hello() + ' I am a programmer.' } } const flavio = new Programmer('Flavio') flavio.hello() ~~~ ## 静态方法 在类中,通常会把方法直接挂载到实例对象上,直接在实例对象上调用。 而静态方法则是直接使用类名来调用,而不是通过对象实例调用: ~~~js class Person { static genericHello() { return 'Hello' } } Person.genericHello() //Hello ~~~ ## For-of循环 2009年的ES5引入了`forEach()`循环,虽然很好用,但是它跟`for`循环不一样,没法break。 ES2015引入了`**for-of**` **循环**,就是在`forEach`的基础上加上了break的功能: ~~~js //iterate over the value for (const v of ['a', 'b', 'c']) { console.log(v); } //get the index as well, using `entries()` for (const [i, v] of ['a', 'b', 'c'].entries()) { console.log(index) //index console.log(value) //value } ~~~ ## 创建一个promise Promise API暴露了一个Promise构造函数,可以通过`new Promise()`来初始化: ~~~js let done = true const isItDoneYet = new Promise((resolve, reject) => { if (done) { const workDone = 'Here is the thing I built' resolve(workDone) } else { const why = 'Still working on something else' reject(why) } }) ~~~ ## 使用一个promise 上面讲了怎么创建一个promise,下面就讲怎么使用(consume)这个promise。 ~~~js const isItDoneYet = new Promise() //... const checkIfItsDone = () => { isItDoneYet .then(ok => { console.log(ok) }) .catch(err => { console.error(err) }) } ~~~ ## 链式promise的例子 Fetch API ~~~js const status = response => { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response) } return Promise.reject(new Error(response.statusText)) } const json = response => response.json() fetch('/todos.json') .then(status) .then(json) .then(data => { console.log('Request succeeded with JSON response', data) }) .catch(error => { console.log('Request failed', error) }) ~~~ 检查日期是否为工作日 ``` const isWeekday = (date) => date.getDay() % 6 !== 0; ``` 反转字符串 ``` const reverse = (str) => str.split('').reverse().join(''); console.log(reverse('hello world'));; ``` 检查当前 Tab 页是否在前台 ``` ~~~ const isBrowserTabInView = () => document.hidden; console.log(isBrowserTabInView()); ~~~ ``` 保留小数点(非四舍五入) ``` const toFixed = (n, fixed) => ~~(Math.pow(10, fixed) * n) / Math.pow(10, fixed); // Examples a=toFixed(25.198726354, 1); ``` 滚动到页面顶部 ``` const goToTop = () => window.scrollTo(0, 0); ``` 获取Cookie ~~~js const cookie = name => `; ${document.cookie}`.split(`; ${name}=`).pop().split(';').shift(); cookie('_ga'); ~~~ ~~~text /** * 将字节转换为合理的容量单位 * @param {number} bytes Bytes in Number */ const humanFileSize = (bytes) => { let BYTES = bytes; const thresh = 1024; if (Math.abs(BYTES) < thresh) { return `${BYTES} B`; } const units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; let u = -1; const r = 10 ** 1; do { BYTES /= thresh; u += 1; } while (Math.round(Math.abs(BYTES) * r) / r >= thresh && u < units.length - 1); return `${BYTES.toFixed(1)} ${units[u]}`; }; /** * storage具有过期时间 * Caching values with expiry date to the LocalHost. * @param {string} key Local Storage Key * @param {any} value Local Storage Value * @param {number} ttl Time to live (Expiry Date in MS) */ const setLocalItem = (key, value, ttl = duration.month) => { const now = new Date(); // `item` is an object which contains the original value // as well as the time when it's supposed to expire const item = { value, expiry: now.getTime() + ttl, }; localStorage.setItem(key, JSON.stringify(item)); }; /** * storage具有过期时间 * Getting values with expiry date from LocalHost that stored with `setLocalItem`. * @param {string} key Local Storage Key */ const getLocalItem = (key) => { const itemStr = localStorage.getItem(key); // if the item doesn't exist, return null if (!itemStr) { return null; } const item = JSON.parse(itemStr); const now = new Date(); // compare the expiry time of the item with the current time if (now.getTime() > item.expiry) { // If the item is expired, delete the item from storage // and return null localStorage.removeItem(key); return null; } return item.value; }; ~~~