高阶函数

高阶函数

函数柯里化

函数柯里化,又称部分求值。一个currying函数首先会接收一些参数,接受这些参数后该函数不会立即求值。而是会将传入的参数在函数内保存,待函数真正需要求值时,之前的所有参数都会被一次性用于求值

非柯里化

var mothlyCost = 0;

 var cost = function(money:number) {
   mothlyCost += money;
 }

 cost(100);
 cost(200);
 cost(300);
 cost(400);

 console.log(mothlyCost);

柯里化实现

function sum(args: Array<number>) {
  // 没有这个参数,就开始计算结果返回
  let count = 0;

  args.forEach(item => {
    count += item;
  })

  return count;
}

// 简单柯里化
var curringCost = function(): any {
  let args:Array<number> = []; 

  return function(money?: number){
      if(money) {
        // 如果有这个参数,就讲数据添加到参数数组中
        args.push(money);
      } else {
        return sum(args);
      }
  }
}

// 先初始化
let testCost = curringCost();
testCost(100);
testCost(200);
testCost(300);
testCost(400);
console.log(testCost());

最后我们来给写一个函数,让其可以未其他函数提供柯里化的能力

套壳柯里化

// 柯里化外套
let comCurring = function(fn:Function){
   var args:Array<any> = [];

   return function(money?: number){

     if(arguments.length > 0){
       // 有参数,保存,不做其他操作
       args.push(...arguments);
     } else {
       // 无参数,使用参入fn对数据处理
       return fn.apply(this, args);
     }
   }
}

// 先初始化
let sumCost = comCurring(sum);
sumCost(100);
sumCost(200);
sumCost(300);
sumCost(400);
console.log(sumCost());

反柯里化

interface Function {
  uncurrying: () => (obj:any, number: any) => {};
} 

Function.prototype.uncurrying = function(){
      // 将this指针保留下来
      let self = this;

      // 返回一个匿名函数被“push”接收了
      return function() {
        /**
         * push(currentObj, 5)
         * 获取第一个参数,也就是需要执行的对象(currentObj)
         * 这个时候arguments就只剩下“5”这个变量了
         */
        let obj = Array.prototype.shift.call(arguments);

        /**
         * 执行这个方法,注意这个方法是由谁调用的
         * 在这个案例中,这个匿名函数是被Array的push方法调用的
         * 所以这里的self为push这个方法
         */
        return self.apply(obj, arguments);
      }
  }

  // 提取push方法
  const push  = Array.prototype.push.uncurrying();
  let currentObj = {
    0 : 1,
    length : 1
  }

  push(currentObj, 5);
  console.log(currentObj)

分时函数

/**
 * 分时函数
 * 将数据分批操作
 * 例子:渲染1000个好友
 */
function timeSharing<T>(arr:Array<T>, fn:Function,  count:number, time:number) {

    function strat(){
      let index = 0,
          len = Math.min(arr.length, count);
      for (; index < len; index++) {
        const element = arr.shift();
        fn(element);
      }
    }

    let timer = setInterval(() => {
      if(arr.length === 0) {
        return clearInterval(timer);
      }
      strat();
    }, time)
}

let arr = [];
for (let index = 0; index < 1000; index++) {
   arr.push(index);
}
timeSharing(arr, function(num: number){
  console.log(num)
}, 10, 200);

惰性加载

其实我觉得这个还是比较勤快,也许是聪明的懒惰。如果方法里有判断,就先加载方法,后面就不需要再进行判断了,下面模拟 浏览器嗅探

  • if A浏览器 使用 aTest方法

  • if B浏览器 使用 bTest方法

公共代码

// 全局控制变量,模拟不同浏览器
const GLOBAL_CONTROL = "A";

function aTest(){
  console.log("A浏览器中使用")
}

function bTest(){
  console.log("B浏览器中使用")
}

基础版

/**
 * 简版
 * 需要使用if判断,每次执行都需要
 */
function compatibleFunction() {
  if(GLOBAL_CONTROL === "A") {
    aTest();
  } else {
    bTest();
  }
}
compatibleFunction();

console.log(compatibleFunction)

先行版

/**
 * 惰性加载函数 先行版
 * 避免了每次判断,只需要开始的时候执行一遍即可
 * 但是如果项目中没有用到,那这个方法就累赘并且还占用了启动时间
 */
let lazyCompatibleBefore = (function() {
  if(GLOBAL_CONTROL === "A") {
    return aTest;
  } else {
    return bTest;
  }
})()
lazyCompatibleBefore();
console.log(lazyCompatibleBefore)

懒加载版

/**
 * 惰性加载函数 懒加载版
 * 避免了每次判断,执行一遍完第一遍后就是以后都是正常方法
 * 中间将方法替换为正确版本的方法
 * 最后记得再执行一遍,不然第一次就相当于只做了替换
 */
let lazyCompatibleRuntime = function() {

  if(GLOBAL_CONTROL === "A") {
    lazyCompatibleRuntime = aTest;
  } else {
    lazyCompatibleRuntime = bTest;
  }

  lazyCompatibleRuntime();
}

console.log(lazyCompatibleRuntime)
lazyCompatibleRuntime();
console.log(lazyCompatibleRuntime)

节流函数

/**
 * 节流函数
 * 通用的节流函数
 * @param fn       需要节流的函数
 * @param time     节流时间
 * @param isFrist  第一次是否立即执行
 */
function throttlingWrapper(fn:Function, time:any, isFrist = true) {
  var timer:any = null,
      _self = fn; 

  let throtting = () => {
    let _me:any = this;

    // 如果timer有值,也就是还在执行中,则直接返回
    if(timer){
      return;
    } 

    // 是否首次触发
    if(isFrist){
      _self.apply(_me, arguments);
      isFrist = false;
      return;
    }

    // 节流函数本体
    timer = setTimeout(() => {
      _self.apply(_me, arguments);
      clearTimeout(timer);
      timer = null
    }, time)
  } 

  return throtting;
}
(0)

相关推荐

  • 「学习笔记」JavaScript基础

    前言 最近一直在跟着黑马教程学习JavaScript内容,遂把这一阶段的学习内容整理成笔记,巩固所学知识,同时也会参考一些博客,书籍上的内容,查漏补缺,给自己充充电

  • 函数的两种创建自定义方式

    函数的两种创建自定义方式

  • 前端面试题整理——手写bind函数

    var arr = [1,2,3,4,5] console.log(arr.slice(1,4)) console.log(arr) Function.prototype.bind1 = functi ...

  • 从一个超时程序的设计聊聊定时器的方方面面

    目录 如何设计一个靠谱的超时程序 JS引擎的运行机制是怎样的? 如何避免程序卡顿? 如何判断H5程序是从后台台恢复过来的? 如何理解定时器的丢弃行为? 在开发中如何选择使用合适的定时器? 有没有一键回 ...

  • 函数之间可以相互调用

    函数之间是可以相互调用的,把一个函数作为参数传递给另一个函数. function fn1() { console.log(111); fn2(); console.log('fn1'); } func ...

  • JavaScript设计模式之观察者模式

    目录 简介 实现 创建观察者对象 简介 观察者模式由称作发布-订阅者模式或消息机制,该模式定义一种依赖关系,旨在解决主体对象与观察者之间功能的耦合. 例如案例:想实现一个评论模块,当用户发送消息时,在 ...

  • 17K star 仓库,解决 90% 的大厂基础面试题

    前言 笔者开源的前端进阶之道已有三年之久,至今也有 17k star,承蒙各位读者垂爱.在当下部分内容已经略微过时,因此决定提笔翻新内容. 翻新后的内容会全部集合在「干爆前端」中,有兴趣的读者可以前往 ...

  • this 的值到底是什么?一次说清楚

    你可能遇到过这样的 JS 面试题: var obj = { foo: function(){ console.log(this) }}var bar = obj.fooobj.foo() // 打印出 ...

  • 这几种前端JavaScript方法封装你知道多少

    这几种前端JavaScript方法封装你知道多少

  • JavaScript--总结三(数组和函数)

    数组 数组的概念: 将多个元素(通常是同一类型)按照一定顺序排列放到一个集合中,这个集合称之为数组---简(一组有序的数据) 数组的作用:可以一次性存储多个数据 数组的定义: 1.通过构造函数创建数组 ...