zoukankan      html  css  js  c++  java
  • 【本周面试题】第4周

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

    2018-12-03 20:57:24

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

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

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

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

    一段”高清“gif展示给你:

     

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

    二、堆栈存储数据的特点 相关面试题:

    一共给自己出了三道题:

    1. 基本类型值的拷贝:

    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”

     

    三、引擎和作用域的对话

    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查询?分别是哪里?

    答案:

     

    四、垃圾回收机制 

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

     垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为 0(没有其他对象引用过该对象),或对该对象的惟一引用是循环的,那么该对象的内存即可回收。

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

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

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

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

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

    六、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是不答应的。

    七、前端中内存泄露的几种情况:

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

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

     

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

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

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

    栈内存中简单数据类型直接拷贝就能得到一个副本,但是复杂数据类型的拷贝如果也想得到一个副本,就需要深拷贝了。

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

  • 相关阅读:
    计算2*3+(2*(5+6)*3)/2+4*6的值
    单链表 删除倒数第m个元素的实现
    string.data()和string.c_str()
    c++ 打印时间
    二分查找
    两个整数相除的计算
    查看一个数字是不是回环数(对称)
    编译#include <stdio.h> 等用尖括号指定的文件提示找不到 VS googleTest 安装的makeInstall
    八皇后---递归
    分治算法--求m的n次方
  • 原文地址:https://www.cnblogs.com/padding1015/p/10060702.html
Copyright © 2011-2022 走看看