资讯专栏INFORMATION COLUMN

在前端如何保护共享对象

solocoder / 2915人阅读

摘要:只要保证在开发中没有对共享对象的写入操作,那么发布到线上时肯定也没有写入操作,这时候这个保护方法就是多余的。

什么是共享对象
被多次使用到的同一个对象即为共享对象

比如我们用标准的es模块来写一个导出单位转换的模块

//converter module
export default {
    cmToIn(){
        //convert logic
    }
}

当我们在其它模块中使用该模块时,converter即是共享对象,内存中只有一份,不管它被import了多少次。

同理,上面展示的是通用方法的对象集合,在前端项目里,我们也会把一些所谓写死的数据集中封装在某个模块里,方便后期的修改,比如我们实现一个constant常量模块,我们把一些项目中使用的,后期可能会修改的数据放进去

//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的情况,这时候或许就能发现问题。

如果上述问题没有测试出来,阴差阳错的上线之后,这个问题对于大型单页应用是致命的,如果某个用户的reportProductId456,访问了写错的页面后,因为被意外的修改了constant中的reports.productId,会导致后续其它模块在读取时不再是最初的123而出问题

如何保护共享对象 const
const关键字声明的仅防止变量被重新赋值,无法防止对象修改
Object.freeze
可以防止被修改,但是如果对象嵌套时,被嵌套的对象依然可以被修改,需要开发者对要freeze的对象递归遍历进行freeze。最重要的一点是,当我修改一个freeze对象时,虽然修改不成功,但也没有任务失败的提示,在前述场景中,我们还是希望开发者在修改一个不允许的被修改的对象时能及时给出相应的提示。
Proxy
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的兼容即可

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/97350.html

相关文章

  • 第10章:并发和分布式编程 10.1并发性和线程安全性

    摘要:并发模块本身有两种不同的类型进程和线程,两个基本的执行单元。调用以启动新线程。在大多数系统中,时间片发生不可预知的和非确定性的,这意味着线程可能随时暂停或恢复。 大纲 什么是并发编程?进程,线程和时间片交织和竞争条件线程安全 策略1:监禁 策略2:不可变性 策略3:使用线程安全数据类型 策略4:锁定和同步 如何做安全论证总结 什么是并发编程? 并发并发性:多个计算同时发生。 在现代...

    instein 评论0 收藏0
  • 并发编程中,你加的锁未必安全

    摘要:我们知道在并发编程中,不能使用多把锁保护同一个资源,因为这样达不到线程互斥的效果,存在线程安全的问题。两个线程都完成转账操作后,的账户余额可能为,也可能为,但是不可能为。摘要:在编写多线程并发程序时,我明明对共享资源加锁了啊?为什么还是出问题呢?问题到底出在哪里呢?其实,我想说的是:你的加锁姿势正确吗?本文分享自华为云社区《【高并发】高并发环境下诡异的加锁问题(你加的锁未必安全)》,作者:冰...

    不知名网友 评论0 收藏0
  • [Java并发-3]Java互斥锁,解决原子性问题

    摘要:同一时刻只有一个线程执行这个条件非常重要,我们称之为互斥。那对于像转账这种有关联关系的操作,我们应该怎么去解决呢先把这个问题代码化。 在前面的分享中我们提到。 一个或者多个操作在 CPU 执行的过程中不被中断的特性,称为原子性 思考:在32位的机器上对long型变量进行加减操作存在并发问题,什么原因!? 原子性问题如何解决 我们已经知道原子性问题是线程切换,而操作系统做线程切换是依赖 ...

    makeFoxPlay 评论0 收藏0
  • Java并发编程之设计线程安全的类

    摘要:有的情况下我们希望自己设计的类可以让客户端程序员们不需要使用额外的同步操作就可以放心的在多线程环境下使用,我们就把这种类成为线程安全类。 设计线程安全的类 前边我们对线程安全性的分析都停留在一两个可变共享变量的基础上,真实并发程序中可变共享变量会非常多,在出现安全性问题的时候很难准确定位是哪块儿出了问题,而且修复问题的难度也会随着程序规模的扩大而提升(因为在程序的各个位置都可以随便使用...

    SoapEye 评论0 收藏0
  • 孔明说天有不测风云,我说无灾备不上云

    摘要:题记三国时赤壁鏖战,孔明说,天有不测风云,欲破曹公,宜用火攻,万事俱备,只欠东风。现在公共云混战,我想说,无灾备不上云,保护数据,未雨绸缪,带了雨伞,还需雨衣。题记:三国时赤壁鏖战,孔明说,天有不测风云,欲破曹公,宜用火攻,万事俱备,只欠东风。现在公共云混战,我想说,无灾备不上云,保护数据,未雨绸缪,带了雨伞,还需雨衣。未雨绸缪,到底是带雨伞还是雨衣呢?时代在变,人的追求也在变。随着公共云对...

    stormgens 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<