Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

让函数进化 #13

Open
classicemi opened this issue Nov 5, 2015 · 1 comment
Open

让函数进化 #13

classicemi opened this issue Nov 5, 2015 · 1 comment

Comments

@classicemi
Copy link
Owner

假设现在有这样一个场景,一个下单页面,需要根据特定的条件去计算购物车商品的总价,特定的条件包括但不限于选中的商品、是否选择折扣、是否叠加套餐和运费险等。这时我们通常需要写一个用来计算总价的函数getTotalPrice(),然而,函数的参数是不确定的,同时函数内的逻辑也会根据参数的不同而有所变化,最简单的方法是在函数内部对参数进行判断:

如果参数分开传入,那么函数的伪代码可能写成这样:

function getTotalPrice(selectedGoods, isDiscounted, pacakge) {
    var totalPrice = 0;
    if (selectedGoods) {
        totalPrice = ...;
        if (isDiscounted) {
            // 计算折扣
            if (package) {...}
        } else if (pacakge) {...}
        return totalPrice;
    } else {
        return 0;
    }
}

有的人可能觉得将参数分开传入函数看起来比较乱,会习惯用一个参数对象包裹一下,在函数内部再对这个对象进行解析。看起来确实有一定的改进,但我认为并没有什么本质上的区别。

现在希望能够做到针对不同参数的情况,在函数内部只书写针对特定个数参数的逻辑,不去写乱七八糟的if-else语句或是参数对象的解析逻辑。同时,这些函数共用一个函数名,并且该逻辑可扩展,即可以随时根据需要,添加函数。

通过对需求的分析,应该会有这样的一个工具函数:

var methods = {};

addFunction(methods, 'getTotalPrice', function(selectedGoods) {...});
addFunction(methods, 'getTotalPrice', function(selectedGoods, isDiscounted) {...});
addFunction(methods, 'getTotalPrice', function(selectedGoods, isDiscounted, pacakge) {...});

这样,在添加每个函数的时候,只需要写针对特定参数的逻辑,调用getTotalPrice的时候,自动根据参数长度实际调用不同的函数。

很容易想到要对不同的函数进行缓存,同时为了公用同一个函数名,缓存的函数需要匿名,进而联想到闭包可以保存局部变量的引用的特性,以下是addFunction的实现:

function addFunction(object, funcName, fn) {
    var oldFunc = object[funcName];
    object[funcName] = function() {
        return (fn.length === arguments.length ? fn : oldFunc).apply(this, arguments);
    }
}

addFunction方法的作用域中,保存了原方法的引用,通过对参数长度的比较确定最终执行哪个匿名函数,要注意的是一个函数的length属性表示改函数接受的形参数量,而arguments.length表示实际传入的参数数量。匿名函数访问oldFuncfn则是典型的闭包的应用,并没有把函数存储在任何典型的数据结构中。

addFunction(methods, 'getTotalPrice', function(total) {
    console.log(total);
});
addFunction(methods, 'getTotalPrice', function(total, discount) {
    console.log(total * discount);
});

methods.getTotalPrice(20); // 输出20
methods.getTotalPrice(20, 0.8); // 输出16

要注意的是,这种方式只能针对不同数量的参数,不能判断参数的名称或类型,如果要判断的话,势必会牺牲addFunction方法的通用性。另外,每添加一个函数,就会增加一层嵌套,调用深度过深的话性能开销也会比较大,使用的时候要进行权衡。

@gideonstele
Copy link

这算是利用闭包实现类似方法重载的方案了,很精髓啊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants