• < 每日技巧: JavaScript代码优化 >


    代码优化


    👉 前言

    无论在什么开发岗位中,代码都通过开发者书写出来,后续也将由开发者去进行维护! 所以我们不难得出一个结论,如果要考虑代码优化的问题,无疑要从开发者视角去看! 这里引入 《计算机程序的构造和解释》 中提到的概念:

    代码是写给人看的,不是写给机器看的,只是顺便计算机可以执行而已。如果代码是写给机器看的,那完全可以使用汇编语言或者机器语言(二进制),直接让机器执行。

    作者强调的是,代码是写给人看的,所以要做到良好的编程风格,方便其他程序员阅读,维护。

    俯瞰

    基于以上来看,在我们开发过程中,要考虑对JavaScript代码进行优化的话,可以从自己本身去入手这件事。或者说,从开发者角度去理解,比如说:

    • 可读性
    • 复用性
    • 低耦合高内聚

    代码即计算机语言,比如说HTML超文本标记语言

    和语言文字一样是为了表达思想、记载信息。所以写得清晰明了,能更有效地表达所要传递的信息。

    👉 一、函数相关优化

    > 适当分解提炼复杂函数 并 提高可读性

    前提: 如果当你需要花费一定时间去浏览一段代码,才能弄清楚它到底要干什么的时候,那么这时候就应该尝试分解提炼这段代码了~

    作用: 方便以后再读这段代码时,一眼就能清晰的知道这个函数的用途。

    案例: 以一个公共导出方法为例

    
    // 提炼前
    exportExcel() {
    	let path = "/knowledge/excelExport";
    	let data = {
    		department: this.form.section,
    		departmentId: this.form.sectionId,
    		knowledgeType: this.documentType,
    		keyword: this.form.keyValue,
    		size: this.pageInfoConfig.rows,
    		current: this.pageInfoConfig.page,
    		mark: "export",
    		showAll: this.showAll ? "1" : "",
    		operateType: this.form.operateType,
    	};
    	this.exportExcelLoading = true;
    	axios({
    		method: "POST",
    		url: API_PATH + path,
    		data,
    		headers: {
    			'Content-Type': 'application/json;charset=UTF-8'
    		},
    		responseType: 'blob'
    	}).then(res => {
    		const content = res.data
    		const blob = new Blob([content])
    		const fileName = `${title}.xls`;
    		if ('download' in document.createElement('a')) { // 非IE下载
    		const elink = document.createElement('a')
    		elink.download = fileName
    		elink.style.display = 'none'
    		elink.href = URL.createObjectURL(blob)
    		document.body.appendChild(elink)
    		elink.click()
    		URL.revokeObjectURL(elink.href) // 释放URL 对象
    		document.body.removeChild(elink)
    		} else { // IE10+下载
    		 navigator.msSaveBlob(blob, fileName)
    		}
    		this.exportExcelLoading = false;
    	}).catch(()=>{
    		this.$message.error('导出失败,请重试!');
    	});
    },
    
    // 提炼后
    exportExcel() {
    	let path = "/knowledge/excelExport";
    	let data = {
    		department: this.form.section,
    		departmentId: this.form.sectionId,
    		knowledgeType: this.documentType,
    		keyword: this.form.keyValue,
    		size: this.pageInfoConfig.rows,
    		current: this.pageInfoConfig.page,
    		mark: "export",
    		showAll: this.showAll ? "1" : "",
    		operateType: this.form.operateType,
    	};
    	this.exportExcelLoading = true;
    	// 对应已经封装在文件中的导出方法
    	exportExcel(path, data, "导出信息-知识库").finally(() => {
    		this.exportExcelLoading = false;
    	});
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    > 文件中的导出方法

    // 封装导出列表方法
    import {API_PATH} from "@/config";
    import axios from "axios";
    import ElementUI from 'element-ui'
    
    const exportExcel = (path, data, title) => {
      return axios({
        method: "POST",
        url: API_PATH + path,
        data,
        headers: {
          'Content-Type': 'application/json;charset=UTF-8'
        },
        responseType: 'blob'
      }).then(res => {
        const content = res.data
        const blob = new Blob([content])
        const fileName = `${title}.xls`;
        if ('download' in document.createElement('a')) { // 非IE下载
          const elink = document.createElement('a')
          elink.download = fileName
          elink.style.display = 'none'
          elink.href = URL.createObjectURL(blob)
          document.body.appendChild(elink)
          elink.click()
          URL.revokeObjectURL(elink.href) // 释放URL 对象
          document.body.removeChild(elink)
        } else { // IE10+下载
          navigator.msSaveBlob(blob, fileName)
        }
      }).catch(()=>{
        ElementUI.Message.error('导出失败,请重试!');
      });
    };
    
    export default exportExcel;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    以上提炼函数的方法,不仅可以提高代码复用率,还可以使暴露出来的代码更加简洁明了,方便统一维护导出方法!

    Tips: 不过需要注意的是,在提炼函数时,需要明确目的性。不能以提炼而提炼,过分的封装提炼,只会显得代码庸余且

    > 函数参数化

    前提: 具有相似操作逻辑的函数,增加参数用以区分不同操作。

    作用: 增加函数接收参数的个数,减少编写相似函数的编写。

    案例: 以上面封装的导出方法为例,传入不同的path、data、title等等。用于区分导出的文件名称和请求的接口!

    exportExcel(path, data, "导出信息-知识库");
    
    • 1

    👉 二、分支语句 ‘更优解’ 或 ‘平替’

    > 使用策略模式替换“胖”分支

    前提: 当分支语句过于臃肿时,可以使用策略模式来取代分支语句

    作用: 当if-else或者switch-case分支过多时可以使用策略模式将各个分支独立出来。减少代码量,使代码更加简洁高效。

    案例:

    // ==================重构前==================
    function getPrice(tag, originPrice) {
        // 新人价格
        if(tag === 'newUser') {
            return originPrice > 50.1 ? originPrice - 50 : originPrice
        }
        // 返场价格
        if(tag === 'back') {
             return originPrice > 200 ? originPrice - 50 : originPrice
        }
        // 活动价格
        if(tag === 'activity') {
            return originPrice > 300 ? originPrice - 100 : originPrice
        }
    }
    
    // ==================重构后==================
    const priceHandler = {
    	newUser(originPrice){
    		return originPrice > 50.1 ? originPrice - 50 : originPrice
    	},
    	back(originPrice){
    		return originPrice > 200 ? originPrice - 50 : originPrice
    	},
    	activity(originPrice){
    		return originPrice > 300 ? originPrice - 100 : originPrice
    	}
    }
    // 实际使用的方法,传入所要使用的方法名
    function getPrice(tag, originPrice){
    	return priceHandler[tag](originPrice)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    > 合理使用 逻辑运算符 / 三元运算符 替代分支语句

    前提: 当分支语句不多时,可以使用运算符代替。实现分支语句的功能。

    作用: 减少代码量,使代码更加简洁高效。

    案例:

    // 案例一
    // ==================== 重构前 ====================
    
    function getInfoByID(id, place) {
    	if(id && place) {
    		...
    	} else {
    		this.$message('无法通过编号 + 地点获取信息')
    	}
    }
    
    // ==================== 重构后 ====================
    function getInfoByID(id, place) {
    	const fn = ((id && place) () => {
    		// 执行内容
    	} : () => {
    		this.$message('无法通过编号 + 地点获取信息')
    	})
    	fn()
    }
    
    // 案例二: 单一分支语句
    // ==================== 重构前 ====================
    if(id && place) {
    	...
    }
    
    if(!(id && place)) {
    	...
    }
    
    // ==================== 重构后 ====================
    (id && place) && (() => { 执行内容 })()
    
    !(id && place) || (() => { 执行内容 })()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    > 以卫语句 取代 嵌套分支语句

    卫语句简述: 如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。 这样的单独检查常常被称为“卫语句”(guard clauses)。

    应用场景: 当分支语句放在需要输出结果的函数内时, 当某个条件成立,无需关心其余内容的情况。即可使用 ”卫语句

    if-else结构: 代表对 if分支else分支 的重视是同等的。这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性。
    卫语句:卫语句则是告诉阅读者: “这种情况不是本函数的核心逻辑所关心的, 如果它真发生了,请做一些必要的整理工作,然后退出。” 为了传递这种信息可以使用卫语句替换嵌套结构。

    // ==================重构前==================
    function payAmount(employee) {
        let result;
        if(employee.isSeparated) {
            result = {amount: 0, reasonCode:"SEP"};
        }
        else {
            if (employee.isRetired) {
                result = {amount: 0, reasonCode: "RET"};
            }
            else {
                result = someFinalComputation();
            }
        }
        return result;
    }
    
    // ==================重构后==================
    function payAmount(employee) {
        if (employee.isSeparated) return {amount: 0, reasonCode: "SEP"};
        if (employee.isRetired) return {amount: 0, reasonCode: "RET"};
        return someFinalComputation();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    📃 参考文献

    往期内容 💨

    🔥 < 今日份知识点: 浅述对函数式编程的理解 及其 优缺点 >

    🔥 < 每日知识点:关于Javascript 精进小妙招 ( Js技巧 ) >

    🔥 < 今日份知识点:谈谈内存泄漏 及 在 Javascript 中 针对内存泄漏的垃圾回收机制 >

    🔥 < 今日份知识点:浅述对 “ Vue 插槽 (slot) ” 的理解 以及 插槽的应用场景 >

  • 相关阅读:
    vue中怎么把reader.onload中的定义的变量在外部调用
    【基础知识】一网络不通问题处理记录
    linux使用yum安装2.x版git
    深度学习:多模态与跨模态
    用CRM系统提高企业销售效率
    【partio】继续探索粒子格式库partio
    Spring的循环依赖,到底是什么样的
    flink重温笔记(十六): flinkSQL 顶层 API ——实时数据流结合外部系统
    什么是nacos/cas/fastdfs
    京东云开发者|经典同态加密算法Paillier解读 - 原理、实现和应用
  • 原文地址:https://blog.csdn.net/MrWen2395772383/article/details/127611272