大纲:
- 函数柯里化
- 偏函数
- 正则截取query部分
- ts
前置知识:
- 函数的参数
- 函数的 length 属性,返回函数预期的参数个数 ---------------------- (形参)
- 函数内的 arguments对象 包含了函数运行时的所有参数 ---------(实参)
- 类数组对象转换成数组
- [].slice.call(类似数组的对象)
- [].slice.apply(类似数组的对象)
- Array.prototype.slice.call(类似数组的对象)
- Array.from()
- Function.prototype.apply()
- apply的第一个参数是要绑定的对象,当是null和undefined时,指的是全局对象window
- concat ------------------- concat不会改变原数组
- concat用于多个数组的拼接,将新数组的成员拼接到旧数组的尾部,返回一个新数组
- 注意:concat不会改变原数组
- 偏函数和函数柯里化概念
偏函数:是固定一个或多个参数,产生另一个较小元的函数n元函数 => 转换成n-x元函数
柯里化:将多个参数函数转化成单一参数函数------------------n元函数 => 转换成n个一元函数
- 偏函数(partial application)
partial:是部分的意思
- 柯里化
currying
- 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 = [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类型的数组 - Array
------------------------- 数组泛型 - 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 = { value: T }; ----------- 类型别名也可以是泛型
接口 vs 类型别名
- 接口创建了一个姓名字,在任何地方都可以使用,类型别名不创建新名字(错误信息就不使用别名)
- 类型别名不能被 extends 和 implements
- 如果你无法通过接口来描述一个类型,并且你需要使用联合类型或者元组类型,通常会使用类型别名
索引类型
- 索引类型查询操作符,索引访问操作符
- 索引类型查询操作符:
keyof T 的结果表示:T上已知公共属性名的联合
- 索引访问操作符:
T[K] ------- 就是T中K属性对应的类型
- 索引类型查询操作符:
映射类型
- 从就类型中创建新类型的方式
-
- Keys:是已经存在的联合类型
- k内部会循环keys中的属性
- 最后是:属性的结果类型