当前位置: 首页 > news >正文

JavaScript 代码不嵌套主义

文章目录

  • 前言
  • 一、何为嵌套代码
  • 二、避免嵌套
    • 1.提炼抽取
    • 2.反转排列
  • 总结


前言

看过不少过度嵌套的代码, 我真正意识到问题的严重性是刚入职那会, 我在一个老项目里看到了40个连续的else if, 套了6层的if, forforEach, 因为我们并没有做什么限制代码嵌套的提前约定.
呃, 那之后认识到会写代码代码写得好完全是两种概念, 能够实现复杂的需求也并不能说明代码写得好, 开始注重代码结构方面.

事实是, 很多时候需要编写的逻辑本身就很恶心, 乍看之下, 堆页岩般的判定嵌套里似乎每一层都是必要的, 也只能说尽量让它看起来不那么恶心.

嗯, 比如少来几次Tab.


一、何为嵌套代码

嵌套代码是在函数内部添加更深层级的代码块, 放在javascript里, 常用的嵌套手段都包含符号’{‘, 那么对于一个代码块, 刨除平级的情况, 其内部的’{'越多就说明这个代码块的嵌套深度越大.
也就是: 禁止套娃.
对于以下代码, 它的嵌套深度为1:

function fun1 () {console.log(1);
}

而如果在内部加上if语句, 其深度将变为2:

function fun1 () {if (true) {console.log(1);}
}

而如果再加一个循环进去, 深度将变为3:

function fun1 () {if (true) {for (let i = 0; i < 5; i++) {console.log(1);}} else {console.log(2);}
}

而…
好的各位, 我最多最多就到这了, 再套下去我就要开始觉得恶心了.在这里可能没有那么直观, 而这段代码放在编辑器里, console.log前面已经有三道竖线了, 光是tab提行就已经开始不舒服了.
在三层嵌套以上, 你所做的一切就不再是一套单一的算法, 这已经开始逐渐演变为多个算法的组合了, 是可以做一些封装抽离而最好不要就这样混写在一起.
实战中三层嵌套绝对连半数以下的计算都处理不了, 那如果还有逻辑没编写呢.


二、避免嵌套

1.提炼抽取

提炼(Extraction), 我一般管这叫抽离, 当然, 不一定要抽到外面, 只要能维持嵌套深度处于稳定的水平就好(不过函数内实在不能在消减嵌套深度那还是抽到外面形成另外一个函数吧).
比如这段嵌套:

function fun1() {const arr = [1, 2, 3, 4]if (arr.length = 4) {arr.forEach((ele) => {if (a === 4) {console.log(4);}});}
}fun1();

可以改为这样:

function fun1() {const arr = [1, 2, 3, 4]const xxx = (a) => {if (a === 4) {console.log(4);}}if (arr.length = 4) {arr.forEach(xxx);}
}fun1();

嵌套深度由4减小为3.

原理十分明了, 就好像在原生环境获取DOM, 有的人喜欢这样:

function change() {document.querySelector("#scar").style.display = 'none';
}

有的人喜欢:

function change() {const scar = document.querySelector("#scar");scar.style.display = 'none';
}

抽离提炼就类似于将前者转化为了后者.
封装axios也是一样的道理(不过那更多还是为了避免接口变动导致的被动局面).


2.反转排列

反转(Inversion), 对于判定语句, 把正面条件排在负面条件前通常会需要更多的判定, 所以改为优先处理负面条件.
先把正面条件放前面:

function justice(e) {if(e.length > 5) {for(let i = 0; i < e.length; i++) {console.log(e);}} else if (e.length === 2){return 2;} else {return false}
}

但是如果先进行负面条件判定:

function justice(e) {if(e.length === 2) {return 2;} else if (e.length < 5) { // 这里也可以另起一个if, 不过这样可以节约一行 )return false;} for(let i = 0; i < e.length; i++) {console.log(e);}
}

可以看到现在深度层级由3减小到2.
这种优化方法需要先把少数, 需要特殊处理的情况在前面解决完及时退出, 剩下的多数情况就可以不放在判定语句中.
而在这个过程中, 需要把最特殊, 且不将其他特殊情况包含在内的情况写在前面, 越特殊, 越提前处理, 此处e.length === 2为最特殊, 而e.length < 5这个特殊情况将e.length === 2包含在内, 所以应当第二个处理.
我在前面也写过这种做法, 将判定嵌套改为平次的卫语句, 称作validation gatekepping, 感兴趣的话可以去看这篇:
JavaScript 如何简化代码里的多级判定?

不过还可以在平次判定这个基础上使用这个技巧, 我们把负面情况放在靠前的平次判定处理, 如果处理中途出现过多嵌套, 那就提炼抽离, 把正面条件放最后:

function justice(e) {if(e.length === 2) {return 2;}if(e.length === 3) {return 3;}if (e.length < 5) {return false;} for(let i = 0; i < e.length; i++) {console.log(e);}
}

截取最近项目里的代码作为例子, 现在有两个world, 一个新一个旧, 如果需要让旧world的视图更新, 那么需要将新world的world.webglGroup.children内的元素部分替换, 其他除world.frameInfo外也要全替换.

async changeWorld(oldFrame, newWorld) {for (const key in newWorld) {if (key === 'frameInfo') {} else if (key === 'webglGroup') {for (const pro in newWorld[key]) {if (pro === 'children') {this.worldList[oldWorldIndex][key][pro] = this.worldList[oldWorldIndex][key][pro].filter((ele) => { return ele.type !== 'Group' });this.worldList[oldWorldIndex][key][pro].push(...newWorld[key][pro].filter((ele) => { return ele.type === 'Group' }));} else {this.worldList[oldWorldIndex][key][pro] = newWorld[key][pro];}}} else {this.worldList[oldWorldIndex][key] = newWorld[key];}}
}

以上是初版, 现在用Extraction提炼和Inversion反转去尝试降低嵌套深度:
先把world.webglGroup.children局部替换的代码提炼为replace,
已知world.frameInfo不需要替换, 那么正常的负面条件写法应当为key === 'frameInfo', 但即便如此key === 'frameInfo'key === 'webglGroup'也是必须用else if处理的, 如果改成平次if又不能终止执行, 那么这两个特殊条件在一轮循环中都会被执行.

本着要把正面条件处理方案写最后的原则, 直接在最后一个特殊条件不满足(按照上文所述写法, 最后一个特殊条件不满足说明前面所列特殊条件均不满足)时执行正面条件处理方案.

else if (key !== 'frameInfo') {this.worldList[oldWorldIndex][key] = newWorld[key];
}
async changeWorld(oldFrame, newWorld) {let oldWorldIndex = this.worldList.findIndex((w) => w.frameInfo.frame === oldFrame);const replace = () => {this.worldList[oldWorldIndex][key]['children'] = this.worldList[oldWorldIndex][key]['children'].filter((ele) => { return ele.type !== 'Group';});this.worldList[oldWorldIndex][key]['children'].push(...newWorld[key]['children'].filter((ele) => { return ele.type === 'Group';}));}for (const key in newWorld) {if (key === 'webglGroup') {replace(key);} else if (key !== 'frameInfo') {this.worldList[oldWorldIndex][key] = newWorld[key];}}return this.worldList[oldWorldIndex];
}

只是判定需求不同罢了.
上面这种写法是在所有负面条件不满足时执行正面条件处理方案.
前面反转的例子是在任意负面条件不满足时结束执行.
但遵循两种优化手段的原则都可以实施优化.


总结

相关文章:

JavaScript 代码不嵌套主义

文章目录前言一、何为嵌套代码二、避免嵌套1.提炼抽取2.反转排列总结前言 看过不少过度嵌套的代码, 我真正意识到问题的严重性是刚入职那会, 我在一个老项目里看到了40个连续的else if, 套了6层的if, for和forEach, 因为我们并没有做什么限制代码嵌套的提前约定. 呃, 那之后认…...

使用默认参数的4大要点

概述 默认参数是C中新增的特性。在C中&#xff0c;可以为函数的参数指定默认值。调用函数时&#xff0c;如果没有指定实参&#xff0c;则自动使用默认参数。默认参数的基本语法这里就不作介绍了&#xff0c;下面重点介绍使用默认参数的一些知识要点。 基本规则 1、当函数中某个…...

Linux文件系统中的硬链接及常见面试题

如果能对inode的概念有所了解&#xff0c;对理解本文会有所帮助。如果对inode的概念不太清楚也没有关系&#xff0c;我们会捎带介绍一下。在文件系统的实现层面&#xff0c;我们可以认为包含两个组件&#xff1a;一个是包含数据块的池子&#xff0c;池子中的数据块是等大小的&a…...

opencv-StereoBM算法

原理解释目前立体匹配算法是计算机视觉中的一个难点和热点&#xff0c;算法很多&#xff0c;但是一般的步骤是&#xff1a;A、匹配代价计算匹配代价计算是整个立体匹配算法的基础&#xff0c;实际是对不同视差下进行灰度相似性测量。常见的方法有灰度差的平方SD&#xff08;squ…...

图像分类竞赛进阶技能:OpenAI-CLIP使用范例

OpenAI-CLIP 官方介绍 尽管深度学习已经彻底改变了计算机视觉&#xff0c;但目前的方法存在几个主要问题:典型的视觉数据集是劳动密集型的&#xff0c;创建成本高&#xff0c;同时只教授一组狭窄的视觉概念;标准视觉模型擅长于一项任务且仅擅长于一项任务&#xff0c;并且需要大…...

Metasploit框架基础(一)

文章目录前言一、基础认知二、批量POC/EXP的构想三、poc检测框架的简单实现四、xray五、Meatsploit框架参考前言 Metasploit 一款渗透测试框架漏洞利用的集合与构建和定制满足你的需求的基础漏洞利用和验证的工具 这几个说法都是百度或者官方文档中出现的手法&#xff0c;说…...

pytorch零基础实现语义分割项目(二)——标签转换与数据加载

数据转换与加载项目列表前言标签转换RGB标签到类别标签映射RGB标签转换成类别标签数据数据加载随机裁剪数据加载项目列表 语义分割项目&#xff08;一&#xff09;——数据概况及预处理 语义分割项目&#xff08;二&#xff09;——标签转换与数据加载 语义分割项目&#x…...

python(8.5)--列表习题

目录 一、求输出结果题 二、计算列表元素个数 三、查找是否存在某元素 四、删除某元素 五、如何在列表中插入元素 六、如何从列表中删除重复的元素 七、 如何将列表中的元素按照从小到大的顺序排序 八、从列表中删除重复的元素 九、大到小的顺序排序 一、求输出结…...

rt-thread pwm 多通道

一通道pwm参考 https://blog.csdn.net/yangshengwei230612/article/details/128738351?spm1001.2014.3001.5501 以下主要是多通道与一通道的区别 芯片 stm32f407rgt6 1、配置PWM设备驱动相关宏定义 添加PWM宏定义 #define BSP_USING_PWM8 #define BSP_USING_PWM8_CH1 #d…...

C语言练习 | 初学者经典练习汇总

目录 1、下面代码输出多少&#xff0c;为什么&#xff1f; 2、你要好好学习么&#xff1f; 3、一直写代码&#xff0c; 4、两个数求最大值 5、输入1-5输出工作日&#xff0c;输入6-7输出休息日&#xff0c;其他输入错误 6、写一个输入密码的代码 7、怎么样当输入数字时候…...

华为OD机试 - 自动曝光(Python) | 机试题算法思路 【2023】

最近更新的博客 华为OD机试 - 卡片组成的最大数字(Python) | 机试题算法思路 华为OD机试 - 网上商城优惠活动(一)(Python) | 机试题算法思路 华为OD机试 - 统计匹配的二元组个数(Python) | 机试题算法思路 华为OD机试 - 找到它(Python) | 机试题算法思路 华为OD机试…...

「6」线性代数(期末复习)

&#x1f680;&#x1f680;&#x1f680;大家觉不错的话&#xff0c;就恳求大家点点关注&#xff0c;点点小爱心&#xff0c;指点指点&#x1f680;&#x1f680;&#x1f680; 目录 第五章 相似矩阵及二次型 &2&#xff09;方阵的特征值与特征向量 &3&#xff…...

1.1 硬件与micropython固件烧录及自编译固件

1.ESP32硬件和固件 淘宝搜ESP32模块,20-50元都有,自带usb口,即插即用. 固件下载地址:MicroPython - Python for microcontrollers 2.烧录方法 为简化入门难度,建议此处先使用带GUI的开发工具THonny,记得不是给你理发的tony老师. 烧录的入口是: 后期通过脚本一次型生成和烧…...

【MySQL进阶】视图 存储过程 触发器

&#x1f60a;&#x1f60a;作者简介&#x1f60a;&#x1f60a; &#xff1a; 大家好&#xff0c;我是南瓜籽&#xff0c;一个在校大二学生&#xff0c;我将会持续分享Java相关知识。 &#x1f389;&#x1f389;个人主页&#x1f389;&#x1f389; &#xff1a; 南瓜籽的主页…...

[Linux篇] Linux常见命令和权限

文章目录使用XShell登录Linux1.Linux常用基本命令&#xff1a;1.1 ls&#xff08;列出当前的目录下都有哪些文件和目录&#xff09;1.2 cd (change directory 切换目录)1.3 pwd&#xff08;查看当前目录的绝对路径&#xff09;1.4 touch&#xff08;创建文件&#xff09;1.5 ca…...

29岁从事功能测试被辞,面试2个月都找不到工作吗?

最近一个28岁老同学联系我&#xff0c;因为被公司辞退&#xff0c;找我倾诉&#xff0c;于是写下此文。 他是14年二本毕业&#xff0c;在我的印象里人特别懒&#xff0c;不爱学习&#xff0c;专业不好&#xff0c;毕业前因为都没找到合适工作&#xff0c;直接去创业了&#xf…...

【C#个人错题笔记1】

观前提醒 记录一些我不会或者少见的内容&#xff0c;不一定适合所有人 字符串拼接 int a3,b8; Console.WriteLine(ab);//11 Console.WriteLine("ab");//ab Console.WriteLine(a""b);//38 Console.WriteLine("ab"ab);//ab38 Console.WriteLine…...

基于lambda的mongodb查询插件

需求背景需要一个像mybatis plus 一样的基于lambda, 且面向对象的查询mongo数据的插件。在网上找了很久&#xff0c;没有发现有类似功能的插件。于是自己手写了一个&#xff0c;借助mongoTemplate屏蔽了底层查询语句的实现细节。在此基础上&#xff0c;实现了查询的统一封装。技…...

基于微信小程序的微信社团小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…...

GEE学习笔记 七十三:【GEE之Python版教程七】静态展示影像和动态展示影像

我们使用GEE在线编辑可以直接通过在线的网页可以加载展示我们计算的结果&#xff0c;而python版的GEE要展示我们的计算结果可能就比较麻烦。如果有同学看过GEE的python版API中可以找到一个类ee.mapclient&#xff0c;这个类的介绍是它是GEE官方通过Tk写的一个加载展示地图的类。…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机

这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机&#xff0c;因为在使用过程中发现 Airsim 对外部监控相机的描述模糊&#xff0c;而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置&#xff0c;最后在源码示例中找到了&#xff0c;所以感…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...