一、什么是柯里化
将一个函数的多个参数变为单个参数的过程叫做柯里化。柯里化后的函数需要多次调用,直到传递的实参个数等于原函数的形参个数时执行
案例:封装一个函数,传入协议、主机名、路径,返回一个地址
function getUrl(protocol, hostname, pathname) {
return `${protocol}://${hostname}/${pathname}`
}
console.log(getUrl('https', 'taobao.com', 'index')) // https://taobao.com/index
console.log(getUrl('https', 'jd.com', 'about')) // https://jd.com/about
部分柯里化:
function getUrl(protocol) {
return function (hostname, pathname) {
return `${protocol}://${hostname}/${pathname}`
}
}
const getUrl1 = getUrl('https')
console.log(getUrl1('taobao.com', 'index')) // https://taobao.com/index
console.log(getUrl1('jd.com', 'about')) // https://jd.com/about
柯里化:
function getUrl(protocol) {
return function (hostname) {
return function (pathname) {
return `${protocol}://${hostname}/${pathname}`
}
}
}
const getUrl1 = getUrl('https')('tabobao.com') // 函数参数得到复用
console.log(getUrl1('index')) // https://taobao.com/index
console.log(getUrl1('about')) // https://taobao.com/about
二、柯里化的好处
- 函数参数复用
- 避免重复判断
- 延迟执行,如bind
三、实现一个通用的柯里化函数,将任何一个函数变成柯里化函数
1、使用lodash中的curry
import curry from 'lodash/curry'
function sum(a, b, c) {
return a + b + c
}
const currySum = curry(sum)
console.log(currySum(1, 2, 3))
console.log(currySum(1)(2, 3))
console.log(currySum(1,2)()()(3))
2、手动实现curry函数
function sum(a, b, c) {
return a + b + c
}
function myCurry(callback, ...args) {
if (args.length >= callback.length) {
return callback(...args)
}
return (...rest) => {
return myCurry(callback, ...args, ...rest)
}
}
const currySum = myCurry(sum)
console.log(currySum(1, 2, 3))
console.log(currySum(1)(2, 3))
console.log(currySum(1, 2)()()(3))
将getUrl函数传入可以得到一个柯里化函数:
function getUrl(protocol, hostname, pathname) {
return `${protocol}://${hostname}/${pathname}`
}
function myCurry(callback, ...args) {
if (args.length >= callback.length) {
return callback(...args)
}
return (...rest) => {
return myCurry(callback, ...args, ...rest)
}
}
const curryGetUrl = myCurry(getUrl)
console.log(curryGetUrl('http')('taobao.com')('index'))
console.log(curryGetUrl('http')('taobao.com')('about'))
上面的myCurry是用ES6写的,用ES5如何实现:
function myCurry(callback) {
var args = Array.prototype.slice.call(arguments, 1)
if (args.length >= callback.length) {
return callback.apply(null, args)
}
return function () {
var rest = Array.prototype.slice.call(arguments)
var list = [callback].concat(args, rest)
return myCurry.apply(null, list)
}
}
四、偏函数
1、什么是偏函数
一个柯里化后的函数,先调用一次,返回一个新的函数,这个新函数就叫偏函数。
由于调用过一次,可以将一部分参数固定住,这样直接使用偏函数时可以减少重复参数的传递
2、偏函数的使用
- 获取数据类型
const isType = function (type) {
return function (obj) {
return Object.prototype.toString.call(obj) === '[object ' + type + ']'
}
}
const isString = isType('String') // 偏函数
const isNumber = isType('Number') // 偏函数
console.log(isString('100'))
console.log(isNumber(100))
- 获取标签
const wrap = function (tag) {
const startTag = `<${tag}>`
const endTag = `</${tag}>`
return (html) => startTag + html + endTag
}
const div = wrap('div') // 偏函数
const h1 = wrap('h1') // 偏函数
document.write(div('div标签'))
document.write(h1('h1标签'))
五、反柯里化
1、什么是反柯里化?
反柯里化函数通过指定this的指向,固化一部分参数
2、反柯里化的作用
让一个对象借用原本不属于它的方法
3、对象借用其他对象的方法
一般可以使用call/apply来借用(在构造函数中也可以通过call/apply借用其他类的方法)
var cat = {
name: 'Tom',
getName: function () {
console.log(this.name)
}
}
var mouse = {
name: 'Jerry'
}
cat.getName.call(mouse) // Jerry
通过uncurry函数将对象中的方法变成公共的,在调用时直接传入对象
var cat = {
name: 'Tom',
age: 8,
getName: function (...rest) {
console.log(this.name, rest)
},
getAge: function () {
console.log(this.age)
}
}
var mouse = {
name: 'Jerry',
age: 6
}
Function.prototype.uncurry = function () {
var _this = this
return function () {
var obj = Array.prototype.shift.call(arguments)
return _this.apply(obj, arguments)
}
}
var commonGetName = cat.getName.uncurry() // 通过反柯里化函数将getName变为公共的
var commonGetAge = cat.getAge.uncurry()
commonGetName(cat, 1)
commonGetAge(mouse)
其中,uncurry函数可以改写成:
Function.prototype.uncurry = function () {
var _this = this
return function () {
return Function.prototype.call.apply(_this, arguments)
}
}
bind会返回一个偏函数,所以可以使用bind实现:
Function.prototype.uncurry = function () {
return Function.prototype.call.bind(this)
}