【重学】函数柯里化,偏函数

大纲:

  1. 函数柯里化
  2. 偏函数
  3. 正则截取query部分
  4. ts

前置知识:

  1. 函数的参数
  • 函数的 length 属性,返回函数预期的参数个数 ---------------------- (形参)
  • 函数内的 arguments对象 包含了函数运行时的所有参数 ---------(实参)
  1. 类数组对象转换成数组
  • [].slice.call(类似数组的对象)
  • [].slice.apply(类似数组的对象)
  • Array.prototype.slice.call(类似数组的对象)
  • Array.from()
  1. Function.prototype.apply()
  • apply的第一个参数是要绑定的对象,当是null和undefined时,指的是全局对象window
  1. concat ------------------- concat不会改变原数组
  • concat用于多个数组的拼接,将新数组的成员拼接到旧数组的尾部,返回一个新数组
  • 注意:concat不会改变原数组
  1. 偏函数和函数柯里化概念
    偏函数:是固定一个或多个参数,产生另一个较小元的函数 n元函数 => 转换成n-x元函数
    柯里化:将多个参数函数转化成单一参数函数------------------n元函数 => 转换成n个一元函数
  • 偏函数(partial application)partial:是部分的意思
  • 柯里化 currying
  1. rest参数
  • rest参数: ...变量名 用来获取函数的多余参数
  • ...变量名 变量名是一个数组

(1)函数柯里化

  • 函数柯里化:就是将一个多个参数的函数,转化成一系列一个参数的函数的过程
  • 柯里化, 即 Currying 的音译。
function x(a,b,c) {
    console.log([].slice.call(arguments))
    console.log([].slice.apply(arguments))
    console.log(Array.prototype.slice.apply(arguments))
        console.log(Array.from(arguments))
}
x(1,2,3,4,5)
x.length --------------------------- 函数形参的个数 3
x内部的arguments.length -------------------- 函数的实参的集合 5

柯里化阶段一

需求:将add(a,b,c) 转化成 curryAdd(a)(b)(c)
缺点:只能处理三个参数的情况,不能处理任意多个参数的情况

function add(a,b,c) {
  return a+b+c
}

function curryAdd(a) {
  return function(b) {
    return function(c) {
      return a+b+c
    }
  }
}

柯里化阶段二

需求:处理任意多个参数相加
缺点: 
  1. 处理相加逻辑的代码,只是在没有参数时才会执行,其他部分都在处理怎么收集所有参数
  2. 所以出来相加逻辑的代码,可以通过传参的形式来调用,执行相加逻辑

function curryAdd() {
  let params_arr = [] //-------------------- 定义一个用于收集参数的数组
  const closure = function() { // ------------ 定义一个闭包
    const args = Array.prototype.slice.apply(arguments) //------------ 收集每次调用的参数,一次可能有多个
    if (args.length) {
       params_arr = params_arr.concat(args)
       // 用concat是因为可以一次调用,一次传了多个参数
       // 注意concat不会改变原数组,返回值是拼接过后的新数组
       return closure
     } // 如果参数存在,就收集到数组,并且返回闭包,继续判断存不存在参数
     params_arr.reduce((total, current) => total + current) // 如果参数已经不存在了,就相加所有尺寸参数
  }
  return closure // -------------------------- 返回一个闭包
}

const fn = curryAdd()
const res = fn(1)(2)(3)(4)() // 10-------------注意需要最后一个调用一个空参数的闭包
const res2 = fn(1)(2)(3)(4, 10)() // 20

柯里化阶段三

缺点:
  1. 函数中在判断是否传入参数,没有了再执行相加
  2. 也就是说,最后一次一定要执行一次空参数的回调
  3. 所以更合理的方式:就是通过函数可以接收参数的总数来判断


function add(arr) {
  return arr.reduce((total, current) => total + current)
}

    function curryAdd(fnAdd) {
      let params_arr = [] //-------------------- 定义一个用于收集参数的数组
      const closure = function(...args) { // args是一个数组
        if (args.length) {
          params_arr = params_arr.concat(args) // 用concat是因为每次调用,都可以传多个参数
          return closure
        } // 如果参数存在,就收集到数组,并且返回闭包,继续判断存不存在参数

        // 如果参数不存在,执行传入的函数
        return fnAdd(params_arr)
      }
      return closure // -------------------------- 返回一个闭包
    }

const go = curryAdd(add)
const res = go(1)(2,3)(4)()
console.log(res, 'res')

柯里化阶段四

    function add(x,y,z,m) {
      return x+y+z+m
    }

    function curryAdd (fnAdd) {
      let params_arr = []
      const maxLength = fnAdd.length // 形参的长度
      console.log(maxLength, 'maxLength')

      const closure = function () {
        params_arr = params_arr.concat([].slice.apply(arguments))
        // 执行一次闭包就收集一个参数,而不是判断完再收集
        if (params_arr.length < maxLength) {
         // 如果参数数组中收集到的参数长度 小于 函数形参的参数长度,执行闭包
          return closure
        }
        return fnAdd(...params_arr)
      }

      return closure
    }

    const go = curryAdd(add)
    const res = go(1,2)(3,4)
    console.log(res) //10

https://juejin.im/post/5c677041f265da2de25b7707

https://www.jianshu.com/p/fe0d80b04129

https://juejin.im/post/5c932a556fb9a070cd56998e

https://juejin.im/post/5c619de8f265da2d8a559131







(2) 偏函数

  • 将一个或者多个参数固定到一个函数,产生一个更小元的函数
function add (a, b) {
  return a + b
}
function partial (fn) {...}

const addPartial = partial(add, 1)  // ------------------ 实现固定一部分参数1
const res = addPartial(2) // 3 -------------------------- 只传一部分参数 2

偏函数实现方式一

  • bind方法
    function add (a, b) {
      return a + b
    }
    function partial () {
      let args = Array.prototype.slice.call(arguments)
      const fn = args.shift()
      return fn.bind(this, ...args)  
     // ----------- bind方法,返回新的函数
     // ----------- 除了第一个参数,后面的参数,是传给fn的参数
    }
    const addPartial = partial(add, 1)
    const res = addPartial(2) // 除了固定的参数,剩下的参数在这里传入
    console.log(res, 'res') // 3

偏函数实现方法二

    function add (a, b, c) {
      return a + b + c
    }

    function partial(fn) {
      let params = Array.prototype.slice.call(arguments, 1) // 拿到除取函数外的其他所有参数,是一个数组

      const closure = function() {
        const currentParams =  Array.prototype.slice.call(arguments) // 每次闭包接收的参数
        params = params.concat(currentParams) // 传入收集参数的数组中
        console.log(params)
        if ( params.length < fn.length ) { // 参数收集数组长度 如果 小于add函数预期参数个数时,继续收集
          return closure
        }
        return fn.apply(null, params) // 如果等于add预期参数时,停止收集参数,执行add函数
      }
      return closure
    }
    const addPratial = partial(add, 2)
    const res = addPratial(3)(4)
    console.log(res, 'res') // 9

https://juejin.im/post/5993a7ea6fb9a0247f4f2d08

正则截取query部分

const str = 'https://www.jianshu.com/u/70c8a3b8bb44?name=wu&age=20'

const slice = str.match(/([^?&=]+)=([^?&=]+)/g)
// 全局匹配
// ([^?&=]+) 除去? & = 外的一个或者多个字符

console.log(slice) // ["name=wang", "age=20"]
const str = 'http://www.baidu.com/?name=wang&age=20&sex=man#'
const res = str.match(/\?(.+)#/)[1].match(/[^&#]+=[^&#]+/g)
console.log(res)






TS

数组

数组的两种表示方法

let list: number[] = [1, 2, 3];

let list: Array<number> = [1, 2, 3]; -------------- 数组泛型 Array<类型变量>

null 和 undefined

  • null 和 undefined 是所有类型的子类型
  • 当指定了 --strictNullChecks标记,null和undefined就只能赋值给void和他们各自

never

  • never类型是任何类型的子类型

类型断言 ----------------------------- 相当于类型转换

  • 类型断言有两种形式
    • 尖括号语法
    • as语法
1. as语法 ---- 在jsx中只能使用 as

let someValue: any = "this is a string"; -------------------------- 本来 someValue 是 any类型的 变量 
let strLength: number = (someValue as string).length; ------------- 被断言成了字符串类型

接口

  • ReadonlyArray<T> ------------ 此数组不能被修改,且类型是T类型的数组
  • Array<T> ------------------------- 数组泛型
  • readonly和const的区别
    • const 用于变量
    • readonly 用于属性

函数类型

interface SearchFunc {
  (source: string, subString: string): boolean;   --------------- 函数类型,包含参数列表和返回值
}

  • private 不能在声明他的类的外部访问
  • 参数属性:通过给构造函数参数前面添加一个访问限制符来声明
    • 比如:使用 private 限制一个参数属性,会声明并初始化一个私有成员
  • abstract:是抽象的意思

类型谓词

  • parameterName is Type,parameterName必须来自当函数签名里的一个参数名

typeof类型保护

类型别名

  • 类型别名会给类型取一个新名字
  • 类型别名和接口很像,当可以作用域原始值,联合类型,元组,和任何其他任何你需要手写的类型
  • 类型别名不会新建一个类型,而是创建了一个名字,来引用那个类型
  • 同接口一个,类型别名也可以是泛型
  • 可以在类型别名的属性中引用自己
type ------------------------------------------- 声明一个类型别名


type Name = string;  ---------------------- 类型别名可以作用于原始类型,联合类型,元组,或者其他任何需要手写的类型
type NameResolver = () => string;  -------- 函数类型
type NameOrResolver = Name | NameResolver; ------- 联合类型
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') { ----------------- typeof类型保护
        return n;
    }
    else {
        return n();
    }
}

type Container<T> = { value: T }; ----------- 类型别名也可以是泛型


接口 vs 类型别名

  • 接口创建了一个姓名字,在任何地方都可以使用,类型别名不创建新名字(错误信息就不使用别名)
  • 类型别名不能被 extends 和 implements
  • 如果你无法通过接口来描述一个类型,并且你需要使用联合类型或者元组类型,通常会使用类型别名

索引类型

  • 索引类型查询操作符,索引访问操作符
    • 索引类型查询操作符:keyof T 的结果表示:T上已知公共属性名的联合
    • 索引访问操作符:T[K] ------- 就是T中K属性对应的类型

映射类型

  • 从就类型中创建新类型的方式
    • Keys:是已经存在的联合类型
    • k内部会循环keys中的属性
    • 最后是:属性的结果类型
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容