【本周面试题】第4周 - 内存机制相关

1、解释一下内存泄露是什么意思?

2018-12-03 20:57:24javascript

 

内存泄漏指任何对象在您再也不拥有或须要他以后,其仍然存在内存中。html

也就是程序中分配的堆内存空间没有被及时释放或没法释放,致使的内存占用过多,形成程序运行速度减慢甚至卡顿、崩溃。前端

在浏览器中识别内存泄漏的方法:java

  • 打开开发者工具,选择 Memory
  • 在右侧的Select profiling type字段里面勾选 timeline
  • 点击左上角的录制按钮。
  • 在页面上进行各类操做,模拟用户的使用状况。
  • 一段时间后,点击左上角的 stop 按钮,面板上就会显示这段时间的内存占用状况。

一段”高清“gif展现给你:面试

 

详见:【进阶1-4期】JavaScript深刻之带你走进内存机制数组

2、堆栈存储数据的特色 相关面试题:

一共给本身出了三道题:浏览器

1. 基本类型值的拷贝:bash

1 var a = 1; 2 var b = a; 3 console.log(b); 4 b = 2; 5 console.log(a);

 

2. 引用类型值的拷贝:(浅拷贝)数据结构

1 var c = { 2   n: 1
3 } 4 var d = c; 5 d.n = 3; 6 console.log(c.n);

 

3. 基本类型值的拷贝:【经典】闭包

1 var e = { 2   n: 1
3 } 4 var f = e; 5 e.x = e = { n: 4}; 6 console.log(e.x); 7 console.log(f);

 

 

答案和解析:

第一题打印:

> 1

> 1

首先第一个b弹出1,毫无疑问。能够理解成b = a = 1。

实际上他是执行:

var b; 给b建立一块空间

b = a;先查找a的值(1),再赋予给b变量。

而后第二个,依旧弹出1。

以前你理解b和a相等了,b变成2是否是a也要变成2啊?不是的,由于b和a不是一块空间。

通俗点理解,就像是拷贝了a的一个副本给b。此时咱们修改(副本)b的值,对a毫无影响。

因此尽管b为2了,a仍是1。

 

第二题打印:

> 3

刚看完上边说a和b不影响,再看这里是否是懵逼了。说好的不影响,怎么d.n改为了3,c.n也改为了3呢?

其实啊,这就是堆栈空间存值的不一样和拷贝堆内存数据的特色了:

首先要知道,堆栈空间存的值不一样:

var c = {}; 定义一个对象c。

js引擎要作的事情以下:

  • var c; //栈内存开辟一块空间放c
  • {n: 1}; // 堆内存开辟一块空间放对象({n: 1}能够当成一本书,书架就是堆空间,就好像在书架上放了一本书)
  • c = {n: 1}; 栈中找到c,赋予对象所在堆内存的地址。(拿书的索书号给了c,并非书自己给了c。索书号就是对象在堆空间的地址)
  • 而后, var d; // 同c,给d开辟一块空间放栈内存
  • d = c; // 获取c的值(就是那个索书号地址),copy一份给d。d拿到的一样是c的副本。可是这个副本是一个指针,两者同时指向堆空间的{n: 1};

以后的代码,运行d.n = 3。就是把堆内存中惟一一处的那个{n:1}对象的n值给修改了。

此时,堆内存中{n:1}这个对象,如今变成了{n:3}。

 

第三题打印:

> undefined

> 一个对象:展开以下:

{ n: 1, x: { n:4 } }

 

此题是一个经典面试题,

首先第1-4行,和第二题定义c一个套路。

栈空间存了变量e和指针,堆空间存了对象{n:1};将e的指针再赋予f。

关键是第5行,这里若是你按从左向右顺序理解的话结果应该是:

各类可能

 

但这道题的坑点就在于js不是按照你想的、你阅读的顺序执行的。

首先,js中正确的赋值顺序是从右向左的。

也就是应该先从最后边开始,先将等号右边的赋值给等号左边。

但这道题还有坑点就是运算符的优先级:点比等号的优先级高。

也就是说,先执行的是

e.x = {n : 4}

 

而后执行:

e = { n: 4}

 

首先说先执行的代码执行后的结果

e.x被赋值后,至关于堆内存中,对象{n:1}多了一个属性x,而且值是{n : 4};

此时,e和f的值状况分别是这样的(注意下边的obj1和obj2分别是我本身为了区分对象给命名的)

 

而后,执行e = { n : 4}; 此时js引擎要作的事情就是:

开辟一个新的堆空间放新对象obj2 --> {n : 4}

而后将这个新对象的地址从新赋值给e。这一步能够这么理解:

var e = 'obj1的地址'; e = 'obj2的地址';

 

最终e在栈空间的值就是obj2的地址。

 

最后,执行console.log(f):

虽然e和obj1切断了关系,可是f还和obj1有关系。因此f打印出来的就是整个obj1。

思考:e.x为何是undefined而不报错? 

这里e被从新指向obj2后,obj2里边并无x值,因此e.x是不存在的。

正常状况下,找不到一个变量会报错: 变量 is not defined。可是为何对象的属性值不存在时只是输出undefined呢?

这一点,咱们获取e.x会去对象身上找,若是不存在x,会去对象的原型链上找,一直找到最顶端,都找不到时就返回“undefined”

 

3、引擎和做用域的对话

2018-12-05  21:14:46

观察下边的这段代码,进行一次引擎和做用域的对话:

1 function foo(a){ 2   var b = a; 3   return a + b; 4 } 5 var c = foo(2);

找出里边的几处RHS查询、几处LHS查询?分别是哪里?

 

 

答案:

 

4、垃圾回收机制 

背诵,具体文案见本周主题阅读第四条

 垃圾回收器按期扫描对象,并计算引用了每一个对象的其余对象的数量。若是一个对象的引用数量为 0(没有其余对象引用过该对象),或对该对象的唯一引用是循环的,那么该对象的内存便可回收。

 

 

5、从内存看,null和undefined的本质区别是什么?

null也是js基本类型之一,它是一个字面量,特指对象的值未设置。表示缺乏的标识,指示变量未指向任何对象。

undefined也是js基本类型之一,是全局对象的一个属性。声明一个变量,未进行初始化时,这个变量的值就是undefiend,

给一个全局变量赋值null,至关于将这个变量的指针对象及值清空,若是是给对象的属性赋值null,或者局部变量赋值为null,至关于给这个属性分配一块空的内存空间,而后值是null。

js会回收全局变量为null的对象。

 

6、ES6语法中,const声明一个只读的常亮,那为何下面能够修改const的值

const foo = {};

// 为foo添加一个属性,能够成功
foo.prop = 123;
for.prop; // 获得123

// 将foo指向另外一个对象,报错:
foo = {};//TypeError: 'foo' is read-only

  

const实际上并非指变量的值不可改动,而是变量指向的那个内存地址所保存的数据不得改动。

对于简单类型的数据(string、number、boolean、null、undefined),值就保存在变量指向的那个内存地址,所以等同于常量

但对于引用类型的数据(复合类型的数据:对象、数组),变量指向的内存地址,保存的是一个指向堆内存实际数据的指针,const只能保证这个指针

是固定的(即指向的地址不变)。至于这个地址对应的堆内存中数据结构的变化,他是控制不了的。

所以,在上例中,为foo添加属性,实际上操做的是堆内存中foo对象的数据结构,const管不着,

而改变foo的指针,指向另外一个对象,const是不答应的。

 

7、前端中内存泄露的几种状况:

(见本周阅读主题篇第六条)

 

 

setTimeout 的第一个参数使用字符串而非函数的话,会引起内存泄漏。
闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)

 

 

8、扩展题:浅拷贝和深拷贝

学了堆栈内存空间,应该就理解了什么叫简单数据类型存在栈内存,复杂数据类型存在堆内存了。

而后面试中,常常会问、业务中也常常会遇到的问题就是深浅拷贝的问题了。

栈内存中简单数据类型直接拷贝就能获得一个副本,可是复杂数据类型的拷贝若是也想获得一个副本,就须要深拷贝了。

具体源码见文章《js中的浅拷贝和深拷贝

相关文章
相关标签/搜索