当前位置: 首页 > 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写的一个加载展示地图的类。…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

基于数字孪生的水厂可视化平台建设:架构与实践

分享大纲&#xff1a; 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年&#xff0c;数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段&#xff0c;基于数字孪生的水厂可视化平台的…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

服务器--宝塔命令

一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行&#xff01; sudo su - 1. CentOS 系统&#xff1a; yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…...