We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
function add(a, b) { return a + b } // 执行add函数 add(1,2) // 假设有一个curry函数可以做到柯里化 let curry_add = curry(add) curry_add(1)(2)
function ajax(type, url, data) { let xhr = new XMLHttpRequest() xhr.open(type, url, true) xhr.send(data) } ajax('POST', 'www.test.com', 'name=ahao') ajax('POST', 'www.test2.com', 'name=ahao') ajax('POST', 'www.test3.com', 'name=ahao') // 利用curry let ajaxCurry = curry(ajax) // 以 POST 类型请求数据 let post = ajaxCurry('POST') post('www.test.com', 'name=ahao') // 以 POST 类型请求来自于 www.test.com 的数据 let postFromTest = post('www.test.com') postFromTest("name=kevin"); // curry这种用途可以理解为:参数复用。本质上是降低通用性,提高适用性
「用闭包把传入的参数保存起来,当传入的参数的数量足够执行函数时,就开始执行函数」
function curry(fn) { let args = [].slice.call(arguments, 1) return function() { let newArgs = args.concat([].slice.call(arguments)) fn.apply(this, newArgs) } } function add(a, b) { return a + b } let addCurry1 = curry(add, 1, 2) let addCurry2 = curry(add, 1) let addCurry3 = curry(add) console.log(addCurry1()) console.log(addCurry2(4)) console.log(addCurry3(1,5))
已经有柯里化的感觉了,但是还没有达到要求,不过我们可以把这个函数用作辅助函数,帮助我们写真正的 curry 函数。
参考文档:[[JavaScript专题之函数柯里化](https://github.com/mqyqingfeng/Blog/issues/42)](mqyqingfeng/Blog#42)
function sub_curry(fn) { let args = [].slice.call(arguments, 1) return function() { return fn.apply(args.concat([].slice.call(arguments))) } } function curry(fn, length) { length = length || fn.length let slice = Array.prototype.slice return function () { if (arguments.length < length) { let combined = [fn].concat(slice.call(arguments)) return curry(sub_curry.apply(this, combined), length - arguments.length) } else { return fn.apply(this, curry) } } } function add(a,b,c) { return [a, b, c]; } var fn = curry(add); fn("a", "b", "c") // ["a", "b", "c"] fn("a", "b")("c") // ["a", "b", "c"] fn("a")("b")("c") // ["a", "b", "c"] fn("a")("b", "c") // ["a", "b", "c"]
参考文档:yygmind/blog#37
function currying(fn, length) { // 1. 第一次调用获取函数fn参数的长度,后续调用获取fn剩余参数的长度 length = length || fn.length // 2. currying 包裹之后返回一个新函数,接收参数为 ...args return function(...args) { // 3. 新函数接收的长度是否大于fn剩余参数需要接受的长度 if (args.length >= length) { // 4. 满足要求,执行函数fn,传入新函数的参数 fn.apply(this, args) } else { // 5. 不满足要求,递归currying函数,新的fn为bind返回的新函数(bind绑定了`...args`参数,未执行),新的length为fn剩余参数的长度 currying(fn.bind(this, ...args), length - args.length) } } } // Test const fn = currying(function(a, b, c) { console.log([a, b, c]); }); fn("a", "b", "c") // ["a", "b", "c"] fn("a", "b")("c") // ["a", "b", "c"] fn("a")("b")("c") // ["a", "b", "c"] fn("a")("b", "c") // ["a", "b", "c"]
add(1, 2, 3) // 6 function add(a) { return function(b) { return function(c) { return a + b + c } } }
前面两次调用每次返回一个新函数,第三次调用后满足参数长度要求然后执行函数
再来看 currying 实现函数,其实就是判断当前参数长度够不够,参数够了就立马执行,不够就返回一个新函数,这个新函数并不执行,并且通过 bind 或者闭包保存之前传入的参数
bind
函数currying的实现中,使用了fn.length来表示函数参数的个数,那么fn.length表示函数的所有参数个数吗? 并不是
currying
fn.length
函数的length属性获取的是形参的个数,但是形参的数量不包括剩余参数的个数,而且仅包括第一个具有默认值之前的参数的个数
length
((a, b, c) => {}).length // 3 ((a, b, c = 3) => {}).length // 2 ((a, b = 3, c) => {}).length // 1 ((a = 1, b, c) => {}).length // 0 const fn = (...args) => { console.log(args.length); } fn(1, 2, 3) // 3
所以在柯里化的场景中,不推荐使用ES6的函数参数默认值
ES6
const fn = currying((a = 1, b, c) => { console.log([a, b, c]); }); fn(); // [1, undefined, undefined] fn()(2)(3); // Uncaught TypeError: fn(...) is not a function
我们期望函数 fn 输出 [1, 2, 3],但是实际上调用柯里化函数时 ((a = 1, b, c) => {}).length === 0,所以调用 fn() 时就已经执行并输出了 [1, undefined, undefined],而不是理想中的返回闭包函数,所以后续调用 fn()(2)(3) 将会报错。
[1, 2, 3]
((a = 1, b, c) => {}).length === 0
fn()
[1, undefined, undefined]
fn()(2)(3)
Function.prototype.call.bind(Object.prototype.toString)
The text was updated successfully, but these errors were encountered:
No branches or pull requests
柯里化
柯里化是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
用途
实现原理
「用闭包把传入的参数保存起来,当传入的参数的数量足够执行函数时,就开始执行函数」
第一版
已经有柯里化的感觉了,但是还没有达到要求,不过我们可以把这个函数用作辅助函数,帮助我们写真正的 curry 函数。
第二版
参考文档:[[JavaScript专题之函数柯里化](https://github.com/mqyqingfeng/Blog/issues/42)](mqyqingfeng/Blog#42)
第三版
参考文档:yygmind/blog#37
解析
前面两次调用每次返回一个新函数,第三次调用后满足参数长度要求然后执行函数
再来看 currying 实现函数,其实就是判断当前参数长度够不够,参数够了就立马执行,不够就返回一个新函数,这个新函数并不执行,并且通过
bind
或者闭包保存之前传入的参数扩展:函数参数 length
函数
currying
的实现中,使用了fn.length
来表示函数参数的个数,那么fn.length
表示函数的所有参数个数吗? 并不是函数的
length
属性获取的是形参的个数,但是形参的数量不包括剩余参数的个数,而且仅包括第一个具有默认值之前的参数的个数所以在柯里化的场景中,不推荐使用
ES6
的函数参数默认值我们期望函数 fn 输出
[1, 2, 3]
,但是实际上调用柯里化函数时((a = 1, b, c) => {}).length === 0
,所以调用fn()
时就已经执行并输出了[1, undefined, undefined]
,而不是理想中的返回闭包函数,所以后续调用fn()(2)(3)
将会报错。总结
Function.prototype.call.bind(Object.prototype.toString)
currying
函数,就是用闭包把传入的参数保存起来,等到传入的参数足够执行函数时,就开始执行函数length
: 获取的是形参的个数,但是形参的数量不包括剩余参数的个数,而且仅包括第一个具有默认值之前的参数个数The text was updated successfully, but these errors were encountered: