javascript - 嵌套函数的用途或优点是什么?

标签 javascript nested-function

我最近一直在从事数字系统转换器的研究,发现此嵌套函数代码块使我对它的使用提出了疑问。

就我而言,第一个代码输出的结果与第二个代码相同。那么,为什么有人诉诸更复杂的东西呢?这种方法的优点是什么?

convertBase(num).numFrom(from).numTo(to);

let convertBase = (num) => {
    return {
        numFrom: function (baseFrom) {
            return {
                numTo: function (baseTo) {

                }
            }
        }
    }
}


convertBase(num, from, to);

let convertBase = (num, baseFrom, baseTo) => {
    return parseInt(num, baseFrom).toString(baseTo);
}

最佳答案

这与嵌套函数无关,而与schönfinkeling/ currying有关。 Schönfinkeling/ currying以开发此技术的MosesSchönfinkel(在Gottlob Frege先前引入它之后)和Haskell Curry(对其进行完善和描述)命名。

简而言之,柯里化是一种允许将n个参数的任何函数转换为n-1个参数的函数的技术,该函数返回采用第n个参数的函数。通过重复应用这一点,您可以证明,使用任意多个参数对函数建模时,您不需要多个参数的函数。

这是一个例子。我可以打开一个将两个数字相加的函数:

function add(a, b) { return a + b; }

add(2, 3)
//=> 5


进入返回加法器函数的“加法器工厂”,该加法器函数在被调用时将产生两个数字的和:

function adderFactory(a) {
    return function adder(b) { return a + b; };
} 


const twoAdder = adderFactory(2);
twoAdder(3)
//=> 5


要么

adderFactory(2)(3)
//=> 5


现在,您可能会想:“但是ECMAScript支持带有多个参数的函数,因此,如果我可以原生使用它们,为什么还要使用currying模拟它们?”你会是对的!由于这个原因,使用curry没有任何意义。

但是,您可能还需要对函数做一些有趣的事情:部分应用程序。 “函数应用程序”只是函数式编程所代表的“调用函数”,因此“部分应用程序”的意思是“仅使用其参数的子集来调用函数”。部分应用程序仅使用其某些参数调用函数,并生成仅针对那些参数专用的函数。在支持部分应用程序的语言中,我可以执行以下操作:

const fourAdder = add(4, ?);


但是,ECMAScript没有部分应用程序。

但是,当我使用函数时,我可以执行“部分应用程序排序”,在这里我至少只能提供前几个参数,而忽略最后几个参数。这意味着您必须考虑哪些参数更可能是固定的,哪些参数更可能是可变的,并且应该通过“可变性”对其进行排序。

因此,就您发布的功能而言,可以创建一个基本转换器,该转换器只能将一个特定的数字从一个特定的基数转换为任何数目的基数。我必须承认,这实际上不是非常有用。如果这样定义函数,它将更加有用:

const convertFromBaseToBase = baseFrom =>
    baseTo =>
        num => parseInt(num, baseFrom).toString(baseTo);

convertFromBaseToBase(2)(8)('1001')
//=> '11'


现在,您可以例如创建一个从八进制到十六进制的转换器,如下所示:

const octalToHexadecimalConverter = convertFromBaseToBase(8)(16);

octalToHexadecimalConverter('17')
//=> "F"


注意!由于只能“部分地从右边应用”的限制,实际上,您也可以使用带有默认参数的可选参数来进行此操作,如下所示:

const baseToToken = Symbol('baseTo'),
    numToken = Symbol('num');

function convertFromBaseToBase(baseFrom, baseTo=baseToToken, num=numToken) {
    if (num === numToken) {
        if (baseTo === baseToToken) {
            return (baseTo, num=numToken) =>
                num === numToken ?
                    num => parseInt(num, baseFrom).toString(baseTo) :
                    parseInt(num, baseFrom).toString(baseTo);
        } else {
            return num => parseInt(num, baseFrom).toString(baseTo);
        }
    } else {
        return parseInt(num, baseFrom).toString(baseTo);
    }
}

convertFromBaseToBase(8, 16, '17')
//=> 'F'

convertFromBaseToBase(8, 16)('17')
//=> 'F'

convertFromBaseToBase(8)(16)('17')
//=> 'F'

convertFromBaseToBase(8)(16, '17')
//=> 'F'


但是,正如您所看到的,这开始变得非常丑陋,很快。

问题中的代码片段也有用,这是出于另一个原因:它提供了一个流畅的界面,该界面为特定参数指定了名称,这样您就不会混淆两个数字参数baseFrombaseTo。但是,这也可以通过其他几种方式解决。一种是通过命名函数以使baseFrombaseTo首先出现,即代替convertBase(num, baseFrom, baseTo)将其命名为convertNumberFromBaseToBase(num, baseFrom, baseTo)。另一种可能性是使用对象参数,如下所示:

function convertBase({ num, baseFrom, baseTo }) {
    return parseInt(num, baseFrom).toString(baseTo);
}

convertBase({ num: '17', baseFrom: 8, baseTo: 16 })
//=> 'F'


但是,即使使用更具描述性的函数名称或流畅的接口,更改参数顺序仍然有意义,以使currying和部分应用程序更加有用。

还请注意,在这种情况下,我什么都没说过,例如,[从Ruby Recursive Indexing/Searching Method (Using Middle Comparison) Returning Incorrect Index Value改编的代码]:

function bsearch(arr, target) {
    function bsearchRec(arr, target, offset=0) {
        const middleIndex = Math.floor(arr.length / 2);

        if (arr[middleIndex] === target) { return offset + middleIndex; } 
        if (arr.length === 1) { return undefined; }

        if (target > arr[middleIndex]) {
            return bsearchRec(arr.slice(middleIndex+1), target, offset + middleIndex + 1);
        } else if (target < arr[middleIndex]) {
            return bsearchRec(arr.slice(0, middleIndex), target, offset);
        }
    }

    return bsearchRec(arr, target);
}

bsearch([1, 3, 4, 5, 9], 5)
//=> 3


此处,嵌套函数bsearchRec嵌套在bsearch内,因为它是bsearch的内部私有实现细节,除bsearch的作者之外,没有人应该知道它。

最后,功能是ECMAScript中用于封装的工具。特别是,函数是ECMAScript实现对象的方式。对象具有由名称和封装标识的行为。在大多数OO语言中,行为,封装和名称到行为的映射(又称“方法调用”)这三件事是由一个实体(即对象)提供的。在ECMAScript中,封装是由函数(闭包)提供的,行为是由函数提供的(嵌套在闭包内部以共享私有状态的),而名称到行为的映射则由字典提供,即使仅实现它们,这些字典也被称为对象成为对象意味着三分之一。

因此,如果没有嵌套函数,ECMAScript中将没有封装,最重要的是,没有对象!甚至模块和类在嵌套函数之上也主要是语法糖。

关于javascript - 嵌套函数的用途或优点是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54968732/

相关文章:

javascript - 如何检查名称是否已存在于 Firebase 实时数据库(Firebase 和 JS)中

javascript - 根据路线 react 路由器导航栏

information-hiding - 什么是嵌套函数?他们有什么用?

swift - Swift 中的嵌套函数

python - 在 Python 的双重嵌套函数中访问变量

javascript - 在功能性 React 组件中定义处理程序的正确方法?

javascript - 语法错误 : missing ; before statement [JSONP, Instagram API]

javascript - 传递链接数

c# - 这个json格式正确吗?

xcode - 如何在 Xcode Debugger 中调用嵌套的 Swift 函数?