被屡次使用到的同一个对象即为共享对象
好比咱们用标准的es
模块来写一个导出单位转换的模块前端
//converter module export default { cmToIn(){ //convert logic } }
当咱们在其它模块中使用该模块时,converter
便是共享对象,内存中只有一份,无论它被import
了多少次。es6
同理,上面展现的是通用方法的对象集合,在前端项目里,咱们也会把一些所谓写死
的数据集中封装在某个模块里,方便后期的修改,好比咱们实现一个constant
常量模块,咱们把一些项目中使用的,后期可能会修改的数据放进去segmentfault
//constant export default { dateFormatter:'YYYY-MM-DD', reports:{ productId:'123', productName:'456' } }
这里仅示意一下后端
防止共享的对象被意外修改致使线上故障
原则上这些通用的模块,咱们不会,也不会有意的在咱们业务代码中去修改里面的数据,尤为像常量
这样的模块,若是要修改的话,咱们确定修改常量
这个模块。缓存
可是,凡事总有意外,好比说咱们有这样一个场景:根据后端返回的用信息,以及前端写死
的一些常量,来判断某个用户能不能展现某个报表,咱们指望的代码多是这样的工具
import Constant from './constant';//引入咱们前面定义的constant模块 //...其它略 export default View.extend({ render(){ //...其它逻辑略 if(Constant.reports.productId==user.reportProductId){ //.... } } });
注意上述代码中的if
语句,若是错写成:if(Constant.reports.productId=user.reportProductId)
,两个等号的比较写成了一个等号的赋值。测试
若是自测的时候,用户接口里user.reportProductId
返回的正好也是123
,那么先赋值,再作if
判断,成立,作为开发者会错误的觉得这里的逻辑没问题。固然,正常状况下也要测试下用户接口里user.reportProductId
返回不是123
的状况,这时候或许就能发现问题。ui
若是上述问题没有测试出来,阴差阳错的上线以后,这个问题对于大型单页应用是致命的,若是某个用户的reportProductId
是456
,访问了写错的页面后,由于被意外的修改了constant
中的reports.productId
,会致使后续其它模块在读取时再也不是最初的123
而出问题prototype
const关键字声明的仅防止变量被从新赋值,没法防止对象修改
能够防止被修改,可是若是对象嵌套时,被嵌套的对象依然能够被修改,须要开发者对要freeze
的对象递归遍历进行freeze
。最重要的一点是,当我修改一个freeze
对象时,虽然修改不成功,但也没有任务失败的提示,在前述场景中,咱们仍是但愿开发者在修改一个不容许的被修改的对象时能及时给出相应的提示。
es6新增的代理操做对象的方法
Proxy
相关的文章很是多,这里就再也不详细说,咱们借助Proxy
来实现一个Safeguard
方法来保护咱们的共享对象代理
const Safeguard = o => { let build = o => { let entity = new Proxy(o, { set() { throw new Error('readonly'); }, get(target, property) { let out = target[property]; if (target.hasOwnProperty(property) && (Array.isArray(out) || Object.prototype.toString.call(out) == '[object Object]')) { return build(out); } return out; } }); return entity; }; return build(o); }
这里简化了代码,你能够根据本身的须要去调整相应的实现逻辑
使用
const user=Safeguard({ name:'行列', address:{ city:'hz' } });
这个user
对象只能读,不能写,当开发者尝试写入新数据时,会抛出错误提示开发者
在单页应用中,咱们须要把地址栏中的字符串地址解析成对象,方便咱们使用。
好比/path/name?a=b&c=d
,咱们可能解析成这样的对象
{ path:'/path/name', params:{ a:'b', c:'d' } }
若是你统计过你的单页应用,会发现固定的用户老是只访问某些页面,咱们能够在用户访问某个页面时,临时的把地址栏中的这个地址字符串解析一遍,也能够把解析结果存起来,当用户再访问这个页面时,不须要解析,把存起来的结果拿出来使用便可
关于这一块我曾经写过Magix.Cache,详细的来讲明该如何智能的缓存哪些须要的信息
对于缓存后的地址栏信息对象,它就是一个共享对象,要确保它不能被开发者写入新的值,就可使用前面咱们定义的Safeguard
方法来进行保护
在单页应用开发中,有些数据须要后端提供,可是后端提供的这些数据可能在很长一段时间内都不会被修改,好比省市数据,前端不必在每次须要使用这种数据时都请求一次,因此前端能够把该接口的数据缓存下来,来节省请求
对于这样的数据对象,也须要保护,简言之,只要是共用的对象,均须要防止它被意外的修改
前面咱们聊到的Safeguard
方法,在我看来是不必发布到线上的,只要开发阶段存在便可。只要保证在开发中没有对共享对象的写入操做,那么发布到线上时确定也没有写入操做,这时候这个保护Safeguard
方法就是多余的。
如何在开发时保护,而发布到线上时去掉呢?
咱们可使用uglify
这个代码压缩工具的global_defs
配置。好比在开发阶段这样定义
if (typeof DEBUG == 'undefined') window.DEBUG = true; //... const user={ name:'行列', address:{ city:'hz' } } if(DEBUG){ user=Safeguard(user); }
而后在压缩时:
uglify({ compress: { global_defs: { DEBUG: false } }, output: { ascii_only: true } });
那么这样压缩出来的代码就不包含DEBUG
相关的语句了
固然,Safeguard
跟随上线也没有什么大问题,最后这个“关于上线”这块只是想作更深刻的探讨,若是Safeguard
要上到线上,注意Proxy
的兼容便可