函数柯里化

函数扁平化

惰性函数

介绍

  • 针对于那些频繁使用的函数
  • 常用于函数库的编写与单例模式中

    开始

    写一个test函数,注意函数首次返回 new Date().getTime() 时间 注意是首次

  • 采用闭包的方式

    1. var test = (function {
    2. var t = new Date().getTime()
    3. return function (){
    4. return t
    5. }
    6. })()

    但是在初始化 test 变量的时候就计算时间 t 了,并不是首次, 那我们解决一下

    1. var test = (function {
    2. var t = new Date().getTime()
    3. return function (){
    4. if(t) {
    5. return t
    6. }
    7. t = new Date().getTime()
    8. return t
    9. }
    10. })()

    这样首次调用 计算时间 解决了,但是当我们多次调用该函数时,我们发现它 每次都会判断 变量 t 是否存在,所以要修改成惰性函数的真实的面目,首次计算 时间 t ,并在接下来的函数调用,都会直接返回计算好的值,不需要加判断 ```javascript // 其实是第一次 调用 test 函数时,拿到时间t, 并修改了函数 test, 并 return 修改后的 test 函数 // 这样接下来每次调用 test 函数,就return 第一次调用函数 计算的时间t了

var test = function(){ var t = new Date().getTime() test = function(){ return t } return test() }

  1. <a name="I3eki"></a>
  2. #### 应用
  3. 理解了惰性函数,那我们开始应用它到实际开发中比如绑定一个事件
  4. - 未采用惰性函数理念开发,如果当做库来使用,或者该函数被频繁调用,肯定是不适合的
  5. ```javascript
  6. // 参数:dom: 给谁绑定事件, type: 事件类型, handler: 事件处理函数
  7. function addEvent(dom, type, handler){
  8. // 判断当前浏览器是否支持 dom.addEventListener() 方法
  9. if(dom.addEventListener){
  10. // false: 代表采用事件冒泡的处理模型, true: 采用事件捕获处理模型
  11. dom.addEventListener(type, handler, false)
  12. } else {
  13. // IE 老版本浏览器
  14. dom.attachEvent('on' + type, handler)
  15. }
  16. }
  • 采用惰性函数开发
    1. // 参数:dom: 给谁绑定事件, type: 事件类型, handler: 事件处理函数
    2. function addEvent(dom, type, handler){
    3. // 判断当前浏览器是否支持 dom.addEventListener() 方法
    4. if(dom.addEventListener){
    5. // false: 代表采用事件冒泡的处理模型, true: 采用事件捕获处理模型
    6. dom.addEventListener(type, handler, false)
    7. // 重点:采用惰性函数理念
    8. addEvent = function(dom, type, handler){
    9. dom.addEventListener(type, handler, false)
    10. }
    11. } else {
    12. // IE 老版本浏览器
    13. dom.attachEvent('on' + type, handler)
    14. // 重点:采用惰性函数理念
    15. addEvent = function(dom, type, handler){
    16. dom.attachEvent('on' + type, handler)
    17. }
    18. }
    19. }

    组合函数

    介绍

    类似下面形式,函数执行最终 从 右 到 左 ```javascript function compose(f, g) { return function (x) { return f(g(x)) } }

function add(x) { var num = 1; return num + x }

function multiply(x) { var num = 2; return num * x }

var res = compose(multiply, add)(1) console.log(res); // 4

  1. <a name="tpibE"></a>
  2. #### 开始
  3. 我们采用 ES5 的写法
  4. ```javascript
  5. function compose(){
  6. var args = Array.prototype.slice.call(arguments) // 将参数 arguments 类数组 变为数组
  7. var len = args.length - 1 // 找到最后一个函数的位置
  8. return function(x){
  9. var res = args[len](x)// 最后一个函数的执行结果
  10. while(len--){ // 将所有传入 compose 的函数全部执行
  11. // 重点
  12. res = args[len](res)
  13. }
  14. return res
  15. }
  16. }

优化
我们发现 res = argslen 都是拿上一次的结果来传给下一个 args[len—],所以 while 去掉,使用reduce
由于,我们调用 args 数组里的值(即函数)是从右到左,所以使用 reduceRight

  1. function compose(){
  2. var args = Array.prototype.slice.call(arguments) // 将参数 arguments 类数组 变为数组
  3. return function(x){
  4. return args.reduceRight((prev, next) => {
  5. return next(prev)
  6. }, x)
  7. }
  8. }

但是我们觉得还是很丑,return return 再 return,那我们采用 箭头函数来写

  1. const compose = (...args) => x => args.reduceRight((prev, next) => next(prev), x)

哇,大功告成,是不是感觉简化了很多

函数式编程—纯函数

定义

  • 对于相同的输入,永远得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态

    作用

  • 在JavaScript中你可以很容易的创建全局变量,这些变量可以在所有函数中访问到,这也是导致bug的常见原因,因为程序中的任何部分都可能是修改全局变量从而导致函数的行为出现异常。

  • 非常容易进行单元测试,因为不需要考虑上下文环境,只需要考虑输入和输出
  • 纯函数是健壮的,改变执行次序不会对系统造成影响,因次纯函数的操作可以并行执行

    开始

    比如我们需要在一个数组中通过一定的条件来过滤元素
    image.png

    函数性能优化—函数记忆

    介绍

  • 执行函数时,将之前函数执行的结果作为缓存,给下一次函数调用使用,比如函数中用到的 递归

  • 设置缓存对象时,为了避免该对象在全局,将使用闭包的方式,因此,可能会造成内存堆积,浏览器无法回收内存,导致程序崩溃,所以,在性能和内存使用上择优

    开始

    1. function memorize(fn){
    2. var cache = {}
    3. return function(){
    4. // 定义一个独一无二的 key,来存value
    5. var key = arguments.length + Array.prototype.join.call(arguments)
    6. if(cache[key]){
    7. return cache[key]
    8. }
    9. cache[key] = fn.apply(this, arguments)
    10. return cache[key]
    11. }
    12. }
    比如计算一个 n!
    注意: n! 和 0!都是1
    1. function factorial(n){
    2. if(n == 0 || n == 1){
    3. return 1
    4. }
    5. return n * factorial(n -1)
    6. }
    7. // 核心函数 memorize
    8. const func = memorize(factorial)
    9. console.log(func(5)) // 首次计算执行时间 2 ms 多
    10. console.log(func(5)) // 使用缓存计算执行时间 0.16 ms

优化页面请求性能—防抖

  1. // 1.鼠标移到 tag 上 发起请求 2.花名搜索
  2. const debounce = (fn, delay) => {
  3. // let timer = null
  4. return function (){
  5. if (timer) {
  6. clearTimeout(timer)
  7. }
  8. timer = setTimeout(() => {
  9. fn.apply(this, arguments)
  10. }, delay)
  11. }
  12. }

优化网络请求性能—节流

  1. //节流 抖音抢优惠券 在指定时间间隔内只触发一次
  2. const throttle = (fn, delay) => {
  3. let startTime = 0
  4. return function () {
  5. const nowTime = new Date().getTime()
  6. if (nowTime - startTime > delay) {
  7. fn.apply(this, arguments)
  8. startTime = nowTime
  9. }
  10. }
  11. }
  12. const tets = (a, b) => {
  13. console.log(a + b)
  14. }
  15. const testThrottle = throttle(test, 10)
  16. for(let i = 0; i < 1000000; i++) {
  17. testThrottle(1,2,3)
  18. }