闭包是js的特性,也是区分其他语言的关键特点之一。
偶然浏览博客,看到一个据说是 jQuery 某个版本实现函数重载的一个方法,出自 jQuery 作者 John Resig:
function addMethod(object, name, f) { var old = object[name]; object[name] = function() { if (f.length === arguments.length) { return f.apply(this, arguments); } else if (typeof old === "function") { return old.apply(this, arguments); } }; }
下面的代码可以展示其重载的实现效果:
const Obj = {}; const f1 = () => { console.log("no prm"); }; const f2 = (x) => { console.log("one prm", x); }; const f3 = (x, y) => { console.log("two prms", x, y); }; addMethod(Obj, "find", f1); addMethod(Obj, "find", f2); addMethod(Obj, "find", f3); Obj.find()
个人觉得这种用闭包的方式存在很大的问题,通过 old 串联三个闭包,导致的结果就是最先重载的方法需要跨越多个闭包实现,可以通过打印直观的看出来;
function addMethod(object, name, f) { var old = object[name]; object[name] = function() { console.log("span") if (f.length === arguments.length) { return f.apply(this, arguments); } else if (typeof old === "function") { return old.apply(this, arguments); } }; } // span span span no prm
第一个无参函数的执行跨越了两层作用域,可见这种方式虽然节省了变量,但是一方面让代码可读性变得晦涩,另一方面使用了闭包的缺点,而非优点,我会使用下面的方式实现:
const addMethod = (obj, type, f) => { obj[f.length] = f; obj[type] = (...args) => { console.log("span"); return obj[args.length](...args); }; }; Obj.find(); // span no prm
这样任何重载方法的执行都不需要跨作用域去查找,可能有人觉得为什么要在对象挂载一些无关的参数,这样太草率了,没有章法。这里也只是为了说明同样的重载可以有更好的选择,本身重载的方法就不是非得挂到对象上,单独实现反而利于函数式编程。