在前面的文章中,我们分别介绍了
今天我们将通过Chrome devtool的Performance
和Memory
来排查程序中的内存泄漏情况。然后我们将通过4道经典的闭包面试题来深入巩固闭包的知识。
使用Chrome devtool来排查内存泄漏情况
var array = []
function createNodes() {
let div
let i = 100
let fragment = document.createDocumentFragment()
for (; i > 0; i--) {
div = document.createElement('div')
div.appendChild(document.createTextNode(i))
fragment.appendChild(div)
}
document.body.appendChild(fragment)
}
function badCode() {
array.push([...Array(100000).keys()])
createNodes()
setTimeout(badCode, 1000)
}
badCode()
上面代码递归调用了badCode
函数,每次向array
数组中写入100000个数字的新数组,变量array
为全局变量并且没有手动释放内存的操作,垃圾回收机制也不会处array
变量,这就导致了内存泄漏;同时badCode
函数还调用了createNodes
函数,每秒创建100个div元素插入到body尾部。
我们先把上面的代码保存到一个HTML文件中,然后在Chrome中打开。
这时我们打开Chrome devtool开发者工具,点击Performance
,
点击这个录制按钮
大约等待20-30秒后,点击Stop
按钮
从图中我们可以看到,JS Heap和Nodes线随着时间一直在上升,并没有被垃圾回收机制回收。因此,可以判断这段代码中可能存在内存泄漏的风险。
而正常的网页可以看到随着时间的推移,JS Heap和Nodes都被垃圾回收正常的释放。
如果我们不知道出问题的代码的位置,我还需要使用Memory
标签,对JS Heap中的每一项,尤其是Size较大的前几项进行排查。
我们可以很明显的看出这个array数组的问题。
一个array
的变量占用了高达了180MB的内存。
通过上图我们可以发现问题是出现在全局Window中的array
变量。
以上就是我们通过Chrome开发者工具来帮助我们排查可能出现的内存泄漏问题,在日常的工作中我们也可以经常用到。
实战面试题目
题一
下面代码将输出什么?
var foo = (function() {
var v = 0
return () => {
return v++
}
}())
for (let i = 0; i < 10; i++) {
foo()
}
console.log(foo())
这道题将输出10,下面我们来一步步分析
foo是一个立即自执行函数,当我们打印foo可以看到
var foo = (function() {
var v = 0
return () => {
return v++
}
}())
console.log(foo)
// 输出
/*
() => {
return v++
}
*/
在执行foo函数时,变量v自增10次,最后执行foo时,得到10。
题二
下面代码将输出什么?
var foo = () => {
var arr = []
var i
for (i = 0; i < 10; i++) {
arr[i] = function(){
console.log(i)
}
}
return arr[0]
}
foo()()
这道题将输出10,这道题也是一道常考题。
这道题类似上面的那道题,执行foo函数返回的是arr[0]
,那arr[0]就是下面的匿名函数
function(){
console.log(i)
}
当运行这个函数的时候,就需要去找变量i的值,那么for循环结束之后变量i就变成了10。
所以运行这个函数就将会输出10。
题三
下面代码将输出什么?
var fn = null
var foo = () => {
var a = 2
function innerFoo() {
console.log(a)
}
fn = innerFoo
}
var bar = () => {
fn()
}
foo()
bar()
正常来说,根据调用栈的知识,foo
函数执行完毕后,其执行上下文也会被释放。但是通过将innerFoo
函数赋值给了全局变量fn
,那么foo
内的变量a
也会被保留下来。所以函数fn
在被执行时,依然可以访问到这个变量,所以将输出2
。
题四
我们将题三中的代码稍加修改,执行下面代码将输出什么?
var fn = null
var foo = () => {
var a = 2
function innerFoo () {
console.log(c)
console.log(a)
}
fn = innerFoo
}
var bar = () => {
var c = 100
fn()
}
foo()
bar()
答案是报错:Uncaught ReferenceError: c is not defined
因为在执行fn函数时,fn已经被复制为innerFoo,变量c并不存在其作用域链上,c只是函数bar的局部变量。
我们可以打个断点清楚的看到,在其作用域中并没有变量c。
欢迎我的公众号【小帅的编程笔记】,让自己和他人都能有所收获!