前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >编写可维护的JavaScript

编写可维护的JavaScript

作者头像
硬核项目经理
发布2019-08-07 15:31:33
8350
发布2019-08-07 15:31:33
举报
文章被收录于专栏:硬核项目经理的专栏

一、基本的格式化

A.缩进层级

1.建议使用4个制表符的缩进

B.语句结尾

1.不要省略分号

C.行的长度

1.建议单行长度不超过80个字符

D.换行

1.下一行两个缩进

2.将符号置于行尾

3.当给变量赋值时,第二行的位置应当和赋值运算符的位置保持对齐

E.空行

• 在每个流程控制语句之前

• 在方法之间

• 在方法中的局部变量(local variable)和第一条语句之间

• 在多行或单行注释之前

• 在方法内的逻辑片段之间插入空行,提高可读性

F.命名

1.变量和函数

• 变量名应当总是遵守驼峰大小写命名法(小驼峰法,如myName),并且命名前缀应当是名词

• 函数第一个单词应当是动词(can、has、is、get、set)

2.常量:使用大写字母及下划线来命名

3.构造函数:遵照大驼峰命名法,如Person()

G.直接量

1.字符串:使用双引号和单引号均可,但要统一,换行时用+号,如””+””来换行,+在上方行尾

2.数字:不要省略小数或整数部分,尽量不要使用八进制

3.null:最好的方式是将它当做对象的占位符(placeholder)

• 使用:用来初始化一个变量,这个变量可能赋值为一个对象;用来和一个已经初始化的变量比较,这个变量可以是也可以不是一个对象;当函数的参数期望是对象时,用作参数传入;当函数的返回值期望是对象时,用返回值付出

• 不使用:不要使用null来检测是否传入了某个参数;不要用null来检测一个未初始化的变量

4.undefined:尽量少使用undefined

5.对象直接量:在第一行包含左花括号,每一个属性的名值对都独占一行,并保持一个缩进,最后右花括号也独占一行

6.数组直接量:不用new Array,直接用[……]

二、注释

A.单行注释

1.注释前后加空格( // aaa),注释前加空行

B.多行注释

1.使用java风格多行注释

2.星号后加空格

3.保持缩进一致

C.使用注释:清晰明了的代码不应该写注释

1.难于理解的代码

2.可能被认为错误的代码

3.浏览器特性hack

D.文档注释

1.所有的方法

2.所有的构造函数

3.所有包含文档化方法的对象

三、语句和表达式

A.花括号的对齐方式

1.所有的块语句都应当使用花括号,包括:if、for、while、do...while...、try...catch...finally

2.推荐使用java规范,左花括号放置在第一句代码的末尾

B.块语句间隔

1.推荐在左圆括号之前和右圆括号之后各添加一个空格

C.switch语句

1.缩进

• 每条case语句相对于switch关键字都缩进一个层级

• 从第二条case语句开始,第条case语句前后各有一个空行

2.case语句的“连续执行”:只要程序逻辑非常清晰即可

3.default:在没默认行为且写了注释的情况下省略default

D.with语句

1.避免使用with语句

E.for循环

1.尽可能避免使用continue,但也没有理由完全禁止使用,它的使用应当根据代码可读性来决定

F.for-in循环

1.总是在for-in循环中使用hasOwnProperty(),除非你想查找原型链,这时应当补充注释

2.for-in循环是用来对实例对象和原型链中的键(key)做遍历的,而不是用来遍历包含数字索引的数组的

四、变量、函数和运算符

A.变量声明

1.所有的var语句都提前到包含这段逻辑的函数的顶部执行,在函数内部任意地方定义变量和在函数顶部定义变量是完全一样的

2.建议将局部变量的定义作为函数内第一条语句,将所有的var语句合并为一个语句,每个变量的初始化独占一行,赋值运算应当对齐,没有初始值的变量应当出现在var语句的尾部

B.函数声明

1.函数声明也会被提前,推荐总是先声明JS函数然后使用函数,函数内部的局部函数应当紧接着变量声明之后声明

2.函数声明不应当出现在语句块之内

C.函数调用间隔

1.在函数名和左括号之间没有空格

D.立即调用的函数

1.为了让立即执行的函数能够被一眼看出来,可以将函数用一对圆括号包裹起来

E.严格模式

1.不推荐将严格模式运用到全局作用域中

2.尽可能的在函数内部使用严格模式

F.相等

1.数字和字符串比较字符串会转为数字,布尔值和字符串比较布尔值会转为数字,对象和类型比较会调用valueOf(),如果没有valueOf会调用toString()

2.null和undefined是相等的

3.推荐使用===和!==

4.尽量不要使用eval()和Function

5.尽量不使用包装类型(String,Boolean,Number)会增加出BUG的概率

五、UI层的松耦合

A.什么是松耦合

1.当你能够作到修改一个组件而不需要更改其他的组件时,你就做到了松耦合;当一个大系统的每个组件的内容有了限制,就做到了松耦合;在一起工作的组件无法达到“无耦合”(no coupling)

B.将JavaScript从CSS中抽离

1.即使是老版本IE中也不要使用CSS表达式(IE9已经删除)

C.将CSS从JavaScript中抽离

1.操作CSS的className来修改元素样式,而不是直接用xxx.style.color=‘red’或xxx.style.cssText=“…”这类

D.将JavaScript从HTML中抽离

1.使用addEventListener(attachEvent或target[‘on’+type])来绑定事件,而不是行内写onclick(jquery.on(……))

2.最好将所有的JS代码都放入外置文件中

F.将HTML从JavaScript中抽离:例如innerHTML这种不要使用

1.从服务器加载:jquery.load(……)

2.简单客户端模板

• 使用HTML的注释,设置为一个模板,然后正则或其他方式替换模板中的内容

• 使用带有自定义type属性的<script>元素

3.复杂客户端模板:使用诸如Handlebars等第三方库所提供的模板系统

六、避免使用全局变量

A.全局变量带来的问题

1.命名冲突:全局环境是用来定义JS内置对象的地方,如果给这个作用域添加了自己的变量,接下来则会面临读取浏览器附带的内置变量的风险。

2.代码的脆弱性:一个依赖于全局变量的函数即是深耦合于上下文环境之中。如果环境发生改变,函数很可能就失效了。

3.难以测试:任何依赖全局变量才能正常工作的函数,只有为其重新创建完事的全局环境才能正确地测试它。

B.意外的全局变量

1.给一个未被var语句声明的变量赋值时,JS就会自动创建一个全局变量。总是使用var来定义变量。

2.使用JSLint或JSHint可以检查,使用严格模式也可以

C.单全局变量方式

1.“单全局变量”:所创建的这个唯一全局对象名是独一无二的(不会和内置API产生冲突),并将你所有的功能代码都挂载到这个全局对象上

2.将功能按照命名空间进行分组,可以让你的单全局对象变得井然有序,同时可以让团队成员能够知晓新功能应该属于哪个部分,或者知道去哪里查找 已有的功能

3.模块是一种通用完的阿乐片段,它并没有创建新的全局变量或命名空间。相反,所有的这些代码都有些话于一个表示执行一个任务或发布一个接口的单函数中。可以用一个名称来表示这个模块,同样这个模块可以依赖其他模块。

4.YUI模块、AMD模块

D.零全局变量

1.使用一个立即执行的函数调用并将所有脚本放置其中

七、事件处理

A.典型用法

1.当事件触发时,事件对象(event对象)会作为回调参数传入事件处理程序中。event对象追住所有和事件相关的信息,包括事件的宿主(target)以及其他和事件类型相关的数据。

B.规则1:隔离应用逻辑

1.事件处理程序不要包含应用逻辑(application logic),应用逻辑是和应用相关的功能性代码,而不是和用户行为相关的

C.规则2:不要分发事件对象

1.应用逻辑不应当依赖于event对象来正确完成功能

• 方法接口并没有表明哪些数据是必要的

• 如果想测试这个方法,必须重新创建一个event对象并将它作为参数传入

2.让事件处理程序使用event对象来处理事件,然后拿到所有需要的数据传给应用逻辑

3.当处理事件时,最好让事件处理程序成为接触到event对象的唯一函数。事件处理程序应当在进入应用逻辑之前针对event对象执行任何必要的操作,包括阻止默认事件或事件冒泡等

八、避免“空比较”

A.检测原始值

1.字符串、数字、布尔值、null和undefined,最佳选择是typeof运算符

2.typeof运算符用于一个未声明的变量也不会报错,未定义的变量和值为undefined的变量通过typeof都将返回“undefined”

3.null,一般不应用于检测语句,除非期望的值真的是null,document.getElementById()如果不存在返回的也是null,typeof null返回object

B.检测引用值

1.Object、Array、Date、Error等对象,最佳方法是使用instanceof运算符,不仅检测构造这个对象的构造器,还检测原型链。

2.instanceof运算符也可以检测自定义的类型,这也是唯一的方法,对于内置JS类型也是如此

3.检测函数最好的方法是使用typeof,因为它可以跨帧(frame)使用。使用in运算符来检测DOM的方法。

4.ES5引入了isArray判断是否数组,否则自定义Object.prototype.toString.call(value)===“[object Array]”

C.检测属性

1.判断属性是否存在的最好方法是使用in运算符,仅会简单地判断属性是否存在,而不会去读属性的值。

2.如果只想检查实例对象的某个属性是否存在,则使用hasOwnProperty()方法

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

A.什么是配置数据

1.配置数据是应用中写死(hardcoded)的值,URL、需要展现给用户的字符串、重复的值、设置(比如每页的配置项)、任何可能发生变更的值

B.抽离配置数据

1.将配置数据拿到外部,使用一个对象(如var config={})来保存所有配置信息

C.保存配置数据

1.将config放到单独的文件中,清晰的分隔数据和应用逻辑

十、抛出自定义错误

A.错误的本质

1.当某些非期望的事情发生时程序就引发一个错误

2.像内置的失败案例一样来考虑错误是非常有帮助的。在代码的某个特殊之处计划一个失败总比要在所有的地方都预期失败简单的多

B.在JavaScript中抛出错误

throw new Error(“Something bad happened.”)

C.抛出错误的好处

1.抛出自己的错误可以使用确切的文本供浏览器显示。除了行和列的号码,还可以包含任何你需要的有助于调试问题的信息

D.何时抛出错误

1.一旦修复了一个很难调试的错误,深度增加一两个自定义错误。当两次发错误时,将有助于解决问题

2.如果正在编写代码,思考一下“我希望【某些事情】不会发生,如果发生,我的代码会一团糟糕”。这时,如果“某些事情 ”发生,就抛出一个错误

3.如果正在编写的代码别人(不知道是谁)也会使用,思考一下他们使用的方式,在特定的情况下抛出错误

E.try-catch语句

1.try中的retrun会等到finally执行完成后才返回

2.错误只应该在应用程序栈中最深的部分抛出,应用程序逻辑总是知道调用某个特定函数的原因,也是最适合处理错误的,不要将catch块留空,至少输出点什么

F.错误类型

1.7种错误类型:Error、EvalError(通过eval()函数执行代码时发生错误)、ReferenceError(期望的对象不存在时抛出)、RangeError(数字超出边界)、SyntaxError(给eval()函数传递的代码中有语法错误时抛出)、TypeError(变量不是期望的类型时抛出)、URIError(给encodeURI()等函数传递非法URI字符串时抛出)

十一、不是你的对象不要动

A.什么是你的

1.不要修改:

• 原生对象(Object、Array等等)

• DOM对象(例如,document)

• 浏览器对象模型(BOM)对象(例如,window)

• 类库的对象

B.原则

1.不覆盖方法

2.不新增方法

3.不删除方法

C.更好的途径

1.基于对象的继承:Object.create()

2.基于类型的继承

• 原型继承:xxx.prototype = new xxx();

• 构造器继承:function xxx(){xxxx.call(this)}

3.继承的限制:不能从DOM或BOM对象继承、继承自Array是不能正常工作的

4.门面模式:为一个已存在的对象创建一个新的接口,也叫包装器,用不同的接口来包装已存在的对象,例如jQuery和YUI的DOM接口

D.关于Polyfill的注解

1.polyfills是指一种功能的模拟,这些功能在新版本的浏览器中已经有完备定义并原生实现了。关键是它们模拟的原生功能要以完全兼容的方式来实现

2.避免使用polyfills,可以在已存在功能之上创建门面来实现

E.阻止修改

1.防止扩展(禁止添加):Object.preventExtension()和Object.isExtensible()

2.密封(禁止删除,同时不可扩展):Object.seal()

3.冻结(禁止修改,同时是不可扩展和密封的):Object.freeze()

十二、浏览器嗅探

A.User-Agent检测

1.服务端根据user-agent字符串来确定浏览器的类型

B.特性检测

1.特性检测不依赖于所使用的浏览器,仅仅依据特性是否存在,所以并不一定需要新浏览器的支持

2.探测标准的方法、探测不同浏览器的特定方法】当被探测的方法均不存在时提供一个合乎逻辑的备用方法

C.避免特性判断

1.不能从一个特性的存在推断出另一个特性是否存在

D.避免浏览器推断

E.应当如何取舍

1.尽可能地使用特性检测,其次考虑用户代理检测,永远不要使用浏览器推断

十三、文件和目录结构

A.最佳实践

1.一个文件只包含一个对象

2.相关的文件用目录分组

3.保持第三方代码的独立

4.确定创建位置

5.保持测试代码的完整性

B.基本结构

1.build目录:放置最终构建后的文件,不提交

2.src目录:放置所有的源文件

3.test或者tests目录:放置测试文件

十四、Ant

十五、校验

十六、文件合并和加工

十七、文件精简和压缩

A.YUI Compressor

B.Closure Compiler

C.UglifyJS

十八、文档化

A.JSDoc Toolkit

B.YUI Doc

十九、自动化测试

A.YUI Test Selenium引擎

B.Yeti

C.PhantomJS

D.JsTestDriver

二十、组装到一起

附录A.JavaScript编码风格指南

附录B.JavaScript工具集

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农老张 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档