Generator函数设计的意思重点是把异步操作放到yeild表达式中,等调用next之后再执行,也是解决回调地狱的一种方案,其实后面es7的async await更好

生成器函数最基本的用法

// 定义个生成器函数,一般会在function后面加上一个星号来表示
let foo = function* () {
    for (let i = 0; i < 3; i++) {
        yield i
    }
}
// 将生成器函数肤质给一个变量,注意此时生成器函数内部的for循环并不会执行,可以在yield i 上面打印个log测试一下
let f = foo()
// 调用f下的next()方法,最后一次因为循环结束,所以没有值被返回
console.log(f.next()) // {value:0, done:false}
console.log(f.next()) // {value:1, done:false}
console.log(f.next()) // {value:2, done:false}
console.log(f.next()) // {value:undefined, done:true}

Generator不能被当做构造函数的方式被使用,换句话说就是不能被new,会报错foo is not a constructor
yield 关键字只能在Generator函数内部使用。例如将上面的for循环改成forEach则会因为作用域的问题报错

一个最典型的应用场景,写一个函数用来生成7得倍数

function* gen(i = 0) {
    while (true) {
        if (i % 7 === 0) {
            yield i
        }
        i++
    }
}
let g = gen()
console.log(g.next()) // 0
console.log(g.next()) // 7
console.log(g.next()) // 14

应用场景2:解决ajax的回调地狱。
例如有三个接口有依赖关系,常规的写法是嵌套的方式把下一个接口写到回调函数中,使用生成器可以用下面这种写法

function request(url,data = 0) {
    // 使用setTimeout来比喻ajax
    setTimeout(function () { 
        reportData.next(data + 1) // 每次都根据上一次的返回结果去请求下一个接口
    }, 1000)
}

function* gen() {
    let res1 = yield request('api/1')
    console.log(res1) // 1
    let res2 = yield request('api/2',res1)
    console.log(res2) // 2
    let res3 = yield request('api/3',res2)
    console.log(res3) // 3
}

let reportData = gen()
reportData.next() // 开始第一次

Proxy 使用

es5中对对象进行拦截使用 Object.defineProperty(),es6增加了new Proxy来增强代理拦截能力

1. 使用Proxy拦截对象,实现私有属性(下划线开头)禁止访问

let user = {
	name:’xxx’,
	age: 18,
	_gender: ‘male’	
}
user = new Proxy(user,{
	get(target, key) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key]
		}
	},
	set(target, key, val) {
		if(key.startsWith(‘_’){
			throw new Error(‘不可访问’)
		} else {
			return target[key] = val
		}
	}
	has (target, key) {
		if(key[0] === "_") { // key.startsWith(‘_’)
			return false;
		}
			return key in target;
	}
	deleteProperty(key){
		if(key.startsWith(‘_’){
			throw new Error(‘不可删除’)
		} else {
			delete target[key]
			return ture	
		}
	}
	ownKeys(target){
		return Object.keys(target).filter(key => !key.startsWith(‘_’))
	}
})

2. 使用Proxy拦截函数调用

let sum = (…args) =>{
	let num = 0
	args.forEach(item => {
		num += item
	})
	return num
}
sum = new Proxy(sum, {
	// 当前函数,上下文this,参数
	apply(target, ctx, agrs) {
		return target(…args) * 2
	}
})

3. 使用Proxy拦截类的构造函数

let User = class {
	constructor (name){
		this.name = name
	}
}
User = new Proxy(User, {
	// 当前类,参数,目标类
	constructor(target, args, newTarget){
		return new target(…args)
	}
})

前端规范,参考于JAVA开发手册第2版

编程规约

1.1 命名风格
  1. 名称不能以下划线和美元符号开头或结尾
  2. 禁止使用拼音和中文命名
  3. 避免使用性别、种族、地域等歧视性语言
  4. 类名使用大驼峰
  5. 方法、参数、变量使用小驼峰
  6. 常量命名全部大写,用下划线隔开,单词要写完整
  7. 抽象类使用Abstract或Base开头
  8. 避免在不同的代码块使用相同的命名
  9. 避免使用单词缩写
  10. 使用变量类型作为单词的结尾,例如startTime、nameList
  11. 如果使用了设计模式要用单词体现,例如:LoginProxy
  12. 如果是形容能力的接口名称,用对应的形容词结尾,例如:Translatable
  13. 枚举类使用Enum后缀
  14. 获取单个对象使用get开头,获取多个对象使用list开头,获取统计使用count,插入使用save/insert,删除使用remove/delete,修改update
1.2 常量定义
  1. 不允许使用魔法值
  2. 不要使用一个常量类维护所有的常量,要按功能归类分开维护
  3. 常量的五层:跨应用、应用内、子工程内、包内、类内
  4. 如果常量仅在一个固定的范围内变化,则用enum类型定义
1.3 代码格式
  1. 打括号前不换行
  2. 4个空格缩进,禁止Tab字符
  3. //注释内容前加一个空格
  4. 单行字符串长度不超过120字符
  5. 单个方法的总行数不超过80
  6. 不同的逻辑、语义、业务之间插入一个空行
1.4 OOP规约
构造方法禁止加入任何业务逻辑,如果有,那么放入init方法中
类内的方法定义顺序依次是:公有方法>getter/setter方法
setter方法中的参数名称和变量名要保持一致,且不要包含业务逻辑
1.5 日期时间
时间单位使用毫秒
不要1年写成365天
1.6 集合
判断对象是否为空使用isEmpty()方法
1.8 控制语句
switch必须包含default,哪怕什么代码都没有
switch括号内的变量如果为字符串时需要先进行null判断
单行的if语句要加打括号
尽量少用if-else,可以改写成
if(condition){
return obj;
}
// 接着写else的逻辑
超过3层的if-esle逻辑判断可以使用卫语句,策略模式、状态模式等实现,例如卫语句的实例:
function findBoyfriend ( man ){
if(man.isUgly()){
console.log(1)
return
}
if(man.isPoor()){
console.log(2)
return
}
if(man.isBad()){
console.log(3)
return
}
}
不要在条件判断中执行复杂的逻辑,可以把这些复杂的逻辑先赋值给一个变量,这样的代码看起来可读性性更高,例如
let existed = Obj.has(x) && obj.xxx ===1 || obj.xxx…
if(existed){ … }
不要在表达式中插入赋值语句,例如:
return (sync = fair)? new FairSync(): new NofairSync()
循环体内减少业务逻辑,移出体外
避免使用反逻辑运算符,例如:
if(!(x >= 1))
如果是对外开发的接口方法要做参数校验
1.9 注释规约
类和方法注释必须使用/**的方式,并且符合jsdoc规范
文件头部添加创建者和创建日期
代码内的注释要在上方另起一行使用// 注释,多行注释使用/ * */ 并对其文字
枚举类型字段必须注释用途
代码更新后注释要更新,保持一致
删除未使用的方法、属性、变量
如果注释掉的代码一定要保留,要说明用途和影响,否则一律删除
注释是给继任者看的,要准确的描述业务含义
TODO要注明标注人和计划处理的时间
1.10 前后端交互
http请求体body中必须控制长度,Nginx默认长度为1mb
1.11 其他
不要在视图层写复杂的业务逻辑

设计规约

如果系统交互的User类超过1类,相关的User Case 类超过5个,则使用用例图来表达结构
如果某个业务对象的状态超过3个,应使用状态图表达且明确状态变化的各个触发条件
如果系统中的调用链路上涉及的对象超过3个,则使用时序图表达各个环节的输入与输出
如果系统中的类超过了5个,则使用类图表达之间的关系
如果系统中的2个对象之间存在协作关系,需要表示复杂的处理流程,则需要使用活动图来表示
系统架构设计时明确以下原则
确定系统边界,确定在技术层面上做与不做
确定系统内模块之间的关系
确定后续演化的原则
确定非功能性需求,如安全性、可用性、扩展性等
在需求分析时要充分考虑异常流程和业务边界
在类的设计与实现时要符合单一原则
谨慎使用继承的方式进行扩展,优先使用聚合和组合
根据依赖倒置原则,尽量依赖抽象类与接口,更有利于扩展
对扩展开放,对修改闭合
共性的业务和行为抽取出来公共模块和配置
设计文档的主要目的是明确需求、理顺逻辑、后期维护、次要目的是知道编码
可扩展性的本质是找到系统的变化点,并隔离变化点
设计的本质就是识别和表达系统的难点
清晰的代码只是文档的某个片段,而不是全部