细说WeakMap

先看一段简单的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>二维数组持有dom</title>
</head>
<body>
<div>

    <input type="text" id="kw"/>
    <button id="search">搜索</button>

    <button id="release">释放</button>
</div>
</body>
</html>
<script>

    let e2 = document.getElementById('search');
    let arr = [[e2, {clickTimes: 0}]];//dom节点与点击次数是一一对应的

    e2.onclick = function () {
        let e1 = document.getElementById('kw');
        arr[0][1].clickTimes++
        e1.value = arr[0][1].clickTimes
    }
    document.getElementById('release').onclick = function () {
        e2.onclick = null

        e2 = null;
        console.log(arr)
    }
</script>

e2是一个dom对象,arr数组对这个对象形成了引用关系。
当e2使用完毕后,点击释放按钮,发现arr仍旧持有e2,也就是说,e2仍在内存中,没有被释放掉。
为了保证内存不会泄露,我们只能手动释放:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>二维数组持有dom</title>
</head>
<body>
<div>

    <input type="text" id="kw"/>
    <button id="search">搜索</button>

    <button id="release">释放</button>
</div>
</body>
</html>
<script>

    let e2 = document.getElementById('search');
    let arr = [[e2, {clickTimes: 0}]];//dom节点与点击次数是一一对应的

    e2.onclick = function () {
        let e1 = document.getElementById('kw');
        arr[0][1].clickTimes++
        e1.value = arr[0][1].clickTimes
    }
    document.getElementById('release').onclick = function () {
        e2.onclick = null

        e2 = null;
        arr[0] = null; //手动释放
        console.log(arr)
    }
</script>        

这样写代码很烦,因为你会把注意力过多的放在内存释放上,这样就无法专心逻辑方面的思考了。
怎么办呢?
es6提供了另一个好用的工具—— WeakMap。
WeakMap的键只能是对象,而且所引用的对象都是弱引用,弱引用的意思就是垃圾回收机制不会标记它。也就是说,只要键引用的对象释放了,WeakMap里面的元素就会自动跟着释放,并不需手动清除。
因为浏览器端无法手动调用gc垃圾回收,我们只能在node端玩了,看下面一段代码:
test.js:

let e1 = new Array(5 * 1024 * 1024)
let e2 = new Array(5 * 1024 * 1024)//开辟大块内存空间

let wm = new WeakMap([[e1,'内存1'], [e2,'内存2']]) //对e1和e2进行了持有

global.gc()//手动调用垃圾清除,必须使用命令: node --expose-gc test.js来执行

console.log(process.memoryUsage())
/*e1和e2清除前:
*{ rss: 105156608,
  heapTotal: 93691904,
  heapUsed: 88018144,
  external: 8224 }
* */
console.log('---------')
e1 = null
e2 = null
global.gc()

console.log(process.memoryUsage())
/*e1和e2清除后:
* { rss: 63799296,
  heapTotal: 52260864,
  heapUsed: 46349472,
  external: 8224 }
* */

这段代码大家必须用以下命令来执行,否则垃圾回收代码会报错:

node --expose-gc test.js

我们主要看heapUsed一项,这个表示正在使用的堆内存。
从e1和e2释放前后可以看出,前后堆内存缩小了一半,由此可见,WeakMap对e1和e2的持有,并未影响垃圾的回收。
我们对比下原来的二维数组:

let e1 = new Array(5 * 1024 * 1024)
let e2 = new Array(5 * 1024 * 1024)//开辟大块内存空间

let arr = [[e1,'内存1'], [e2,'内存2']] //对e1和e2进行了持有

global.gc()//手动调用垃圾清除,必须使用命令: node --expose-gc test.js来执行

console.log(process.memoryUsage())
/*e1和e2清除前:
*{ rss: 105107456,
  heapTotal: 93691904,
  heapUsed: 88017976,
  external: 8224 }
* */
console.log('---------')
e1 = null
e2 = null
global.gc()

console.log(process.memoryUsage())
/*e1和e2清除后:
* { rss: 105865216,
  heapTotal: 94216192,
  heapUsed: 88292424,
  external: 8224 }
* */

可以看出,二维数组前后并未释放heapUsed空间。
有了WeakMap,前面的问题就容易解决了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>dom填入WeakMap</title>
</head>
<body>
<div>

    <input type="text" id="kw"/>
    <button id="search">搜索</button>

    <button id="release">释放</button>
</div>
</body>
</html>
<script>

    let e2 = document.getElementById('search');
    let wm = new WeakMap()
    wm.set(e2, {clickTimes: 0}) //回收时,只需保证e2销毁掉即可
    e2.onclick = function () {
        let e1 = document.getElementById('kw');
        let times = wm.get(e2)
        times.clickTimes++
        e1.value = times.clickTimes
    }
    document.getElementById('release').onclick = function () {
        e2.onclick = null
        e2 = null;

        console.log(wm.get(e2))//e2已经没了,不会造成内存泄露
    }

</script>

还有一个妙用就是用于对象私有属性。

const counterWm = new WeakMap();//动作名称
const actionWm = new WeakMap();//回调函数

class CountDo {
    //初始化
    constructor(counter, action) {
        counterWm.set(this, counter);
        actionWm.set(this, action);
    }
    doIt() {
        if(counterWm.get(this)){
            actionWm.get(this)()
        }
    }
}

let c = new CountDo('do1', () => console.log('做第一件事'))

c.doIt()
console.log(counterWm.get(c))
console.log(actionWm.get(c))
c = null //当c销毁后,两个WeakMap里面的元素会自然清除掉
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值