🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 第二部分总结(专题系列) ## 防抖与节流 防抖和节流的作用都是防止函数多次调用,区别在于,假设一个用户一直触发这个函数,且每次触发函数的时间间隔小于wait,防抖的情况下只会调用一次,而节流的情况会每隔一定时间调用函数 ~~~ // 防抖 function debounce(func, wait, immediate) { var timeout, result; var debounced = function () { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { // 如果已经执行过,不再执行 var callNow = !timeout; timeout = setTimeout(function(){ timeout = null; }, wait) if (callNow) result = func.apply(context, args) } else { timeout = setTimeout(function(){ func.apply(context, args) }, wait); } return result; }; debounced.cancel = function() { clearTimeout(timeout); timeout = null; }; return debounced; } ~~~ 这个防抖函数和最普通的防抖函数相比关注了以下几个需求: - this 和 event 的指向都要正确 - 允许立即执行 - 能返回函数的执行结果 - 能取消然后重新触发 ~~~ // 节流 function throttle(func, wait) { var timeout; return function() { context = this; args = arguments; if (!timeout) { timeout = setTimeout(function(){ timeout = null; }, wait) // 立即执行 func.apply(context, args) } } } ~~~ 这样实现的节流函数满足了这样一个需求:鼠标移入能立刻执行,停止触发的时候还能再执行一次(假设事件处理程序是 onmousemove) ## 数组去重 尝试写一个名为 unique 的工具函数,我们根据一个参数 isSorted 判断传入的数组是否是已排序的,如果为 true,我们就判断相邻元素是否相同,如果为 false,我们就使用 indexOf 进行判断 ~~~ var array1 = [1, 2, '1', 2, 1]; var array2 = [1, 1, '1', 2, 2]; // unique 工具函数用于数组去重 function unique(array, isSorted) { var res = []; var seen = []; for (var i = 0, len = array.length; i < len; i++) { var value = array[i]; if (isSorted) { // 如果数组是排序过的,判断当前元素与前一个元素是否相等 if (!i || seen !== value) { res.push(value) } seen = value; } else if (res.indexOf(value) === -1) { // 如果数组是未排序的,使用 indexOf 方法 res.push(value); } } return res; } console.log(unique(array1)); // [1, 2, "1"] console.log(unique(array2, true)); // [1, "1", 2] ~~~ 在数组中元素有一些特殊类型如对象、NaN的情况下,不同的数组去重方法可能会有不同的返回值 ~~~js // demo1 var arr = [1, 2, NaN]; arr.indexOf(NaN); // -1 ~~~ indexOf 底层还是使用 === 进行判断,因为 NaN === NaN的结果为 false,所以使用 indexOf 查找不到 NaN 元素 ~~~js // demo2 function unique(array) { return Array.from(new Set(array)); } console.log(unique([NaN, NaN])) // [NaN] ~~~ Set 认为尽管 NaN === NaN 为 false,但是这两个元素是重复的。 ## 快速获取数组的最大值和最小值 使用 `Math.max()`函数需要注意的问题: 1. 如果有任一参数不能被转换为数值,则结果为 NaN。 2. max 是 Math 的静态方法,所以应该像这样使用:Math.max(),而不是作为 Math 实例的方法 (简单的来说,就是不使用 new ) 3. 如果没有参数,则结果为`-Infinity`(注意是负无穷大) 1.如果任一参数不能被转换为数值,这就意味着如果参数可以被转换成数字,就是可以进行比较的,比如: ~~~js Math.max(true, 0) // 1 Math.max(true, '2', null) // 2 Math.max(1, undefined) // NaN Math.max(1, {}) // NaN ~~~ 2.如果没有参数,则结果为 -Infinity,对应的,Math.min 函数,如果没有参数,则结果为 Infinity,所以: ``` var min = Math.min(); var max = Math.max(); console.log(min > max); ``` 结合ES6的扩展运算符使用 ``` var arr = [6, 4, 1, 8, 2, 11, 23]; console.log(Math.max(...arr)) ``` ## 惰性函数 我们看下面的这个例子,在 DOM 中添加事件时需要兼容现代浏览器和 IE 浏览器(IE < 9),方法就是对浏览器环境进行判断,看浏览器是否支持,简化写法如下。 ~~~js // 简化写法 function addEvent (type, el, fn, capture = false) { if (window.addEventListener) { el.addEventListener(type, fn, capture); } else if(window.attachEvent){ el.attachEvent('on' + type, fn); } } ~~~ 但是这种写法有一个问题,就是每次添加事件都会调用做一次判断,那么有没有什么办法只判断一次呢?可以利用闭包和立即调用函数表达式(IIFE)来处理。 ~~~js const addEvent = (function(){ if (window.addEventListener) { return function (type, el, fn, capture) { el.addEventListener(type, fn, capture); } } else if(window.attachEvent){ return function (type, el, fn) { el.attachEvent('on' + type, fn); } } })(); ~~~ 也可以使用惰性函数来处理 ~~~ function addEvent (type, el, fn, capture = false) { // 重写函数 if (window.addEventListener) { addEvent = function (type, el, fn, capture) { el.addEventListener(type, fn, capture); } } else if(window.attachEvent){ addEvent = function (type, el, fn) { el.attachEvent('on' + type, fn); } } addEvent(type, el, fn, capture); // 这一步是关键 } ~~~ 第一次调用`addEvent`函数后,会进行一次环境判断,在这之后`addEvent`函数被重写,所以下次调用时就不会再次判断环境。