全局变量的管理
发布于 5 年前 作者 xiuying48 1951 次浏览 来自 分享

本文是我的小程序开发日记的其中一篇, GitHub 原文地址 欢迎star,感谢万分!

前言

在浏览器的环境下有一个全局变量:window
若定义变量时,遗漏了var,此时声明的变量就变成了全局变量,自动挂载到window下,可当做window的属性来访问,也可以直接访问。

小程序的底层也是通过Web实现的,因此同样存在window对象,但是微信团队做了些处理:

微信团队将window设置成了writable:false,且值也为undefined

即我们无法像在web那样任意声明全局变量。但微信团队提供了其他的全局变量,比如常用的wxglobal

问题

虽然window是只读的,但是global是可写的:

因此常见的做法,就是将需要全局访问的变量都保存到global下,间接声明了全局变量。

全局变量的污染,在小团队的项目里可能没什么感知。但是在一个大型的项目里,是非常常见的,一不小心就将别人声明的变量覆盖了。

另外如果可以随意注册全局变量,又不加以管理的话,有可能会导致内存泄漏,最终导致应用闪退。

同理,setStorage也存在同样的问题。

思考

简单地将这些变量改成readonly肯定是不可取的,这影响了日常的开发。

在早期的前端开发中,也有同样类似的全局变量污染的问题,我依稀记得两种解决方案:

  • 命名空间
  • 模块化

其中 模块化 明显不是这个问题的解决方案。因为目前的确是需要全局变量的,问题只是如何避免污染和管理全局变量而已。

因此 命名空间 是可以深入探索的思路。

实践

命名空间

命名空间是一种常用的代码组织形式。

大致做法是,先通过命名分配空间,再使用空间。

我的习惯是,用业务或者功能来命名空间

global.localStorage = {
    doSet() {},
    doGet() {},
    doClear() {}
}

global.util = {
    format() {},
    valide() {}
}

命名空间是通过互相约定的方式来工作的,因此仍然会存在覆盖的问题。

Symbol

Symbol是ES2015中新增的基本数据类型。这个类型有个特别之处,每个Symbol()返回的值都是独一无二的,举个例子:

Symbol('foo') === Symbol('foo') // false

因此通过Symbol的方式,可以完美避免变量被覆盖:

// car.js
let car = Symbol()
global[car] = {}

// health.js
let health = Symbol()
global[health] = {}

由于每个Symbol返回的值是唯一的,因此这个Symbol可以单独保存,以便各个文件引用。

由于 Symbol 属于新特性,因此需要关注下兼容性

管理声明

通过Symbol的方式解决了变量的污染问题,但仍然无法对全局变量的声明进行管理。

我想到的办法就是给 global 增加个代理,对 global 的任何操作,都先经过代理检测,这样就有了强力的保障。

因此,可以使用新特性:Proxy 来监听 global 的变更,举例说明:

global = new Proxy(global, {
    set(obj, prop, val) {
        if (prop in obj) {
            throw new TypeError(`${prop}: 该属性已定义!`)
        }

        // 可以做其他策略
        // 或者上报数据,让你知道有哪些人偷偷定义了全局对象
        obj[prop] = val
        return true
    },
})

由于 Proxy 属于新特性,因此需要关注下兼容性

总结

使用 Proxy 之后,能对 global 的各种操作(设置属性,设置原型等13种操作)进行监控,即能避免重复定义变量,也可以很好的管理全局变量,两全其美。

2 回复

global是如何获取到的,就像用wx一样使用吗?

小程序插件开发中 有这个global吗

回到顶部