lambda x. ( lambda y. plus x y )function add(a) {
return function (b) {
return a + b
}
}
add(1)(2)let arr = []
function addCurry() {
let arg = Array.prototype.slice.call(arguments); // 递归获取后续参数
arr = arr.concat(arg);
if (arg.length === 0) { // 如果参数为空,则判断递归结束
return arr.reduce((a,b)=>{return a+b}) // 求和
} else {
return addCurry;
}
}
addCurry(1)(2)(3)()addCurry 函数会引用一个外部变量 arr,不符合纯函数的特性,于是就优化为:function addCurry() {
let arr = [...arguments]
let fn = function () {
if(arguments.length === 0) {
return arr.reduce((a, b) => a + b)
} else {
arr.push(...arguments)
return fn
}
}
return fn
}.toString 写法:function addCurry() {
let arr = [...arguments]
// 利用闭包的特性收集所有参数值
var fn = function() {
arr.push(...arguments);
return fn;
};
// 利用 toString 隐式转换
fn.toString = function () {
return arr.reduce(function (a, b) {
return a + b;
});
}
return fn;
}
接下来,让我们看看它怎么变?!ajax 函数,它有 3 个参数 url、data、callbackfunction ajax(url, data, callback) {
// ...
}function ajaxTest1(data, callback) {
ajax('http://www.test.com/test1', data, callback);
}function ajax(url, data, callback) {
// ...
}
let ajaxTest2 = partial(ajax,'http://www.test.com/test2')
ajaxTest2(data,callback)partial 函数是这样写的:function partial(fn, ...presetArgs) { // presetArgs 是需要先被绑定下来的参数
return function partiallyApplied(...laterArgs) { // ...laterArgs 是后续参数
let allArgs =presetArgs.concat(laterArgs) // 收集到一起
return fn.apply(this, allArgs) // 传给回调函数 fn
}
}partial 函数不止对 ajax 函数有作用,对于其它想减少传参的函数同样适用。console.log('A');不符合时,则执行语句:console.log('others')转为代码即:const handleOption = (param) =>{
if(param === 'A'){
console.log('A')
}else{
console.log('others')
}
}handleOption('A'),都必须要走完 if...else... 的判断流程。比如:const handleOption = (param) =>{
console.log('每次调用 handleOption 都要执行 if...else...')
if(param === 'A'){
console.log('A')
}else{
console.log('others')
}
}
handleOption('A')
handleOption('A')
handleOption('A')
有没有什么办法,多次调用 handleOption('A'),却只走一次 if...else...?答案是:柯里化。const handleOption = ((param) =>{
console.log('从始至终只用执行一次 if...else...')
if(param === 'A'){
return ()=>console.log('A')
}else{
return ()=>console.log('others')
}
})
const tmp = handleOption('A')
tmp()
tmp()
tmp()
这样的场景是有实战意义的,当我们做前端兼容时,经常要先判断是来源于哪个环境,再执行某个方法。比如说在 firefox 和 chrome 环境下,添加事件监听是 addEventListener 方法,而在 IE 下,添加事件是 attachEvent 方法;如果每次绑定这个监听,都要判断是来自于哪个环境,那肯定是很费劲。我们通过上述封装的方法,可以做到 一处判断,多次使用。肯定有小伙伴会问了:这也是柯里化?
嗯。。。怎么不算呢?把 'A' 条件先固定下来,也可叫“缓存下来”,后续的函数执行将不再传 'A' 这个参数,实打实的:把多参数转化为单参数,逐个传递。const calculateFn = (num)=>{
const startTime = new Date()
for(let i=0;i<num;i++){} // 大数计算
const endTime = new Date()
console.log(endTime - startTime)
return "Calculate big numbers"
}
calculateFn(10_000_000_000)calculateFn(10_000_000_000) 肯定是不明智的,太费时。一般的做法就是声明一个全局变量,把运算结果保存下来:比如 const resNums = calculateFn(10_000_000_000)如果有多个大数运算呢?沿着这个思路,即声名多个变量:const resNumsA = calculateFn(10_000_000_000)
const resNumsB = calculateFn(20_000_000_000)
const resNumsC = calculateFn(30_000_000_000)calculateFn() 函数的参数和声名的常量名一一对应,都是一个麻烦事。有没有什么办法?只用函数,不增加多个全局常量,就实现多次调用,只计算一次?答案是:柯里化。代码如下:
function cached(fn){
const cacheObj = Object.create(null); // 创建一个对象
return function cachedFn (str) { // 返回回调函数
if ( !cacheObj [str] ) { // 在对象里面查询,函数结果是否被计算过
let result = fn(str);
cacheObj [str] = result; // 没有则要执行原函数,并把计算结果缓存起来
}
return cacheObj [str] // 被缓存过,直接返回
}
}
const calculateFn = (num)=>{
console.log("计算即缓存")
const startTime = new Date()
for(let i=0;i<num;i++){} // 大数计算
const endTime = new Date()
console.log(endTime - startTime) // 耗时
return "Calculate big numbers"
}
let cashedCalculate = cached(calculateFn)
console.log(cashedCalculate(10_000_000_000)) // 计算即缓存 // 9944 // Calculate big numbers
console.log(cashedCalculate(10_000_000_000)) // Calculate big numbers
console.log(cashedCalculate(20_000_000_000)) // 计算即缓存 // 22126 // Calculate big numbers
console.log(cashedCalculate(20_000_000_000)) // Calculate big numberscached 缓存函数的处理,所有的大数计算都能保证:输入参数相同的情况下,全局只用计算一次,后续可直接使用更加语义话的函数调用来得到之前计算的结果。此处也是柯里化的应用,在 cached 函数中先传需要处理的函数参数,后续再传入具体需要操作得值,将多参转化为单个参数逐一传入。const multi10 = function(x) { return x * 10; }
const add100 = function(x) { return x + 100; }
add100(multi10(7))const multi10 = function(x) { return x * 10; }
const add100 = function(x) { return x + 100; }
const compose = function(f,g) {
return function(x) {
return f(g(x))
}
}
compose(add100, multi10)(7)let compute = compose(add100, multi10)
compute(7)
我们对闭包的解释:“闭包是一个函数内有另外一个函数,内部的函数可以访问外部函数的变量,这样的语法结构是闭包。”与我们对柯里化的解释“把接受多个参数的函数变换成接受一个单一参数(或部分)的函数,并且返回接受余下的参数和返回结果的新函数的技术”,这两种说法几乎是“等效的”,只是从不同角度对 同一问题 作出的解释,就像 lambda 演算和图灵机对希尔伯特第十问题的解释一样。同一问题:指的是在 lambda 演算诞生之时,提出的:怎样用 lambda 演算实现接收多个参数?function debounce(fn, delay) {
delay = delay || 200;
let timer = null;
return function() {
let arg = arguments;
// 每次操作时,清除上次的定时器
clearTimeout(timer);
timer = null;
// 定义新的定时器,一段时间后进行操作
timer = setTimeout(function() {
fn.apply(this, arg);
}, delay);
}
};
var count = 0;
window.onscroll = debounce(function(e) {
console.log(e.type, ++count); // scroll
}, 500);// 函数节流,频繁操作中间隔 delay 的时间才处理一次
function throttle(fn, delay) {
delay = delay || 200;
let timer = null;
// 每次滚动初始的标识
let timestamp = 0;
return function() {
let arg = arguments;
let now = Date.now();
// 设置开始时间
if (timestamp === 0) {
timestamp = now;
}
clearTimeout(timer);
timer = null;
// 已经到了delay的一段时间,进行处理
if (now - timestamp >= delay) {
fn.apply(this, arg);
timestamp = now;
}
// 添加定时器,确保最后一次的操作也能处理
else {
timer = setTimeout(function() {
fn.apply(this, arg);
// 恢复标识
timestamp = 0;
}, delay);
}
}
};
var count = 0;
window.onscroll = throttle(function(e) {
console.log(e.type, ++count); // scroll
}, 500);timer。// 防抖动
_.debounce(func, [wait=0], [optinotallow={}])
// 节流
_.throttle(func, [wait=0], [optinotallow={}])
// 将一个断言函数结果取反
_.negate(predicate)
// 柯里化函数
_.curry(func, [arity=func.length])
// 部分应用
_.partial(func, [partials])
// 返回一个带记忆的函数
_.memoize(func, [resolver])
// 包装函数
_.wrap(value, [wrapper=identity])function before(n, func) {
let result, count = n;
return function(...args) {
count = count - 1
if (count > 0) result = func.apply(this, args)
if (count <= 1) func = undefined
return result
}
}
const fn= before(3,(x)=>console.log(x))
fn(1) // 1
fn(2) // 2
fn(3) // 不执行function after(n, func) {
let count = n || 0
return function(...args) {
count = count - 1
if (count < 1) return func.apply(this, args)
}
}
const fn= after(3,(x)=>console.log(x))
fn(1) // 不执行
fn(2) // 不执行
fn(3) // 3对闭包概念模糊不清的、或者只会背概念的 => 初级会写防抖、节流、或柯里化等高阶函数的 => 中级深刻理解高阶函数封装思想、能自主用闭包封装高阶函数 => 高级OK,以上便是本篇分享,专栏第 2 篇,希望各位工友喜欢~ 欢迎点赞、收藏、评论 ?后文会再深入 JavaScript 函数式编程,展开讲解:纯函数、延迟处理、JS 迭代器等、敬请期待~
我是掘金安东尼 ? 100 万人气前端技术博主 ? INFP 写作人格坚持 1000 日更文 ✍ 关注我,安东尼陪你一起度过漫长编程岁月 ?我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。
我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的
我需要从json记录中获取一些值并像下面这样提取curr_json_doc['title']['genre'].map{|s|s['name']}.join(',')但对于某些记录,curr_json_doc['title']['genre']可以为空。所以我想对map和join()使用try函数。我试过如下curr_json_doc['title']['genre'].try(:map,{|s|s['name']}).try(:join,(','))但是没用。 最佳答案 你没有正确传递block。block被传递给参数括号外的方法
在这段Ruby代码中:ModuleMClassC当我尝试运行时出现“'M:Module'的未定义方法'helper'”错误c=M::C.new("world")c.work但直接从另一个类调用M::helper("world")工作正常。类不能调用在定义它们的同一模块中定义的模块函数吗?除了将类移出模块外,还有其他解决方法吗? 最佳答案 为了调用M::helper,你需要将它定义为defself.helper;结束为了进行比较,请查看以下修改后的代码段中的helper和helper2moduleMclassC
也许这听起来很荒谬,但我想知道这对Ruby是否可行?基本上我有一个功能...defadda,bc=a+breturncend我希望能够将“+”或其他运算符(例如“-”)传递给函数,这样它就类似于...defsuma,b,operatorc=aoperatorbreturncend这可能吗? 最佳答案 两种可能性:以方法/算子名作为符号:defsuma,b,operatora.send(operator,b)endsum42,23,:+或者更通用的解决方案:采取一个block:defsuma,byielda,bendsum42,23,