Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

函数类型(一些扩展知识) #24

Open
adodo0829 opened this issue Apr 7, 2020 · 0 comments
Open

函数类型(一些扩展知识) #24

adodo0829 opened this issue Apr 7, 2020 · 0 comments

Comments

@adodo0829
Copy link
Owner

函数类型(一些扩展知识)

函数节流和防抖

在一些DOM事件操作场景中, 函数有可能被非常频繁地调用而造成大的性能问题,此时就需要节流和防抖

常见场景

  1. mousemove事件
    如果要实现一个拖拽功能,需要一路监听 mousemove 事件,在回调中获取元素当前位置,然后重置 dom 的位置来进行样式改变。如果不加以控制,每移动一定像素而触发的回调数量非常惊人,回调中又伴随着 DOM 操作,继而引发浏览器的重排与重绘,性能差的浏览器可能就会直接假死。

  2. window.onresize事件
    为window对象绑定了resize事件,当浏览器窗口大小被拖动而改变的时候,这个事件触发的频率非常之高。如果在window.onresize事件函数里有一些跟DOM节点相关的操作,而跟DOM节点相关的操作往往是非常消耗性能的,这时候浏览器可能就会吃不消而造成卡顿现象

  3. mousedown/keydown 事件(触发就请求接口)

4.搜索联想(keyup事件)

  1. 监听滚动事件判断是否到页面底部自动加载更多(scroll事件)

对于这些情况的解决方案就是函数节流(throttle)或函数去抖(debounce, 本质上其实就是限制某一个方法的频繁触发, 限制频率

函数防抖

函数防抖的原理是将即将被执行的函数用setTimeout延迟一段时间执行,不让其连续触发

function debounce(fn, delay = 1000) {
  let self = fn       // 函数引用
  let timeId = null   // 定时器id
  let isFirst = true  // 是否首次调用
  return function() {
    let args = arguments
    let _this = this
    // 是否首次调用
    if (isFirst) {
      self.apply(_this, arguments)
      isFirst = false
      return
    }
    // 是否延迟执行中
    if (timeId) {
      return
    }
    // 延迟执行
    timeId = setTimeout(() => {
      clearTimeout(timeId)
      timeId = null
      self.apply(_this, args)
    }, delay)
  }
}
window.onresize = debounce(function(){ 
  console.log('hello debounce')
});

函数节流

函数节流使得连续的函数执行,变为固定时间段间断地执行,稀释函数执行的频次

触发事件时,设置一个定时器,再触发事件的时候,如果定时器存在,就不执行,
直到定时器执行,然后执行函数,清空定时器,这样就可以设置下个定时器
function throttle(fn, gap=1000) {
  let timeId, args, _this
  return function() {
    _this = this
    args = arguments
    if (!timeId) {
      timeId = setTimeout(() => {
        timeId = null
        fn.apply(_this, args)
      }, gap)
    }
  }
}
window.onresize = throttle(function(){ 
  console.log('hello throttle')
});

惰性函数(即函数重写)

概念: 函数执行的分支只会在函数第一次调用的时候执行,在第一次调用过程中,该函数会被覆盖为另一个按照合适方式执行的函数,这样任何对原函数的调用就不用再经过执行的分支了

函数重写

在函数内部重新定义函数,函数变量的引用更换

function a() {
  console.log('a1');
  a = function() { // 引用新的函数体
    console.log('a2');
 }
}
a() // a1
a() // a2

使用场景

  • 浏览器能力检测
# 在处理浏览器兼容时,我们通常会判断某些属性来书写不同的函数体
# 想想它有什么弊端? 我们每次调用 addEvent 都会进行 if 判断,
# 如何让它只在第一次调用时判断, 之后的调用不用再判断呢? 重写...
function addEvent(type, element, func) {
    if (element.addEventListener) {
        element.addEventListener(type, func, false);
    }
    else if(element.attachEvent){ // IE
        element.attachEvent('on' + type, func);
    }
    else{
        element['on' + type] = func;
    }
}

// 函数在第一次调用时,该函数会被覆盖为另外一个按合适方式执行的函数
function addEvent(type, element, func) {
    if (element.addEventListener) {
        addEvent = function (type, element, func) {
            element.addEventListener(type, func, false);
        }
    }
    else if(element.attachEvent){
        addEvent = function (type, element, func) {
            element.attachEvent('on' + type, func);
        }
    }
    else{
        addEvent = function (type, element, func) {
            element['on' + type] = func;
        }
    }
    return addEvent(type, element, func);
}

// 在声明函数时就指定适当的函数, 这样也只用判断一次
var addEvent = (function () {
    if (document.addEventListener) {
        return function (type, element, func) {
            element.addEventListener(type, func, false);
        }
    }
    else if (document.attachEvent) {
        return function (type, element, func) {
            element.attachEvent('on' + type, func);
        }
    }
    else {
        return function (type, element, func) {
            element['on' + type] = func;
        }
    }
})()

函数式编程

函数式编程指南
一种编程范式; 我觉得挺有意思的,平时也只是在数组,对象操作那一块用的比较多而已,可以学一些其中的思想. 到时候单独写一个主题来总结...

  • 函数缓存(将函数上次的计算结果缓存起来)
// 返回f()的带有记忆功能的版本
function memorize(f) {
    var cache = {}; // 将值保存到闭包内
    return function() {
        // 将实参转换为字符串形式,并将其用做缓存的键
        var key = arguments.length + Array.prototype.join.call(arguments, ",");
        if(key in cache){
            return cache[key];
        }else{
            return cache[key] = f.apply(this,arguments);
        }
    }
}
memorize()函数创建一个新的对象,这个对象被当作缓存的宿主,并赋值给一个局部变量,
因此对于返回的函数来说它是私有的。
所返回的函数将它的实参数组转换成字符串,并将字符串用做缓存对象的属性名。
如果在缓存中存在这个值,则直接返回它;
否则,就调用既定的函数对实参进行计算,将计算结果缓存起来并返回
var factorial = memorize(function(n){
    return (n<=1) ? 1 : n*factorial(n-1);
});
factorial(5); // 120
  • 单参函数连续调用(返回自己)
# add(1)(2)(3)()的另一种实现
function add(n){
    return function f(m){  
        if(m === undefined){
            return n;
        }else{
            n += m;
            return f;
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant