编写可维护的Js代码

更新日期: 2019-11-23阅读: 2k标签: 代码

基本格式化

4空格缩进
不省略分号(在原生及使用工具函数的情况不建议省略,在使用比较完善的框架vue或者自己配置好 webpack 时可以省略)
1行代码长度不超过80个字符(个人比较推荐,毕竟编辑器的自动换行有时真的很难受)
运算符后换行,换行后增加2个缩进单位(变量赋值换行,变量保持和上一行等号右侧第一个变量对齐)
空行,流控制语句之前,方法之间,变量和第一条语句之间,注释之前,
变量和函数命名方式,小驼峰方式
变量命名名词开头,函数命名动词开头,命名长度尽可能短,尽量体现出值得数据类型
can 函数返回布尔值
has 函数返回布尔值
is 函数返回布尔值
get 函数返回非布尔值
set 函数用来保存值
常量使用大写字母和下划线来命名,下划线分割单词 (URL MAX_NUMBER)
构造函数(class) 大驼峰命名方式
字符串,统一用单引号,多行字符或者需要携带参数使用反引号
数字,不要省略浮点数小数点前后的部分
null 当做对象占位符
避免使用 undefined
创建对象时使用对象直接量: {}
创建数组时使用数组直接量: []


注释

单行注释

  1. 独占一行,注释前总是有一个空行,缩进与下一行代码保持一致
  2. 代码行尾部,代码结束到注释之间有一个空格,代码加上注释不应超过单行最大字符数限制,超过了就应放到当前代码行的上方
  3. 单行注释不应以连续多行注释的形式出现

多行注释

  • 最少三行第一行 / , 第二行 与上一行 对齐,最后一行 / (单行多行注释区分开比较好,多行注释风格很多,这里只是一种,找到适合的就好)

/*
 *空格 + 注释
 */
  • 注释前总是有一个空行,缩进与下一行代码保持一致
  • 代码结束到注释之间有一个缩进

添加注释的一般原则

  • 需要让代码表达的意思更清晰
  • 难以理解的代码
  • 可能被认为错误的代码
  • 浏览器特性 hack

文档注释

最流行的 javaDoc 文档格式
多行注释以单斜杠加双星号开头 /**
接下来是描述信息
使用 @ 来表示一个或多个属性
/**
 *注释
 *@ 变量1 {String} 一个字符串变量
 */    

如何添加文档注释

所有的方法: 方法作用,期望的参数,返回值 进行描述
所有的构造函数(类): 自定义类型,期望的参数添加描述
所有包含文档化方法的对象: 如果对象包含一个或多个附带文档注释的方法,那么这个对象也应适当添加文档注释
文档生成工具 (jsdoc:目前还在维护更新,使用量,star数也比较多)
注:使用文档生成工具要使用工具要求的注释风格


语句和表达式

块语句都应使用花括号

if...else...
switch
for
while
do..while..
try...catch...
...

花括号对齐方式,左花括号挡在块语句的第一行末尾

if () {
    
} else {
    
}

块语句间隔

语句名,小括号,花括号都没有空格

if(true){
    
}

小括号左右加空格

if (true) {
    
}

小括号左右加空格,小括号与小括号内容之间加空格

if ( true ) {
    
}

switch 语句

缩进

每条 case 语句相对于 switch 关键字缩进一个层级,每条 case 语句后都有一个空行

switch (color) {
  case '#000':
      break
  
  case '#f00':
      break
  
  case '#00F':
      break
  
  default:
      break
}

case 和 switch 左对齐,且没有空行

  switch (color) {
  case '#000':
      break
  case '#00F':
      break
  default:
      break
}
连续执行: 有意连续执行需要加注释,其他情况默认每条语句结束必须是 break,return,throw
default:如果default什么也没做,加上注释,省略default

while 语句 尽量避免使用,因为在严格模式下不能运行,而且容易造成对于变量的认知错误

let message = '你好!'
let book = {
  name: '少少',
  age: 18,
  message: '你好吗?'
}

with (book) {
    message += name
    console.log(message)
}

for 循环 遍历数组

break 立即退出循环
commit 跳过本次循环 // 尽量避免这种方式

for-in 循环 遍历对象

不用定义任何控制条件,返回属性名而不是值

不仅遍历对象的实例属性同样遍历从原型继承来的属性,往往因意外的结果而终止

使用 hasOwnProperty() 过滤实例属性,除非想查找原型链


变量 函数 运算符

变量声明

集中在函数顶部

在有相关性时,合并变量声明

let value = 10,
    result = 10,
    i,
    len;

函数声明

先声明后使用
函数声明不应该出现在语句块之内

函数调用间隔

函数名和小括号之间没有空格

立即调用的函数

const myFunction = function () {
    // 函数
}
let value = function () {
    return 'string' // 变量
}()

// 好的写法
let value = (function () {
    return 'string'  
}())

严格模式 "use strict"

尽可能使用严格模式

不在全局使用严格模式

function () {
    "use strict"
    
}

(function () {
    "use strict"
    function () {
        
    }
    function () {
        
    }
    function () {
        
    }
})()

相等 JavaScript 有强制类型转换机制

数字 与 字符串比较,尝试将字符串转换数字
数字 与 布尔值比较,尝试将布尔值转换为数字
对象 与 其他类型,先调用valueOf(),没有就调用toString()
使用 === 和 !==


UI 层的松耦合

什么是松耦合:修改一个组件而不需要修改其他的组件;组件知道的越少,越有利于形成整个系统
将 js 代码和 css 代码分离开,需要操作样式时最佳方法是操作 className,除非需要 js 计算 css 的属性(比如常见的轮播图)

html 和 js 代码分离开

从服务器加载
客户端模板(Handlebars)


避免使用全局变量

全局变量带来的问题

命名冲突
代码脆弱性,依赖全局变量,任何地方都能改变全局变量的值
难以测试依赖全局变量的函数

意外的全局变量

当使用一个未被声明的变量时,JavaScript会自动创建为全局变量

单全局变量

创建的这个唯一全局对象名是独一无二的(不会和内置api产生冲突),并将所有的功能代码全都挂载到这个全局对象上,因此每个可能的全局变量都会成为你唯一的全局对象的属性,从而不会创建多个全局变量


// 再比如表示三本书
// 方法一
function Book (name) {
    return `书名:${name}`
}
const Book1 = Book("name1")
const Book2 = Book("name2")
const Book3 = Book("name3")

console.log(Book1, Book2, Book3)

// 单全局变量
class Books {
    Book1 = Book("name1")
    Book2 = Book("name2")
    Book3 = Book("name3")
    
    Book (name) {
        return `书名:${name}`
    }
}

console.log(Books.Book1, Books.Book2, Books.Book3)

/*
 *其他例子: jquery 定义了 $ 和 jQuery
 *以及vue 定义了 Vue
 */

命名空间(在类中定义分组,方便管理)

class My {
    dom = {
        addDom () {
            ...
        },
        removeDom () {
            ...
        }
        ...
    }
    
    event = {
        onClick () {
            ...
        },
        oninput () {
          ...  
        }
        ...
    }
}


事件处理

典型用法 (流水账式写法)


// 常见但是不推荐的写法 
handleClick (event) {
    const popup = document.getElementById("popup")
    popup.style.left = event.clientX
    popup.style.top = event.clientY
    popup.className = "show"
}

隔离应用逻辑 应用逻辑和用户行为应该区分开(一个函数只做一件事,便于函数复用)

clas Myapp {
    handleClick (event) {
      this.showPopup(event)  
    },
    showPopup (event) {
      const popup = document.getElementById("popup")
      popup.style.left = event.clientX
      popup.style.top = event.clientY
      popup.className = "show"
    }
}

不要分发事件对象 应用逻辑不应该依赖 event 对象来完成功能(便于测试,便于函数复用)

 clas Myapp {
    handleClick (event) {
    
      // 事件处理程序是接触 event 对象的唯一函数 (一个函数只做一件事)
      event.preventDefault()
      event.stopPropagation()
      
      // 传入应用逻辑
      this.showPopup(event.clientX, event.clientY)  
    },
    showPopup (x, y) {
      const popup = document.getElementById("popup")
      popup.style.left = x
      popup.style.top = y
      popup.className = "show"
    }
}


避免空比较

使用 "typeof 变量" 的方式检测 字符串,数字,布尔值,undefined,函数,
使用 "变量 instanceof 对象" 的方式检测某个值的引用类型(不能用于检测是否属于 Object,因为 js 所有的值原型链最终都是 Object )

检测数组 (es5已有自带检测方法)


// 关注对象能做什么,而不是他是什么
function isArr(value) {
    return typeof value.sort === 'function'
}

// 更为优雅有效的解决方案 (兼容 IE6 以下版本)
function isArray (value) {
    if (typeof Array.isArray === 'function') {
        return Array.isArray(value) // 自带判断数组方法
    } else {
    
        // 某个值内置的 toString 在所有的浏览器中都会返回标准的字符串结果
        return Object.prototype.toString.call(value) === '[object Array]'
    }
}

检测属性是否存在

不好的写法,通过给定的名字检查属性的值

let myObject = {
    count: 0
}

if (myObject['count']) {
    // 存在但不执行
}

好的写法,通过 in 运算符或者 hasOwnPrperty()

let myObject = {
    count: 0
}

if ('count' in myObject) { // in 只检查属性是否存在于对象实例或者对象原型
    // 执行
}

if (myObject.hasOwnPrperty('count')) { // 只检查是否存在于对象实例
    // 执行
}

将配置数据从代码中分离出来

什么是配置数据 写死在代码里,且将来可能会被修改

接口地址
重复的值
设置(比如页面配置项)
任何可能发生变更的值
...
抽离配置数据,把配置数据拿到外部(类似 vue 中的 data)
保存配置文件,把配置单独保存到文件(类似 vue-cli 搭建的项目中 vue.config.js 的作用)

抛出自定义错误

什么是错误 帮助我们快速定位代码 bug ,以便于调试维护

JavaScript 抛出错误的方法

throw new Error('错误信息')

如何书写抛出的错误文本

throw new Error('函数名: 可能的原因')

什么时候抛出错误

不确定自己的函数在哪些地方调用
工具函数
JavaScript 类库 (函数调用栈应该在进入库代码接口的时候就终止;意思是在调用类库时先要校验然后进行相应的提示,而不是等到)
修复了一个很难调试的错误,这时添加一两个自定义错误
不想要某种情况发生,为避免这种情况

try-catch 语句 catch 代码块不能省略或留空

能在错误抛出前解析它;
抛出错误会中断 js 进程, 使用 try-catch 则不会中断 js 进程

个人理解: try-catch 语句可以用在自己知道可能会出错但是不能明确的知道错误原因,
或者在报错后仍需继续报错代码块之后语句的情况下


不是自己的对象不要动

什么是自己的对象

自己创建了一个对象,那么自己拥有这个对象
自己不是创建对象的人,但是负责维护代码,那么也拥有这个对象
只是使用者,不要修改对象

对象的使用原则

不覆盖对象原型上的属性,方法
不在对象原型新增属性,方法
不删除对象原型上的属性,方法

如何使用?

继承
设计模式之外观模式(门面模式 Facade Pattern)

防止对象被修改

Object.preventExtensions(对象名) 该方法不能向对象中新添加属性和方法了,但是可以修改对象中存在的属性
Object.seal(对象名) 密封对象不能新添加属性、不能删除属性。
Object.freeze(对象名) 冻结对象,即是不可扩展的,也是密封的,而且其属性值也不能修改


链接: https://fly63.com/article/detial/7003

不要浪费时间写完美代码

一个系统可以维持5年,10年,甚至20年以上,但是代码和设计模式的生命周期非常短,当对一个解决方案使用不同的方法进行迭代的时候,通常只能维持数月,数日,甚至几分钟的时间

Google内部在代码质量上的实践

良好的编程习惯涉及到很多方面,但在软件行业内,大多数的公司或组织都不会把良好的编程习惯列为主要关注点。 例如,具有可读性和可维护性的代码比编写好的测试代码或使用正确的工具更有意义,前者的意义在于可以让代码更易于理解和修改。

减少嵌套,降低代码复杂度

减少嵌套会让代码可读性更好,同时也能更容易的找出bug,开发人员可以更快的迭代,程序也会越来越稳定。简化代码,让编程更轻松!

关于 Google 发布的 JS 代码规范

Google为了那些还不熟悉代码规范的人发布了一个JS代码规范。其中列出了编写简洁易懂的代码所应该做的最佳实践。代码规范并不是一种编写正确JavaScript代码的规则,而是为了保持源代码编写模式一致的一种选择。

你解决的问题比你编写的代码更重要!

程序员似乎忘记了软件的真正目的,那就是解决现实问题。您编写的代码的目的是为了创造价值并使现有世界变得更美好,而不是满足您对自我世界应该是什么的以自我为中心的观点。有人说:如果你拥有的只是一把锤子,那么一切看起来都像钉子一样

tinymce与prism代码高亮实现及汉化的配置

TinyMCE是一个轻量级的基于浏览器的所见即所得编辑器,由JavaScript写成。它对IE6+和Firefox1.5+都有着非常良好的支持。功能方强大,并且功能配置灵活简单。另一特点是加载速度非常快的。

js函数式编程与代码执行效率

函数式编程对应的是命令式编程, 函数式编程的核心当然是对函数的运用. 而高阶函数(Higher-order)是实现函数式编程的基本要素。高阶函数可以将其他函数作为参数或者返回结果。所以JS天生就支持函数式编程

接手代码太烂,要不要辞职?

朋友发表了一条说说:入职新公司,从重构代码到放弃”,我就问他怎么了?他说,刚进一家新公司,接手代码太烂,领导让我先熟悉业务逻辑,然后去修复之前项目中遗留的bug,实在不行就重构

js高亮显示关键词_页面、搜索关键词高亮显示

页面实现关键词高亮显示:在项目期间遇到一个需求,就是搜索关键词时需要高亮显示,主要通过正则匹配来实现页面关键词高亮显示。在搜索结果中高亮显示关键词:有一组关键词数组,在数组中筛选出符合关键字的内容并将关键字高亮

写优雅的代码,做优雅的程序员

软件工程学什么? 学计算机,写程序,做软件,当程序员。听说学计算机很辛苦? 是的,IT行业加班现象严重。在计算机世界里,技术日新月异,自学能力是程序员最重要的能力之一。选了这个专业,就要时刻保持好奇心和技术嗅觉,不能只满足于完成课内作业。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!