本文同步自我的 GitHub
假设现在有这样一个场景,一个下单页面,需要根据特定的条件去计算购物车商品的总价,特定的条件包括但不限于选中的商品、是否选择折扣、是否叠加套餐和运费险等。这时我们通常需要写一个用来计算总价的函数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
表示实际传入的参数数量。匿名函数访问oldFunc
和fn
则是典型的闭包的应用,并没有把函数存储在任何典型的数据结构中。
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
方法的通用性。另外,每添加一个函数,就会增加一层嵌套,调用深度过深的话性能开销也会比较大,使用的时候要进行权衡。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。