如何优雅地重构两个高度相似的JavaScript函数
重构高度相似的JavaScript函数:优雅合并重复代码
提问场景
我有两个高度相似的JavaScript函数,希望将代码优雅地重构为单一函数,仅少数几行代码存在差异——其中一个函数比另一个多3行代码。
原始代码:
function doStuffForFacebook() { var example = "toto" //about 7 lines of code resizeScale = 1 } function doStuffforYoutube() { var example = "toto" //the VERY SAME 7 lines of code as those inside doStuffForFacebook() //the 3 lines which are different from above if ( typeof cryptoVideoX !== "undefined" && cryptoVideoX !== null && cryptoVideoX !== 0 ) { var vidRatio = cryptoVideoX / netVideoY; } else { //no crypto video add-on by source var vidRatio = netVideoRatio; } var manually_recalculated_width_to_have_full_screen_height_video = Math.round(vidRatio * winHeight);// fb only accepts rounded values $fbPlayer.attr('data-width', manually_recalculated_width_to_have_full_screen_height_video ); resizeScale = 1 }重复这7多行代码只为了几行差异,我认为可以优化。我考虑过使用回调的方式实现,如下所示:
function doStuffForFacebook() { resizeVideo(null);//nil because here no callback is necessary } function doStuffforYoutube() { resizeVideo(addSomeCodeCallback); } function resizeVideo(callback) { var example = "toto" //the 7 lines of common CODE callback(); resizeScale = 1 } function addSomeCodeCallback() { if ( typeof cryptoVideoX !== "undefined" && cryptoVideoX !== null && cryptoVideoX !== 0 ) { var vidRatio = cryptoVideoX / netVideoY; } else { //no crypto video add-on by source var vidRatio = netVideoRatio; } var manually_recalculated_width_to_have_full_screen_height_video = Math.round(vidRatio * winHeight);// fb only accepts rounded values $fbPlayer.attr('data-width', manually_recalculated_width_to_have_full_screen_height_video ); }我知道可以拆分前后逻辑为单独函数,但不想这么做,因为这些逻辑属于大型if/else块,整体更合理。请问如何按照JavaScript最佳实践正确实现?
几种符合最佳实践的重构方案
方案一:可选参数+条件分支(最直观易维护)
这种方式适合差异代码少、平台数量不多的场景,核心逻辑集中,可读性拉满:
// 核心通用函数,接收平台标识参数 function doStuff(platform) { var example = "toto"; // 7行通用代码 // 仅针对Youtube执行差异化逻辑 if (platform === 'youtube') { // 用三元表达式简化原有的if/else,更简洁 const vidRatio = typeof cryptoVideoX !== "undefined" && cryptoVideoX !== null && cryptoVideoX !== 0 ? cryptoVideoX / netVideoY : netVideoRatio; // 优化变量名,提升语义化 const fullScreenWidth = Math.round(vidRatio * winHeight); $fbPlayer.attr('data-width', fullScreenWidth); } resizeScale = 1; } // 保留原有的入口函数,兼容旧调用逻辑 function doStuffForFacebook() { doStuff('facebook'); } function doStuffForYoutube() { doStuff('youtube'); }
方案二:优化回调方案(函数式风格,灵活度高)
如果你偏爱回调的思路,可以优化掉null传参的尴尬,直接用匿名回调内联差异逻辑,代码更紧凑:
function doStuff(customLogic) { var example = "toto"; // 7行通用代码 // 仅当传入有效回调时才执行,避免报错 typeof customLogic === 'function' && customLogic(); resizeScale = 1; } function doStuffForFacebook() { doStuff(); // 无需传null,简洁清晰 } function doStuffForYoutube() { // 直接把差异逻辑写成匿名回调传入,不用单独定义函数 doStuff(() => { const vidRatio = typeof cryptoVideoX !== "undefined" && cryptoVideoX !== null && cryptoVideoX !== 0 ? cryptoVideoX / netVideoY : netVideoRatio; const fullScreenWidth = Math.round(vidRatio * winHeight); $fbPlayer.attr('data-width', fullScreenWidth); }); }
方案三:配置对象模式(扩展性最强)
如果未来可能新增更多平台,或者差异逻辑会变得复杂,用配置对象的方式更符合开闭原则——新增平台无需修改核心函数,只需要添加配置项:
// 定义各平台的差异化逻辑配置 const platformConfigs = { facebook: { // Facebook不需要额外逻辑,留空函数 afterCommonLogic: () => {} }, youtube: { afterCommonLogic: () => { const vidRatio = typeof cryptoVideoX !== "undefined" && cryptoVideoX !== null && cryptoVideoX !== 0 ? cryptoVideoX / netVideoY : netVideoRatio; const fullScreenWidth = Math.round(vidRatio * winHeight); $fbPlayer.attr('data-width', fullScreenWidth); } } }; function doStuff(platform) { var example = "toto"; // 7行通用代码 // 执行对应平台的差异化逻辑 platformConfigs[platform].afterCommonLogic(); resizeScale = 1; } // 入口函数保持不变 function doStuffForFacebook() { doStuff('facebook'); } function doStuffForYoutube() { doStuff('youtube'); }
选择建议
- 若当前仅两个平台、差异代码极少:优先选方案一,维护成本最低,新人接手也能快速理解;
- 若差异逻辑可能单独复用,或喜欢函数式编程:选方案二,灵活度更高;
- 若未来有扩展多平台的计划:选方案三,符合代码设计的开闭原则,扩展性拉满。
内容的提问来源于stack exchange,提问作者Mathieu
相关产品推荐
相关产品推荐

