✨
blog
  • Blog
  • Element-UI
    • 2019-09-04
  • JS
    • ES6 之 Set 和 Map
    • let 和 const 声明常见概念
    • 元编程
    • ES6之字符串的扩展
    • ES6 之异步流程的前世今生(上)
    • ES6之异步流程的前世今生(下)
    • ES6 之模块你知吗
    • ES6 之解构赋值与箭头函数的妙用
    • 迭代器
    • ES5 之原型(一)
    • ES6之类(二)
    • es7之装饰器
    • es6之数组详解
    • js之this指向
    • 对象
    • vue项目配合使用canvas联动
    • 本文解决痛点:对象里面是否有值
  • MAC
    • vue源码之method
    • Mac的使用技巧
    • 前文
    • Mac常用软件(二)
    • 如何查看 Mac 端口号以及占用情况
  • Node
    • Node之Buffer详解
    • 浏览器与 node 的事件循环(event loop)有何区别
    • Node之多线程
    • node之模块解析(一)
    • 错误捕获与内存告警
  • TS
    • Record
    • 使用方法
    • 工具泛型
    • 类型体操
    • 泛型
  • chrome
    • v8 引擎
    • v8 垃圾回收机制
    • 浏览器的知识
  • flutter
    • 路由
    • 页面布局
  • go
    • index
  • html&css
    • 两栏布局
    • ES5和ES6的区别
    • ES5 和 ES6 的区别
    • HTTP详解
    • TCP 与 UDP 的区别
    • MDN
    • css modules 使用教程
    • css 居中
    • 拖拽
    • flex布局
    • h5 新增特性 html5
    • history 与 hash 路由策略
    • position 定位方式
    • rem布局
    • svg
    • web性能优化
    • 事件循环
    • 从输入网址后发生了什么
    • 前端状态管理
    • 圣杯布局与双飞翼
    • 性能优化 页面的性能统计指标
    • 本地存储的几种对比
    • 浏览器的渲染进程
    • 浏览器缓存策略详解
    • 盒模型
    • 为什么要移动端适配
    • 跨域的 N 种实现方式
  • web3
    • 常见概念
    • vue项目配合使用canvas联动
  • webgl
    • Mac使用技巧(二)
    • Node之模块解析path
  • 代码库
    • documeng的一些常见操作
    • eventBus事件
    • jquery提交
    • jquery的一些常见操作
    • 常见操作
    • 数组polyfill
    • TS代码片段
      • 面试官眼中的test unit
  • 全年安排
    • AfterShip
    • 大企业
  • 函数编程题
    • Promise问题
    • 继承
  • 前端早早聊
    • vue生态
    • 开发一款VScode语言插件
    • 简历回顾和进行复盘
    • 重新认知性能优化及其度量方法
    • 2022-09-17-音视频专场.md
      • 2022-09-17-音视频专场
    • 前端晋升专场
      • 成长的诀窍是靠自己
      • 销销帮
    • 前端监控专场
      • 字节前端监控实践
      • 李港:大前端-从无到有搭建自研前端监控系统
    • 前端跳槽
      • 50个面试官在线招聘
      • 如何识别优秀的前端猎头来跳槽大厂
      • 面试套路
    • 支付宝
      • 面试
    • 管理专场
      • 芋头:管理者眼中的web技术发展前沿
    • 组件专场
      • 基于webCompents的跨技术组件库实践
    • 面试
      • 面试辅导问题
      • 早早聊面试
      • 前端沙箱是什么? 怎么实现沙箱环境?
  • 常见总结
    • 2018年终总结-年底了,你总结了吗?我先来
    • 在逆境中成长
    • 2021年终总结
    • 2024年全年总结
    • 项目
    • Tell2.0 前端复盘
    • 复盘
    • 前端工程师素养
    • 学习方法论
    • 希望与破晓| 2022 年终总结
    • 新起点, 新征途 | 掘金年度征文
    • 稳定| 2023 年终总结
    • 趁着有风快飞翔 | 2019 年终总结
    • AfterShip
      • Emotion:用 JavaScript 编写 CSS 的强大工具
      • 个人中长期目标
      • 事故复盘
      • 时间解析
      • 国内外区别
      • 独立站建设
    • MEIZU
      • NativeApp与H5通信原理
      • SSR 原理
      • SSR的常见问题
      • CLI
      • electron 应用发布流程
      • electron
      • electron 面试
      • 数据结构与算法之美
      • mgc 一期复盘
      • 架构原理
      • 喵币管理
      • 三期复盘总结
      • 异常监控之 sentry 实践
      • 微前端
      • qiankun 原理解析
      • 快游戏一期
      • 游戏中心复盘
    • 个人准则
      • index
    • 编程猫
      • pc 接入 micro bit 方案
      • prompt engineer
      • web work 跨域解析与解决方式
      • web 中的 ai
      • 低版本 node 环境下 ffmpeg 的使用
      • 关于 taobao 源 https 过期
      • 加密 json
      • 安卓 5 和 6 的白屏解决
      • 性能排查与优化实践
      • 探月接入
      • 接入硬件
      • 新生态下的state
      • monorepo 包管理方式
      • 自修复 npm 库
      • 音频的绘制
    • 谨启
      • 音视频
      • 小程序
        • taro 规范
        • 结合 mobx 在跳转前预请求
        • Taro 浅析用法与原理
        • 前文
        • 小程序优化指南
        • 小程序内部实现原理
        • 支付相关
    • tencent
      • TAPD
        • MathJax的食用
        • canvas渲染优化策略
        • 为什么 JavaScript 是单线程的呢?
        • svg 总是不对
        • 前端库
        • 原生端和js端如何通信
        • 在旧项目中复用vue代码
        • 提升自我
        • 批量编辑优化
        • 插入业务对象
        • 编辑器
        • 挂载点
        • 性能优化对比
        • 遇到的问题
        • 项目迁移公告
        • 领导力
      • 行家
        • 实战篇
        • 职业发展、领导力、个人成长
        • 高质量沟通
  • 慕课网
    • react-native原理
    • react-native学习
  • 杂文
    • Dom 节点变动检测并录制的简单实现
    • 错误监控&错误捕获
    • NextJS与NuxtJS
    • 负载均衡的几种常用方式
    • PM2
    • service worker 控制网络请求?
    • SSL 和 TLS 的区别
    • Babel 你太美
    • echart踩坑经验
    • keyup、keydown你都知道有什么区别吗
    • 常见概念
    • 首屏加载优化与性能指标分析
    • preload 和 prefetch 的详解
    • 在项目中配置这几个关系
    • roullp 解析
    • tinymce原理浅析
    • wasm 在前端的应用
    • websocket
    • webworker
    • 项目
    • 从 ajax 到 axios
    • 从postcss 到自己开发一款插件
    • 从输入浏览器到页面展示涉及的缓存机制
    • 代码整洁之道
    • 你知道什么是aop吗
    • 函数式编程
    • 函数式编程指南
    • 前端input框文字最大值
    • 攻坚战
    • 前端书写 sdk
    • 前端文字转语音播放
    • 前端领域的 Docker 和 Kubernetes
    • 前端安全
    • 前端进阶之内存空间
    • 前端音频浅析
    • 十分钟搞定多人协作开发
    • 字符串的比较
    • 尾递归
    • 前文
    • 常见的算法可以分为以下三类
    • 手机调试--mac篇
    • 数组的原生系列
    • COOP 和 COEP - 新的跨域策略
    • 浅谈react组件书写
    • 浏览器与 Node.js 事件循环的区别
    • 由三道题引伸出来的思考
    • 移动端300ms点击延迟
    • 移动端和pc端事件
    • Git 常见疑惑
    • 我们离发 npm 包还有多远
    • 重绘和重排
    • AI 时代下的前端编程范式
    • 音频可视化实战
  • 极客时间
    • Serverless入门课
    • 二分查找
    • 二叉树
    • 全栈工程师
    • 动态规划面试宝典
    • 前端与rust
    • 散列表
    • 前端方面的Docker和Kubernetes
    • 栈
    • 深入浅出区块链
    • 玩转 vue 全家桶
    • 玩转 webpack
    • 程序员的个人财富课
    • 算法
    • 说透元宇宙
    • 跳表
    • 链表
    • 10x 程序员工作法
      • index
    • Node开发实战
      • HTTP服务的性能测试
      • JavaScript语言精髓与编程实战
      • 什么是node。js
      • svg精髓
    • ReactHooks核心原理与实战
      • ReactHooks核心原理与实战
    • Rust
      • Rust编程第一课
      • 前置篇
      • 深度思维
      • 重构
      • 类型体操
      • 基础知识
    • WebAssembly入门课.md
      • 基础篇
      • SSR的注水和脱水
      • jsBriage通信原理
      • 基础知识篇
    • 互联网的英语私教课
      • 互联网人的英语私教课
    • 代码之丑
      • 代码之丑
    • 前端全链路优化实战课
      • 网页指标
    • 图解 Google V8
      • 图解 Google V8
    • 浏览器工作原理与实践
      • 浏览器工作原理与实践
    • 算法面试通关 40 讲
      • 算法面试通关40讲
    • 跟月影学可视化
      • index
    • 软件设计之美
      • 软件设计之美
    • 重学前端
      • js
  • 后续的文件增加都会增加到上面并以编号对应
    • 1029. 两地调度
    • 151.翻转字符串里的单词
    • 2022.3.15
    • 前端数据结构
    • 前端常见算法
    • 前端常见排序
    • 恢复一棵树
  • 设计模式
    • 前端常见设计模式之MVC与MVVM
    • 前端之代理模式
    • 前端常见设计模式之单例模式
    • 前端常见设计模式之发布订阅模式
    • 前端之工厂模式
    • 观察者模式
    • 前端常见设计模式之适配器模式
  • 译文
    • [译] 如何使用CircleCI for GitHub Pages持续部署
    • 您是否优化了 API 的性能
    • [译][官方] Google 正式发布 Flutter 1.2 版本
    • 什么是 Deno ,它将取代 NodeJS ?
  • 读后感
    • JavaScript二十年
    • 1368个单词就够了
    • js编程精解
    • labuladong 的算法小抄
    • lodash常用方法
    • vue的设计与实现
    • 所有的静态资源都是get请求
    • 人生
    • 人生护城河
    • 你不知道的JavaScript
    • 前端核心知识进阶
    • 华为工作法
    • 反脆弱
    • 好好学习
    • 左耳听风
    • 摩托车维修之道
    • 数学之美
    • 深入理解svg
    • 浏览器的ESM到底是啥
    • 经济学原理
    • 编程珠玑
    • 防御式 css 精讲
    • 韭菜的自我修养
  • 雪狼
    • 2022-07-17
    • 基础知识
    • 阶一课程
      • 实战辅导一
      • 实战辅导二
  • 嵌入式
    • 树莓派
      • 排序
  • 源码
    • React
      • 核心知识点
      • errorBoundaries
      • immutable.js 的实现原理
      • React.Suspense
      • react源码分析之Fiber
      • batchedUpdate
      • Component
      • Context
      • react 源码分析之 diff 算法
      • React 中的 key 属性:原理、使用场景与注意事项
      • 使用方式
      • react源码分析之memo
      • react 源码分析之mixin
      • 实战篇
      • react源码分析之react-dom
      • 使用方式
      • scheduleWork
      • useImperativeHandle的使用与原理
      • React 书写小技巧
      • 入口和优化
      • 合成事件和原生事件的区别
      • react 性能优化
      • 构建一个 hooks
      • 浅析 styled-components
      • 生命周期
      • 组合 vs 继承
      • 通信机制
      • 高阶组件
      • 慕课网
        • 应用篇
        • 课程导学
    • ReactHook
      • useCallback
      • useContext
      • useEffect 与 useLayoutEffect
      • useHook
      • useMemo
      • useReducer
      • 原理
      • useState
      • 总结
    • Redux
      • mobx 原理解析
      • redux-saga
      • redux-thunk
      • Mobx 和 Redux 对比
      • 使用方法
      • redux 原理
    • Vite
      • Vite原理
      • Vite配置
      • 热更新原理
      • vite 为什么生产环境用 Rollup
    • Webpack
      • PostCSS
      • Webpack5 核心原理与应用实践-loader
      • Webpack5 核心原理与应用实践-plugin
      • Webpack5 核心原理与应用实践
      • 区分
      • 升级详情
      • treeShaking(树摇Tree Shaking)
      • 编写一个自己的webpack插件plugin
      • 代码分离(code-splitting)
      • webpack 打包优化
      • 基础配置
      • webpack 打包优化
      • webpack 工作原理
      • webpack 按需加载原理
      • webpack 热更新 HMR(Hot Module Replacement)
      • 缓存
      • webpack 自定义 plugin
    • next
      • tailwind
      • 什么是水合
    • sveltejs
      • index
    • tinymce
      • 并发篇
    • 源码手写系列
      • create
      • call
      • bind
      • call
      • es6 单例
      • forEach vs Map
      • instanceOf
      • new
      • reduce
      • 取两个重复数组的交集
      • 函数柯理化
      • 动态规划
      • 基于Generator函数实现async
      • 新建 js 文件
      • 手写一个 slice 方法
      • 手写一个 webpack loader
      • Plugin
      • 手写一个寄生组合式继承
      • 二叉树
      • 链表相关的操作
      • 手动实现发布订阅
      • 数组去重
      • 数组扁平化
      • 数组
      • 构造大顶堆和小顶堆
      • 深浅拷贝 深拷贝
      • 两者对比
    • vue
      • vue2
        • vm.attrs与$listeners
        • vue 和 react 的 diff 算法比较
        • vue 源码分析
        • vue 优化的 diff 策略
        • extends
        • 核心原理篇
        • keep-alive
        • vue 源码分析之 mixins
        • vue 源码分析之 nextTick
        • vue之slot
        • vnode
        • vue 源码分析之 watch
        • 原理
        • vue 源码分析之transition
        • vue 源码分析之异步组件
        • 调用的是 watch
        • 安装
        • react源码分析之portals
        • event 的实现原理(事件的实现原理)
        • 什么是h
        • 分析provide 和 inject
        • vue 源码分析之 use
        • v-model
        • vue源码分析之vuex
        • 响应式原理
        • 初始化的流程
        • 组件更新
        • 编译
        • 父子组件生命周期
        • 原理
        • 多实例
        • Vue 面试
        • 源码研读一
        • 响应式原理
        • 常见问题
        • 数组的劫持
        • vue之自定义指令
        • 运行机制全局概览
      • vue3相比vue2的提升点
        • vue composition api
        • vue3的虚拟dom优化
        • vue3层面的双向数据绑定
        • 预处理优化
  • 重构
    • notification
      • 讲解
  • 面试
    • AfterShip经历
      • JS对URL进行编码和解码
      • ShippingLabelTemplate
      • 接入keycloak详解
      • reCAPTCHA接入
      • yalc与动态解决升级的依赖包
      • RBAC 简介
      • 多语言计划
      • 接入Google登录及其主动弹出快捷登录方式
      • 读书计划
        • 传染
        • 这就是OKR
    • 编程猫经历
      • 2024.1.16
      • 2025.2.20
      • 2025.2.21
      • 2025.2.26
      • 2025.3.28
      • 2025.3.3
      • 2025.3.7
      • 行动轨迹
      • 面试主观题
    • 腾讯经历
      • 2022.02.21
      • 2022.03.30
      • 2022.04.24
      • 2022.04.25
      • 2022.04.27
      • 2022.04.28
      • 2022.04.29
      • 2022.05.05
      • 不同公司的面试关注点不同
      • 2022.05.07
      • 2022.05.09
      • 2022.05.10
      • 2022.05.11
      • 2022.05.12
      • 2022.05.13
      • 2022.05.16
      • 2022.05.17
      • 2022.05.19
      • 2022.05.27
      • 面试
      • 行动轨迹
      • 面试主观题
    • 针对字节
      • 2022.05.14
      • 2022.05.17
      • HR面试准备
      • Promise的相关题目
      • React 进阶实践指南(二)
      • React 面试准备
      • vue 与 react 有什么不同 (react 和 vue 有什么区别)
      • TypeScript 全面进阶指南
      • cookie和session区别
      • express 面试准备 koa 中间件原理
      • next面试准备
      • requestCallBack
      • interface 与 type 异同点
      • 取消 promise
      • 如何设计一个前端项目
      • 进阶篇
      • 早早聊面试准备
      • 自动化部署
      • 挖掘项目的深度
      • 面试
      • 出题指数
    • 魅族经历
      • 2020.09.11
      • 一灯
      • 一灯
      • 一灯
      • 2020.09.20
      • 2020.09.21
      • 网易二面
      • 2020.09.23
      • 头条
      • 360 金融面试题
      • 富途一面
      • 算法
      • 字节
      • 2020.11.04
      • baidu 一面
      • meta 标签的作用
      • 字节
      • 2020.11.22
      • 2020.11.25
      • 微前端接入笔记
      • 面试的基本原则
由 GitBook 提供支持
在本页
  • 概述篇
  • 变量篇
  • 复杂判断
  • 函数
  • 重构篇

这有帮助吗?

  1. 杂文

代码整洁之道

普通人读的懂的代码, 才是好代码 --- 沃滋基梭德

概述篇

  1. 代码可读性 代码必须是对人类是可读的。不要考虑计算机如何处理它,因为会有许多工具来转换我们的代码(编译器)。因此,最重要的是,代码将是人类可读的,因为你在开发代码时,最长的工作就是阅读代码,而不是写代码。

const users = [{ id: 1, name: "Carlos Caballero", memberSince: "1997–04–20", favoriteLanguageProgramming: ["JavaScript", "C", "Java"] }, { id: 2, name: "Antonio Villena", memberSince: "2014–08–15", favoriteLanguageProgramming: ["Go", "Python", "JavaScript"] }, { id: 3, name: "Jesús Segado", memberSice: "2015–03–15", favoriteLanguageProgramming: ["PHP", "JAVA", "JavaScript"] } ];

/***********************/

const users = [
    { id: 1, name: "Carlos Caballero", memberSince: "1997–04–20", favoriteLanguageProgramming: ["JavaScript", "C", "Java"] },
    { id: 2, name: "Antonio Villena", memberSince: "2014–08–15", favoriteLanguageProgramming: ["Go", "Python", "JavaScript"] },
    { id: 3, name: "Jesús Segado", memberSice: "2015–03–15", favoriteLanguageProgramming: ["PHP", "JAVA", "JavaScript"] },
    ];


/***********************/

const users = [{
    id: 1,
    name: "Carlos Caballero",
    memberSince: "1997–04–20",
    favoriteLanguageProgramming: ["JavaScript", "C", "Java"],
},
{
    id: 2,
    name: "Antonio Villena",
    memberSince: "2014–08–15",
    favoriteLanguageProgramming: ["Go", "Python", "JavaScript"],
},
{
    id: 3,
    name: "Jesús Segado",
    memberSince: "2015–03–15",
    favoriteLanguageProgramming: ["PHP", "JAVA", "JavaScript"],
}];


  1. 使用英语编写代码

const benutzer = {
     id: 1,
     name: "John Smith",
     mitgliedVon: "1997–04–20",
    };

    Gehaltserhöhung(benutzer, 1000); 

    /***********************/

    const użytkownik = {
     id: 1,
     imię: "John Smith",
     członekZ: "1997–04–20",
    };
    wzrostWynagrodzeń(użytkownik, 1000);

    /***********************/

    const user = {
     id: 1,
     name: "John Smith",
     memberSince: "1997–04–20",
    };
    increaseSalary(user, 1000);
  1. 团队协作 可以通过配置一个标准的 .editorconfig 文件

  • 用空格还是 Tab 来格式化代码

  • 在函数的名称旁边还是在下一行写大括号

  • 是否在语句的结尾处放一个分号

root = true

    [*]
    end_of_line = lf
    insert_final_newline = true

    [*.{js,py}]
    charset = utf-8

    [*.py]
    indent_style = space
    indent_size = 4

    [Makefile]
    indent_style = tab

    [*.js]
    indent_style = space
    indent_size = 2

    [{package.json,.travis.yml}]
    indent_style = space
    indent_size = 2

有一个在业界广泛使用的工具被称为 Prettier,它能够根据 linter 的规则实时改变我们的代码格式(IDE的插件)。

变量篇

  1. 变量名要名副其实 变量的名称必须能够描述出该变量的作用和用途。

  2. 变量名可以读出来

在选择变量的名称时,另一个错误的行为是删除一个词中某些字母,使用这些缩略语读起来很困难。首先,我们是在用英语编码,而且不是所有的开发者都是讲英语的。因此,我们为什么要把它的名字减少 3 或 4 个字符?这有什么好处呢?代码会被工具(转译器包括其他语言的编译器)操作,最终正确地完成格式化(使用 Prettier)。因此,把不能发音的名字放在一起,只能让我们更费力地去推断变量的用途。

  1. 不要在名称中使用变量的类型 在变量名称中使用数据类型作为前缀是一个很古老的做法,现在让我们反思一下这个问题。

变量名称中必须要用类型作为前缀吗? 每个公司和工程都有各自的前缀规范,那如何去学习和书写这种代码呢? 如果我在变量的名称中使用一种编程语言的类型系统,为什么要用它呢? 当变量的数据类型发生变化时,比如把 Array 修改为 Map,这种情况怎么处理? 这个前缀能给我们带来什么?它是可以发音的吗?

const aCountries = [];
const sName = "";
const dAmount = 3.2;

const countries = [];
const name = "";
const amount = 3.2;

  1. 对同一类型的变量使用相同的词汇表 对同一类型的数据使用相同的词汇表。如果我们需要检索一个用户或客户的信息,我们不能以不同的方式称呼用户或客户。也就是说,不能有时称其为 user,有时称其为 customer,甚至是 client 这个词。更不可 取的是,在变量名称上额外再加一个后缀。 下面代码就是很好的示例,同一个概念,有三个不同的定义。必须自始至终使用统一的命名,不管是 user、customer 还是 client,只能用同一个。

getUserInfo();
getClientData();
getCustomerRecord(); 

getUser();
  1. 不要添加不需要的上下文 在变量名称中没有必要添加类或包的相关上下文。 在变量名称中添加上下文是很常见的,这样可以知道这个变量位于哪个工作区。

const Car = {
    carMake: 'Honda',
    carModel: 'Accord',
    carColor: 'Blue',
}

function paintCar(car){
    car.carColor = 'red'
}

const Car = {
    make: 'Honda',
    model: 'Accord',
    color: 'Blue'
}
function paint(car){
    car.color = 'Red'
}
  1. 不要使用魔法数字和字符串 在编写代码时,不应该在源代码中直接使用数字或文本字符串,这些通常也被称为魔法数字.这个数字是什么意思?必须要解释这个数字吗?这让我们不得不思考业务逻辑之外的事情。

这些魔法数字或字符串必须存储在常量中,通过对常量的名称来表达出它们的用途。在业务逻辑层面上,对于那些有意义的数字或文本字符串,如果没有一个确切的名字就会引起噪音。

在业务逻辑层面上,对于那些有意义的数字或文本字符串,如果没有一个确切的名字就会引起噪音。

// what the heck is 86400000 for?
setTimeout(blastOff, 864000000);
user.rol = 'Administrator';

const MILLISECONDS_IN_A_DAY = 'Administrator';

setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
user.rol = ADMINISTRATOR_ROL

复杂判断

  1. 不要使用标记 (flag) 作为函数参数 这样会破坏函数功能的单一性,我们必须创建两个函数来实现各自对应的逻辑功能,而不是使用一个函数来实现两个逻辑功能,因为他们是不同的功能。

// Dirty
function book(customer, isPremium) {
  // ...
  if (isPremium) {
    premiumLogic();
  } else {
    regularLogic();
  }
}

// Clean (Declarative way)
function bookPremium (customer) {
  premiumLogic();
}

function bookRegular (customer) {
  retularLogic();
}
  1. 封装判断条件 条件封装在具有明确语义的函数中

if (platform.state === 'fetching' && isEmpty(cart)) {
    // ...
}


function showLoading(platform, cart) {
    return platform.state === 'fetching' && isEmpty(cart);
}

if (showLoading(platform, cart)) {
    // ...
}
  1. 用卫语句替换嵌套的条件语句

function getPayAmount() {
    let result;
    if (isDead){
        result = deadAmount();
    } else {
        if (isSeparated){
            result = separatedAmount();
        } else {
            if (isRetired){
                result = retiredAmount();
            }else{
                result = normalPayAmount();
            }
        }
    }
    return result;
}


function getPayAmount() {
    if (isDead) return deadAmount();
    if (isSeparated) return separatedAmount();
    if (isRetired) return retiredAmount();
    return normalPayAmount();
}
  1. 空对象 不断检查对象是否为null, 并根据该检查判断是否显示默认操作。这种模式称为空对象模式,我们默认封装了空对象行为的对象

class Dog {
  sound() {
    return 'bark';
  }
}

['dog', null].map((animal) => {
  if(animal !== null) { 
    sound(); 
  }
 });

class Dog {
  sound() {
    return 'bark';
  }
}

class NullAnimal {
  sound() {
    return null;
  }
}

function getAnimal(type) {
  return type === 'dog' ? new Dog() : new NullAnimal();
}

['dog', null].map((animal) => getAnimal(animal).sound());
// Returns ["bark", null]
  1. 使用多态删除条件 通过判断对象的类型去定义对象的方法,但是这里的条件语句被滥用了,在这种场景下,我们可以通过类的继承,为每个特定类型创建一个类,利用多态来避免使用条件判断。

function Auto() {
}
Auto.prototype.getProperty = function () {
    switch (type) {
        case BIKE:
            return getBaseProperty();
        case CAR:
            return getBaseProperty() - getLoadFactor();
        case BUS:
            return (isNailed) ? 
            0 : 
            getBaseProperty(voltage);
    }
    throw new Exception("Should be unreachable");
};



abstract class Auto { 
    abstract getProperty();
}

class Bike extends Auto {
    getProperty() {
        return getBaseProperty();
    }
}
class Car extends Auto {
    getProperty() {
        return getBaseProperty() - getLoadFactor();
    }
}
class Bus extends Auto {
    getProperty() {
        return (isNailed) ? 
                0 : 
                getBaseProperty(voltage);
    }
}
// Somewhere in client code
speed = auto.getProperty();

  1. 使用策略模式/命令模式移除条件


函数

  1. 使用默认参数去代替短路操作或条件赋值

    在大多数编程语言中,函数的参数支持设置默认值。这就意味着我们可以在代码中避免使用短路操作和条件赋值。

function setName(name) {
    const newName = name || 'Juan Palomo';
}

function setName(name  = 'Juan Palomo') {
    // ...
}
  1. 函数参数(理想情况下不多于 2 个)

当一个函数有很多参数时,可以把这些参数组合在一起构成一个对象。我们需要避免使用多个基础类型 (如字符串、数字、布尔值等) 作为函数的入参,而是要使用抽象级别更高的对象作为入参。这样我们会更接近业务逻辑,并且更加远离底层实现。

function newBurger(name, price, ingredients, vegan) {
    // ...
}

function newBurger(burger) {
    // ...
} 

function newBurger({ name, price, ingredients, vegan }) {
    // ...
}

const burger = {
    name: 'Chicken',
    price: 1.25,
    ingredients: ['chicken'],
    vegan: false,
};
newBurger(burger);

第一个例子中,我们实现了一个生产汉堡的函数,它有 4 个参数。这些参数是固定的,并且必须按照这个顺序传参,这会给我们带来很多的限制。这样的函数在使用的时候不是很灵活。

第二个例子中,最大的改进就是使用一个对象来作为参数,只需要传入一个 burger 对象就可以生产出一个 “新汉堡”。通过这种方式,我们将汉堡的基本属性整合到 1 个对象里面。

在第三个例子中,我们对传入的对象进行解构赋值,让对象的属性在函数体中可访问到,但是实际上我们传入的仅仅是一个参数,这使得这个函数有了更大的灵活性。

  1. 避免副作用 - 全局变量 副作用是未来麻烦的根源。虽然从定义上来说副作用不一定是有害的,但是如果在项目中没有节制的引起副作用,代码出错的可能性就会大大提高。 不惜一切代价避免副作用,并且确保函数是可以被测试到的。

let fruits = 'Banana Apple';

function splitFruits() {
    fruits = fruits.split(' ');
}

splitFruits();

console.log(fruits); // ['Banana', 'Apple'];

改进后

function splitFruits(fruits) {
    return fruits.split(' ');
}

const fruits = 'Banana Apple';
const newFruits = splitFruits(fruits);

console.log(fruits); // 'Banana Apple';
console.log(newFruits); // ['Banana', 'Apple'];

  1. 避免副作用 - 可变对象 是直接修改对象本身,如果你一直从事计算机相关的工作,你会知道 JavaScript 自诞生以来就是支持对象可变的,目前许多库都在尽量避免使用可变对象。

数组的方法一般被分为两部分: 一部分是会对数组本身进行修改的方法,例如: push、pop、sort,另一部分是不会对数组本身产生修改的方法例如:filter、reduce 、map 等。

const addItemToCart = (cart, item) => {
    cart.push({ item, date: Date.now() });
};

//无副作用
const addItemToCart = (cart, item) => {
    return [...cart, {
                item, 
                date: Date.now(),
            }];
};
  1. 函数应该只做一件事

function emailCustomers(customers) {
    customers.forEach((customer) => {
        const customerRecord = database.find(customer);
        if (customerRecord.isActive()) {
            email(client);
        }
    });
}

function emailActiveCustomers(customers) {
    customers
        .filter(isActiveCustomer)
        .forEach(email);
    }

function isActiveCustomer(customer) {
    const customerRecord = database.find(customer);
    return customerRecord.isActive();
}
  1. 函数应该只是有一个抽象级别 每个函数应仅具有单个抽象级别。

function parseBetterJSAlternative(code) {
    const REGEXES = [
        // ...
    ];

    const statements = code.split(' ');
    const tokens = [];
    REGEXES.forEach((REGEX) => {
        statements.forEach((statement) => {
        // ...
        });
    });

    const ast = [];
    tokens.forEach((token) => {
        // lex...
    });

    ast.forEach((node) => {
        // parse...
    });
}

const REGEXES = [ // ...];
function tokenize(code) {    
    const statements = code.split(' ');
    const tokens = [];
    REGEXES.forEach((REGEX) => {
        statements.forEach((statement) => {
            tokens.push( /* ... */ );
        });
    });
    return tokens;
}
function lexer(tokens) {
    const ast = [];
    tokens.forEach((token) => ast.push( /* */ ));
    return ast;
}
function parseBetterJSAlternative(code) {
    const tokens = tokenize(code);
    const ast = lexer(tokens);
    ast.forEach((node) => // parse...);
}
  1. 优先考虑函数式编程而不是命令式编程 函数式编程相对于命令式编程相比的另一个特点是代码更具可读性。 函数式编程在这方面就具有很大的优势;但对于那些使用命令式编程学习并开始解决问题的初级程序员来说,他们很难使用这种编程范式,因为它改变了他们的工作习惯。但是在这个行业中,我们必须适应变化,况且目前使用函数式编程的场景越来越多。

const items = [{
    name: 'Coffe',
    price: 500
  }, {
    name: 'Ham',
    price: 1500
  }, {
    name: 'Bread',
    price: 150
  }, {
    name: 'Donuts',
    price: 1000
  }
];

let total = 0;
for (let i = 0; i < items.length; i++) {
  total += items[i].price;
}

// 优化后
const total = items
  .map(({ price }) => price)
  .reduce((total, price) => total + price);
  1. 函数链式调用

class Car {
    constructor({ make, model, color } = car) {
        this.make = make;
        this.model = model;
        this.color = color;
    }
    setMake(make) {
        this.make = make;
    }
    setModel(model) {
        this.model = model;
    }
    setColor(color) {
        this.color = color;
    }
    save() {
        console.log(this.make, this.model, this.color);
    }
}    
const car = new Car({make: 'hoel', model: 'Jetta', color: 'gray'});
car.setColor('red');
car.save();
class Car {
    constructor({ make, model, color } = car){
        this.make = make;
        this.model = model;
        this.color = color;
    }
    setMake(make) {
        this.make = make;
        return this;
    }
    setModel(model) {
        this.model = model;
        return this;
    }
    setColor(color) {
        this.color = color;
        return this;
    }
    save() {
        console.log(this.make, this.model, this.color);
        return this;
    }
}
const car = new Car({make: 'hoel', model: 'Jetta', color: 'gray'});
.setColor('red')
.save();

重构篇

重构很重要的原因:

  1. 改进软件/应用程序的设计

  2. 使软件更容易理解

  3. 发现 bug

  4. 修复现有的旧数据库

  5. 为用户提供更好的一致性

    重构不是银弹,但它是一种有价值的武器,可以帮助你控制好代码和项目 (软件/应用)。它是一个科学的过程,对现有的代码进行改造,使代码可读性更高、更好理解和更整洁。而且,使添加新功能、构建大型应用程序以及发现和修复 bug 变得非常便捷。

撒密码是由明文和密文两个字母表构成的,密文字母表是将明文字母表向左或向右移动一个固定位置得到的。例如,这个恺撒密码,使用的偏移量为 6,相当于右移 6 位

Plain:
    ABCDEFGHIJKLMNOPQRSTUVWXYZ

Cipher:
   GHIJKLMNOPQRSTUVWXYZABCDEF



重构建议: 无论我们如何重构代码,都要使用自动化测试来帮助我们验证有没有 “破坏” 代码。

console.assert(cipher
('Hello World',1) === 'Ifmmp!Xpsme',
`${cipher('Hello World', 1)} === 'Ifmmp!Xpsme'`,
);

console.assert(decipher(cipher
('Hello World',3),3) === 'Hello World',
`${decipher(cipher('Hello World', 3), 3)} === 'Hello World'`,);
  1. 魔法数字 通过定义语义化的变量来移除代码中出现的魔法数字

const NUMBER_LETTERS = 26; 
const LETTER ={
    a: 65,
    z: 90,
    A: 97,
    Z: 122
}
  1. 从 if-else 中提取相似代码 下一步是将代码中重复的代码提取到函数中。具体来说,if 控制结构体中的赋值代码在整个代码中是重复的,我们可以提取这些赋值代码。

  2. 避免使用 else 下一步是避免使用 else 控制结构。避免使用它是很容易的,我们只需要在循环开始之前将代码从 else 移到变量 character,并作为它的默认值。

  3. 合并 IF 逻辑

  4. 简化算法逻辑

  5. 封装条件

  6. 移除 if-else 控制结构

  7. 变量命名

上一页从输入浏览器到页面展示涉及的缓存机制下一页你知道什么是aop吗

最后更新于2个月前

这有帮助吗?