js foreach用法_js函数高阶函数

学习JavaScript的童鞋都知道,js中的函数是一种很强大的存在:“函数是一等公民”。本文讨论了js中函数的几个特点,并就高阶函数的定义和应用给出一些基本的示例。

1 简介

本文介绍了js中函数的几个特点,并引出了高阶函数的概念,并利用高阶函数实现了两个小例子:

•定义一个只能被调用一次的函数?•定义一个具有缓存功能的函数?

2 内容

•函数的几个特点•高阶函数的概念•高阶函数的两个小应用

3 函数的几个特点

3.1 定义函数的四种方式

定义函数的方式有很多种

// 1. 函数声明function f(a,b) {return a + b;}// 2. 函数表达式var f = function(a,b) {return a + b}// 3. 箭头函数var f = (a,b) => a + b// 4. 构造器var f = new Function('a', 'b', 'return a + b');

3.2 把函数当作对象

在js中“一切皆对象”,函数也不例外。先来看下基本的函数的使用:

function f(p){    console.log(p)}f();

这是一个标准的先定义函数,再调用函数的过程。一切是正常的。但是,如何理解如下的代码:

console.log(f.length)

从字面上看,它在访问f的length属性,它的执行结果是1。对于函数f来说,它确实有一个属性length,表示函数形参的个数。

那我们是否可以给函数添加其它的属性呢?答案是可以的。代码如下:

function f(p){    f.counter++    console.log(p)}// 添加属性f.counter = 0;// 添加属性f.func = function(){    console.log('func,'+this.counter)}// 调用函数f();console.dir(f)console.log(f.counter);f.func()

上面的代码中, 我们给函数f添加额外的属性counterfunc。每调用这一个函数一次,就会自增一次被调用次数。

3e48b8ba9c9c7fd27d42750faf708c57.png

上图是打印出f后的结果,它的内容也是键值对格式的,看起来也与对象无异。

3.3 把函数当作实参

在js中,函数是可以当作实参的。例如:

function f(n){    console.log(n)}function myFunc(func,m) {    console.log('myFunc')    func(m); // 执行传入的函数}myFunc(f,10);

上面的代码是可以正常工作的,在执行代码的过程中,myFunc这个函数中的形参func的值就是函数f。如果这段代码不太好理解,也可以换成这样:

// 改写成函数表达式的方式来定义函数f// 你可以把f看作是一个变量,它的值就是函数var f = function(n){    console.log(n)}function myFunc(func,m) {    console.log('myFunc')    func(m); // 执行传入的函数}myFunc(f,10);

当然了,这种用法其实我们一直就在使用,例如我们经常使用的一些代码:

var arr = [];// 数组的方法arr.sort(function(){})arr.forEach(function(){})// jquery的入口函数$(function(){})// jquery中的ajax方法$.get(url,function(res){} )

上面的代码中,都是把一个函数(具体来说是一个没有名字的函数)当作另一个函数的实参传入。

3.4 在函数内部再定义函数

在函数内部再定义函数也是一个比较常见的用法。

一个“没有什么作用”的示例如下:

function f() {    function f_1 () {        console.log("f_1")    }    f_1();}f();

在函数内部去定义一个函数在语法上是正确的。我们也来回顾一下一些常见的用法。

$(function f1(){    document.getElementById("btn").addEventListener("click",function f2(){    })    $("#btn1").on("click",function f3(){    })})

在上面的代码中,我们在函数f1的内部定义并使用两个函数f2,f3。当然了,一般情况下, 我们不需要给事件响应函数(f1,f2,f3这三个函数)取名字。但还是有场景需要名字的,例如你想要removeEventListener[1]时,就需要具体的函数名。

3.5 把函数当作返回值

看代码:

function f() {    var f1 = function(){        console.log("f1")    }    return f1;}var rs = f();rs();// 或者是 f()();

上面的代码中,在函数f的内部又定义一个函数f1(为了降低理解的难度,代码中是通过函数表达式的方式来定义f1的,其它格式的定义也可以的。)在执行f()之后,rs 中保存是f1的函数体,所以它还可以继续加括号去调用 ,就是rs()。

这就是一个把函数当作返回值来使用的案例。

4 高阶函数的定义

高阶函数的英文是 “Higher-Order Function”。它是对其他函数进行操作的函数:把其它函数当作参数或返回值。简单来说,高阶函数:

•它也是一个函数•它接收其它函数作为参数,或将其它函数作为返回值输出。

按这个定义来回顾上面的代码,其实好多地方都用上了高阶函数。

[1,2,3].forEach(function() {} ) ; // 把函数当作参数传入了forEach函数。

5 高阶函数的应用

一般的应用公式是:

var f_new  = 某高阶函数(f)

把原函数作为高阶函数的入参,并返回一个新函数,这个新函数较原函数有一些新的功能。下面我们来看一些例子。

5.1 定义一个只能被执行指定次数的函数

这里有一个函数f,默认情况下它可以被调用任意多次。

function f(){    console.log("f")}f();f();f();

下面提点过份的要求:在不修改原函数f的基础上,产生一个新的函数f1:

•f1具备f的功能•f1只能被调用2次

最朴素的想法是:

function f() { console.log('f') }var counter = 0 ;function f1() {    counter++;    if(counter >2){        console.log("超过2次了")    } else {        f();     }}

这种写法就只能限制在f这个函数上,如何拓展到所有的函数呢,下面就要用到高阶函数啦。

function times (f, num = 2){    var counter = 0 ;    var f1 = function () {        counter++;        if(counter > num){            console.log("超过"+num+"次了")        } else {            f();         }    }    return f1;}

上面的函数times接收两个参数,f表示要处理的函数,num表示最多允许f函数被调用的次数。它的返回值是一个新的函数。我们把times这个函数称为高阶函数以区别它和普通函数的区别哈。

完整的实现如下:

function f1() { console.log("f1") }function f2() { console.log("f2") }function times (f, num = 2){    var counter = 0 ;    var f1 = function () {        counter++;        if(counter > num){            console.log("超过"+num+"次了")        } else {            f();         }    }    return f1;}var f1_1 = times(f1,1); //f1_1具备了f1的功能,但只能被成功调用1次。var f2_3 = times(f2,3); //f2_3具备了f2的功能,但只能被成功调用3次。

再进一步,如果f1这个函数是带参数的,又该如何处理?这就需要补上参数及当前上下文了。

function times (f, num = 2){    var counter = 0 ;    var f1 = function (...arg) {        var that = this;        counter++;        if(counter > num){            console.log("超过"+num+"次了")        } else {            f.apply(that,arg);         }    }    return f1;}

注意两个地方:

•函数签名中的...arg 。这是es6中的新语法,用来把全部的实参收集到数组arg中。•调用函数f时用到的f.apply(that,arg) 。它用来执行函数f,并把f内部要用到的this值设置为that,同时把arg中保存的参数传入。

5.2 带缓存功能的函数

缓存,通俗来讲是把某次运行的结果保存起来,方便下次直接返回。举个例子:我的英文不太好,经常要去查字典,第一天遇到了width不认识它,所以去查了一次,结果是"宽度",第3天又遇到了width,又不记得了,所以再得再去查一次。

查询字典(width) ===> 宽度

如果这个查字典的功能是带缓存的话,它就应该记下我曾经查询过的单词。写个函数来模拟下哈。

下面一个字典,它只能查两个单词。

function dict(word){    if(word === 'width'){        // 省略查询过程        return '宽度'    }     else if(word === 'height'){        return '高度'    }    else {        return '不认识'+word    }}console.log( dict('width') )  // 查一次console.log( dict('width') )  // 还要查一次。

带缓存功能的函数的思路是:把查询的条件及查询的结果保存成键值对。基本步骤是:收到查询条件(函数的实参),检查缓存中是否有结果

•如果有:直接返回,不用再查了。•如果没有:去查一次(执行函数体),保存信息到缓存中,返回查询结果。

具体见下代码:

// 缓存函数f的查询条件及结果function cache(f){    // obj对象就是缓存区    var obj = { }    return function(arg) {        // 检查缓存        if(obj[arg]){            return obj[arg]        } else {            let rs = f(arg);            obj[arg] = rs;            return rs        }    }    // 这里就有一个典型的闭包}function dict(word){    if(word === 'width'){        // 省略查询过程        console.log("...省略查询过程")        return '宽度'    }     else if(word === 'height'){        return '高度'    }    else {        return '不认识'+word    }}var f1 = cache(dict)console.log( f1('width') )  // 查一次console.log( f1('width') )  // 直接从缓存中取

注意,上面的示例只是一个基本的演示示例,离工具函数的要求还相去甚远。想进一步了解缓存函数,可以参考underscorejs库中的memoize[2]

6 小结

•js中的函数可以当作另一个函数的实参,作为函数的返回值。•高阶函数也是一个函数,只不过它的形参或者返回值是函数。•高阶函数的应用一般是在不改变原函数的情况下,通过定义一个工具函数把原函数当作一个入参数,去返回一个具备特殊功能的新函数。本文中只是列出几个小例子。类似的例子还有函数的防抖,节流,柯里化等,将会在后面介绍。版权信息:凡人进阶。如果对您有帮助,欢迎转发,关注。

References

[1] removeEventListener: https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/removeEventListener[2] memoize: https://underscorejs.org/#memoize

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值