一个关于JS操纵符in问题激发的探究

工作是这样的:各人都知道“内存泄露”这回事吧。它有几个常见的场景:

闭包利用不妥引起内存泄漏

(未声明的)全局变量

疏散的DOM节点

(随意的)节制台的打印

遗忘的按时器

轮回引用

内存泄漏需要重视,它是如此严重甚至会导致页面卡顿,影响用户体验!

个中第 3 点引起了我的留意 —— 我虽然清楚地知道它说的是好比:“假设你手动移除了某个dom节点,本应释放该dom节点所占用的内存,但却因为疏忽导致某处代码仍对该被移除节点有引用,最终导致该节点所占内存无法被释放”的环境

<div> <div>我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') let child = document.querySelector('.child') let root = document.querySelector('#root') btn.addEventListener('click', function() { root.removeChild(child) }) </script>

该代码所做的操纵就是点击按钮后移除.child的节点,固然点击后,该节点确实从dom被移除了,但全局变量child仍对该节点有引用,所以导致该节点的内存一直无法被释放。

办理步伐:我们可以将对.child节点的引用移动到click事件的回调函数中,那么当移除节点并退出回调函数的执行上文后就会自动排除对该节点的引用,自然也就不会存在内存泄漏的环境了。(这实际上是在事件中及时检测该节点是否存在,假如不存在则欣赏器必不会触发remove函数的执行)

<div> <div>我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') btn.addEventListener('click', function() { let child = document.querySelector('.child') let root = document.querySelector('#root') root.removeChild(child) }) </script>

这段代码很完美么?不。因为它在每次事件触发后都建设了对child和root节点的引用。耗损了内存(你完全可以想象一些人会狂

点按钮的环境…)。

其实尚有一种步伐:我们在click中去判定当前root节点中是否还存在child子节点,假如存在,则执行remove函数,不然什么也不做!

这就激发了标题中所说的行为。

怎么判定?

遍历?不,过分贫苦!

不知怎的,我溘然想到了 for...in 中的 in 操纵符,它可以基于原型链遍历工具!

我们来还原一下其时的场景:打开GitHub,随便找一个父节点,并获取它:

mygithub

图中画红框的就是我们要取的父元素,橘赤色框的就是要判定是否存在的子元素。

let parent=document.querySelector('.position-relative'); let child=document.querySelector('.progress-pjax-loader');

这里留意,因为获取到的是DOM节点(类数组工具),所以我们在操纵前必然要先处理惩罚一下:

object

let p_child=[...parent.children];

array

然后

console.log(child in p_child);

!!!

为什么呢?(此时笔者还没有意识到工作的严重性)

我想,是不是那边出了问题,用es6的includes API验证一下:

console.log(p_child.includes(child));

yes

没错啊!

再用一般的数组验证一下:

Verification

???

此时,笔者才想起到MDN上查阅一番:

mdn

进而我发明:in操纵符单独利用时它检测的是左侧的值(作为索引)对应的值是否在右侧的工具内部(属性 & 原型上)

回到上面的代码中,我们发明:

vertification_2

这验证了我们的结论。

很显然,“子元素”并不等同于“存在于原型链上” —— 这又引出了一个常识点:!

所以颠末一番“折腾”,源代码照旧应该直接这样写:

<div> <div>我是子元素</div> <button>移除</button> </div> <script> let btn = document.querySelector('button') let child = document.querySelector('.child') let root = document.querySelector('#root') let r_child = [...root.children] btn.addEventListener('click', function() { if(r_child.includes(child)){ // 可能你这里直接判定child是否为null也可以...吧 root.removeChild(child) } }) </script>

略显急遽的末了

所以,看书进修有时候并不能“不求甚解”~

还要勇于“折腾”,学会“查文档”[/风趣脸].

总结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsjfjs.html