javaScript---函数柯里化、闭包

2021-06-08 15:06

阅读:402

标签:函数作用域   func   next   curry   rgs   nta   很多   为什么   校验   

目录
  • 前言
  • 闭包
  • 函数柯里化
    • 函数柯里化的好处
      • 1、参数复用
      • 2、提前确认
      • 3、延迟运行
    • 面试题扩展

前言

前两天看vue源码的时候,看到了 makeMap 方法,查看网上的解析,发现了函数柯里化这个名词,一时好奇,就搜索学习了一下,顺便复习了一下关于闭包的一些知识。

闭包

先简单说一下js的闭包。闭包是指有权访问另一个函数作用域中的变量函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数,如下例:

function f1(){
    var n=999;
    function f2(){
      alert(n);
    }
    return f2;
  }

console.log(n); // error: n is not defined

let fn = f1(); // 此处 fn 就是闭包f2函数
fn(); // 弹出 n的值 999

f1函数中的变量n在f1函数外部原本不能被访问,但是函数f2被定义在f1的内部,所以在f2中可以访问到变量n。

上例中,f2就是一个闭包函数,执行f1函数后,函数f2便被赋值给fn,执行fn后,便可得到f1函数内部定义的变量n。

函数柯里化

乍一看到这个名词的时候,有种高数公式的既视感......不知道为什么,就觉得它非常高级,让人心生敬畏。现在知道为什么那些大佬们都喜欢拽一些专业名词了,当你一开始就用一些通俗易懂的口水话给别人解释的一个专业技能时,别人会觉得,害,就这啊,很简单嘛,说这么半天;但当你先说出一系列专业名词,在他露出疑惑的神情时,再用“简而言之”起承转合一下,他就会如醍醐灌顶一般,将你视为解惑的师者,这,可能就是专业名词的魅力叭。咳咳......扯远了,回归函数柯里化,首先明确一下概念。

函数柯里化,就是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回一个函数,这个函数接受其他剩余参数。

看完这个概念,就有一点闭包的感觉了,没错,函数柯里化就是利用js闭包实现的!

先看一个函数柯里化的简单示例:

function fn1(a,b,c){
	return a+b+c;
}
// 函数柯里化 后变为
function fn2(fn){
	var firstArgs=Array.prototype.slice.call(arguments,1)  //这里,arguments的第一个参数是fn,所以从1开始
	var _cur=function(){
		var nextArgs=[...arguments]
		var allArgs=firstArgs.concat(nextArgs)
		return fn.apply(this,allArgs)
	}
	
	return _cur
}

调用原函数时,需传入三个参数a、b、c,进行函数柯里化之后,可选择先固定第一个参数,示例如下:

var add1=fn2(fn1,10) // 此处的“10”在上述示例的第一个arguments中
add1(10,20) //40 // 此处的“10,20”在上述示例的第二个arguments中
add1(20,30) //60 // 此处的“20,30”在上述示例的第二个arguments中

也可先固定两个参数,示例如下:

var add1=fn2(fn1,10,20) 
add1(10) //40 
add1(30) //60 

函数柯里化的好处

1、参数复用

比如上述示例中,var add1=fn2(fn1,10)即可以复用参数“10”;var add1=fn2(fn1,10,20) 即可以复用参数“10、20”。

例如对正则表达式的校验,示例如下:

function curryingCheck(reg) {
    return function(txt) {
        return reg.test(txt);
    }
}

var hasNumber = curryingCheck(/\d+/g); // 校验字符串中是否含有数字

hasNumber(‘test1‘);      // true
hasNumber(‘testtest‘);   // false

在项目中可能有很多地方需要进行数字校验,为了复用同一个正则表达式,避免重复定义,即可使用函数柯里化的方式实现。

2、提前确认

提前确认主要用于对浏览器的兼容性判断,有一些老版本浏览器不支持一些新的js事件,为了减少判断次数,便可使用函数柯里化,示例如下:

var on = (function() {
    if (document.addEventListener) {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.addEventListener(event, handler, false);
            }
        };
    } else {
        return function(element, event, handler) {
            if (element && event && handler) {
                element.attachEvent(‘on‘ + event, handler);
            }
        };
    }
})();

上述示例则为提前确认浏览器是否支持addEventListener方法,从而判断dom元素调用on(jQuery)方法时底层执行addEventListener还是attachEvent。

3、延迟运行

Function.prototype.bind = function (context) {
    var _this = this
    var args = Array.prototype.slice.call(arguments, 1)
 
    return function() {
        return _this.apply(context, args)
    }
}

上述示例为bind方法的实现,实际上则是运用了函数柯里化。当某函数调用bind时,会延迟执行,不会像call和apply一样立即执行,而是会先返回一个函数,再次调用才会执行。

面试题扩展

实现一个add方法,使计算结果能够满足如下预期:

add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;

实现如下:

function add() {
    // 第一次执行时,定义一个数组专门用来存储所有的参数
    var _args = Array.prototype.slice.call(arguments);

    // 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
    var _adder = function() {
        _args.push(...arguments);
        return _adder;
    };

    // 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
    _adder.toString = function () {
        return _args.reduce(function (a, b) {
            return a + b;
        });
    }
    return _adder;
}

上述的_adder.toString是在重写function的toString方法,因为当我们直接将函数参与其他的计算时,函数会默认调用toString方法,直接将函数体转换为字符串参与计算,function的原生toString方法效果如下:

function fn() { return 20 }
console.log(fn + 10);     // 输出结果 function fn() { return 20 }10

但经过重写后,_adder的toString方法作用就变为了累加器。

javaScript---函数柯里化、闭包

标签:函数作用域   func   next   curry   rgs   nta   很多   为什么   校验   

原文地址:https://www.cnblogs.com/upward-lalala/p/14520814.html


评论


亲,登录后才可以留言!