JavaScript中非匿名函数的美

图片


根据一些民意调查JavaScript中的匿名箭头功能是ES-2015最受欢迎的功能,互联网上大量的教程也突显了这一点。 毫无疑问,它们非常有用,但是在这篇简短的文章中,我们将看看使用缺少注意的示例至少使用具有命名功能的精彩表达式-NFE的示例。


短暂的帮助


命名函数表达式-JavaScript中函数表达式的扩展,允许您命名作为表达式的一部分创建的函数( FunctionExpression ):


let fe = function named(...) { /* function body */ }; 

重点是在fe变量引用的函数内部,可以通过名为的名称访问该函数本身。 只能从函数内部重写名称!


使用方法


例如,我们需要编写一个装饰函数,该函数将计算对某个函数的调用次数。 使用NFE可以非常优雅地完成此操作:


 const count = f => function df() { df.calls = (df.calls || 0) + 1; return f(...arguments); }; 

在这里,我们可以访问返回的df函数,以便在调用它时,将计数器保存在calls属性中,必要时将可以读取该计数器:


 const csum = count((x, y) => x + y); csum(5, 10); // 15 csum(50, 1); // 51 csum.calls; // 2 

或保存函数调用的所有结果:


 const accum = f => function df() { let res = f(...arguments); (df.results = (df.results || [])).push(res); return res; }; 

无论如何,我们利用JavaScript中的函数是对象这一事实,并可以在本地任务中为其添加必要的属性。


显然,我们可以调用一个函数。 使用NFE进行递归特别好。 假设我们要查找斐波那契数列的第n个成员(由于某种原因,每个人迟早都希望这样):


 const rf = function getfn(n) { return n > 2 ? getfn(n - 2) + getfn(n - 1) : 1; }; rf(1); // 1 rf(10); // 55 

这里不值得关注“尾部”。 但是通过FunctionDeclaration可以做到这一点-是的。 但是expression函数具有一个优点:如果要将函数从rf转移到另一个变量,则无需在内部编辑递归调用。


计时器实现的简洁示例:


 const ping = (callback, t) => setTimeout(function pf() { callback(); setTimeout(pf, t); }, t); 

在这里,我们使用NFE作为文字作为参数,而不是声明不必要的变量。 为什么不设置setInterval ? 代替回调,这里可能需要等待计时器的下一个滴答之前的承诺的解决。


一个有趣的例子是NFE和IIFE( 立即调用函数表达式 )的组合,当只需要递归函数的结果时。 一次:


 let data = { f10: function fact(n) { return n > 1 ? n * fact(n - 1) : 1; }(10) }; data.f10; // 3628800 

是这样吗 好吧,这是一个真正的问题:有一个严格增加的函数f在自然数集中起作用。 求出函数值不超过给定y的极限点x 。 这种函数的一个例子是f(x) = 3 * x + 5f(x) = 2^x + 11


 const find = (f, y) => function bs(a, b) { if (a + 1 === b) return a; let m = Math.ceil((a + b) / 2); return f(m) <= y ? bs(m, b) : bs(a, m); }(-1, y + 1); find(x => 3 * x + 5, 200); // 65 find(x => Math.pow(2, x) + 11, 1000); // 9 

我们将不讨论数学细节。 考虑一下实现:


  1. 所需的find函数具有我们的两个参数f,y,并返回IIFE的结果。
  2. 立即调用的函数实现对解决方案的二进制搜索,第一个调用获得初始范围。
  3. 搜索本身是通过递归实现的,递归使用NFE名称进行后续调用。 我们不声明搜索功能,也不创建新变量。

当然,可以通过带有前提条件的简单循环来解决类似的问题,但这对于控制台编码来说是绝对必要的。


最后,谈谈堆栈跟踪。 我们有一些NFE函数,在其中抛出了异常:


 const fe = function named() { throw new Error('Something went wrong'); }; 

由于表达式中有一个命名函数,因此我们将在堆栈跟踪中看到它:


 Uncaught Error: Something went wrong at named (<anonymous>:2:11) 

但是,从ES-2015开始,许多匿名函数表达式实际上会创建一个带有名称的函数,从而使其脱离上下文:


 const unnamed = function() { throw new Error('Something went wrong'); }; 

表达式右侧的函数与左侧的变量关联:


 Uncaught Error: Something went wrong at unnamed (<anonymous>:2:7) 

但这并不总是可能的。 一个典型的例子是通过IIFE初始化外部库脚本,作为一个选项:


 /** * @license * @description */ ;(function() { // some awesome code }.call(this)); 

或函数返回另一个函数的常见示例:


 const bind = (f, ctx) => function() { return f.apply(ctx, arguments); }; 

没有输出变量,因此如果发生异常,我们将看到anonymous 。 NFE可能会在诉讼中有所帮助。


结论简短


NFE擅长编写简洁且不受支持的代码,以快速制作各种任务的原型。 要在最终代码中使用它们的优点,您应该考虑以下几点:JavaScript已经充满了有趣的规范。

Source: https://habr.com/ru/post/zh-CN427461/


All Articles