编程风格
基本格式化
注释
语句和表达式
-
块语句都应使用花括号
- 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 循环 遍历对象
变量 函数 运算符
编程实践
UI 层的松耦合
- 什么是松耦合:修改一个组件而不需要修改其他的组件;组件知道的越少,越有利于形成整个系统
- 将 js 代码和 css 代码分离开,需要操作样式时最佳方法是操作 className,除非需要 js 计算 css 的属性(比如常见的轮播图)
-
将 html 和 js 代码分离开
避免使用全局变量
-
全局变量带来的问题
- 命名冲突
- 代码脆弱性,依赖全局变量,任何地方都能改变全局变量的值
- 难以测试依赖全局变量的函数
-
意外的全局变量
- 当使用一个未被声明的变量时,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(对象名) 冻结对象,即是不可扩展的,也是密封的,而且其属性值也不能修改
|