基础-必会知识(中)

2 基础-必会知识(中)

2.1 请问你了解js中的闭包吗?

【考点映射】

  • js闭包

【频率】★★★★★

【难度】☆☆

【参考答案】

概念一:闭包是指有权访问另一个函数作用域中的变量的函数(概念出自《JavaScript高级程序设计》)

概念二:一个函数和对其周围状态(词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包,也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。(概念出自MDN)

可以简单理解为:闭包就是一个函数,一个外部函数通过调用函数并return返回出内部函数,此内部函数就是一个闭包

js作用域只能函数内部向外层访问,闭包就是将函数内部和函数外部连接起来的一座桥梁,能够在函数外部访问到函数内部作用域的局部变量的函数
function f1(){
    var n=999;
  function f2(){  //f2函数就是闭包
    alert(n);
  }
  return f2;// 重点在这里,将闭包函数作为返回值,做到f1能访问到f2的内部局部变量
}
var result=f1();
result(); // 999

此时f2函数形成了一个闭包,因f2函数里需要访问f1作用域下的n变量,但他们不处于同一个作用域,故两者相互牵引,需要输出n,f1中的变量n就必须存在,作用域链在f1中找到n,输出n时,垃圾回收机制会认为f2还没有执行完成,但此时作用域链查找已经到了f1作用域下,所以n的内存空间不会被垃圾回收机制清除

闭包优点:

  • 可以读取函数内部的变量

  • 延长局部变量寿命,不被垃圾回收机制销毁

  • 封装变量(模仿块级作用域)

高频考题:

for(var i=0;i<5;i++){
      setTimeout(function(){
            console.log(i); //输出5个5
      });
} 

预期应该是输出0、1、2、3、4,但实际是输出5个5,因为setTimeout事件是被异步触发的,当事件被触发的时候,for循环早已经结束

可利用闭包解决该问题:将每次循环的i值封闭起来, 当沿着作用域链从内到外查找变量i时,会先找到被封闭在闭包环境中的i

//1、在setTimeout外部创建一个自执行函数,并将i当作参数传递进闭包
for(var i=0;i<5;i++){
    (function(num){
        setTimeout(function(){
            console.log(num);   // 输出0,1,2,3,4         
        }, num*1000);
      }
    )(i)
}

//2、在setTimeout内部函数创建一个闭包,并将i当作参数传递进去
for(var i=0;i<5;i++){
        setTimeout(function(num){
            return function(){ //用匿名函数打造一个num变量副本
            	console.log(num);   // 输出0,1,2,3,4 
            }    
        }(i), i*1000);
} 

闭包缺点:

  • 闭包会导致变量不会被垃圾回收机制所清除,会大量消耗内存

  • 使用不恰当可能会造成内存泄漏的问题

避免闭包引起的内存泄漏:

1、在退出函数之前,将不使用的局部变量全部删除或者赋值为null

将变量设置为null:切断变量与它此前引用的值之间的连接,当垃圾回收器下次运行时,会删除这些值并回收它们占用的内存

2、避免变量的循环赋值和引用


2.2 请问js垃圾回收机制是什么工作原理?

【考点映射】

  • js垃圾回收机制

【频率】★★★★★

【难度】☆

【参考答案】

js语言有 自动垃圾回收机制,执行环境会管理 代码执行过程中使用的内存,垃圾收集器会定期(周期性)找出不再继续使用的变量,然后释放其内存

不再使用的变量:生命周期结束的变量(局部变量),全局变量的生命周期直至浏览器卸载页面才会结束

栈内存 垃圾回收:

栈内存中的垃圾回收其实就是销毁执行栈中的执行上下文,栈顶就是正在执行函数的执行上下文, 当函数执行完毕后,执行栈中对应的执行上下文会被销毁

ESP 是执行栈中用来记录当前执行状态的指针, 当执行完一行后,ESP 指针下移,即该行对应的上下文被回收。 可理解为js引擎就是通过ESP指针的下移操作完成栈内存中的垃圾回收

堆内存 垃圾回收:

js中堆内存的垃圾回收主要建立在 代际假说 和 分代收集 两个概念上

代际假说:

  • 大部分对象的存活时间都很短,分配完内存以后很快就变得不可访问

  • “不死”的对象,存活时间都很长

分代收集:

  • 堆内存分为 新生代 和 老生代 两个区域

  • 新生代区域:存放的都是存活时间比较短,占内存比较小的对象

  • 老生代区域:存放的都是存活时间比较长,占内存比较大的对象

主垃圾回收器和副垃圾回收器:

新生代区域:副垃圾回收器

老生代区域:主垃圾回收器

这两个垃圾回收器的大致工作流程是相同的,可以简化为三步:

(1)、标记待回收的内存

(2)、垃圾内存回收

(3)、内存碎片整理(频繁的垃圾回收后,会产生很多不连续的内存空间,不利于后续数据的存储)

副垃圾回收器 工作流程

主要是对新生代区域进行垃圾回收,新生代区域的内存空间比较小,大约是 1~8M

采用的是 Scavenge 算法 进行垃圾回收,主要是将新生代区域 分成两部分:空闲区域 和对象区域, Scavenge 算法具体工作流程:

(1)、所有进入新生代区域新产生的对象都会存放到对象区域中

(2)、当对象区域被写满的时候会进行垃圾回收

(3)、垃圾回收器会标记垃圾数据(使用“标记清除算法”)

(4)、标记完成后对象区域会将有效数据按照一定顺序存放到空闲区域的一端

(5)、存放好后,对象区域和空闲区域会角色互换

(6)、清空当前的空闲区域的内存空间

其中,因为是对象区域的有效数据按照一定顺序放到了空闲区域中,所以也顺便完成内存碎片的整理

注意:新生代区域的空间很小,经常很快被填满,js有一个对象晋升策略解决这种情况:

对象晋升策略规定:两次垃圾回收还存活的对象就会被移动到老生代区域

主垃圾回收器 工作流程

对老生代区域进行垃圾回收,老生代区域的内存空间要大很多,用 Scavenge算法 效率明要低很多,还是按照以下三步进行垃圾回收:

(1)、通过标记清除算法,标记垃圾数据

(2)、标记垃圾数据后,主垃圾回收器开始进行垃圾回收,把可回收对象加入到空闲列表中

(3)、 剩下就是内存碎片整理,主垃圾回收器会将存活的对象移动到一端,然后清理掉边界以外的内存

【延伸考点】

1、什么是标记清除算法与引用计数算法?

两算法都是针对垃圾数据标记的

标记清除:js中最常用的垃圾回收方式,当变量进入环境时,(一般是在函数中声明一个变量),将这个变量标记为“进入环境”。而当变量离开环境时,则将其标记为“离开环境”。逻辑上讲,永远不能回收 进入环境的变量 所占用的内存,因为当执行流进入相应的环境,就可能会用到它们

function test(){
    var a =10;//被标记 ,进入环境
    var b =20;//被标记 ,进入环境
}
test();//执行完毕 之后 a、b又被标离开环境,被回收

引用计数:跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,这个值的引用次数是1;若同一个值又被赋给另一个变量,则该值的引用次数再加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减1

当这个值的引用次数变成0时,则表示没有办法再访问这个值了,其占用的内存空间可回收

function test(){
    var a ={};//a的引用次数为0
    var b = a ;//a的引用次数加1,为1 
    var c = a;//a的引用次数再加1,为2
    var b ={};//a的引用次数减1,为1
}

注意:引用计数算法是js早期的垃圾标记算法,现在几乎不怎么用,该算法存在一个问题:无法应对互相引用的情况,当两个对象互相引用时,就会永远无法被回收,从而造成内存泄漏。 基于这个问题,后来提出了标记-清除算法


2.3 请问js有哪几种常见的内存泄露情况?

【考点映射】

  • js内存泄露

【频率】★★★★

【难度】☆

【参考答案】

1、闭包

闭包可以延长局部变量寿命,若使用不当则会导致内存泄露

2、意外的全局变量

js中如果不用var声明变量,该变量将被视为window对象(全局对象)的属性,也就是全局变量,目前开发场景中:主要还是使用let和const较多

function foo(arg) {
    bar = "this is a hidden global variable";
}
function foo(arg) {
    window.bar = "this is an explicit global variable";
}

上面代码中两个函数是等价的,调用完函数后,变量仍然存在,会导致泄漏

如果不注意this的话,也可能发生内存泄露:

function foo() {
    this.variable = "potential accidental global";
}
foo();// 没有对象调用foo, 也没有给它绑定this, 所以this是window

解决办法:加上“use strict”,启用严格模式来避免,严格模式会组织创建意外的全局变量

3、被遗忘的定时器或者回调

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

如上代码,若id为Node的元素从DOM中被移除,但定时器仍会存在,因为回调函数中包含对someResource的引用,定时器外面的someResource也不会被释放

4、没有清理的DOM元素引用

var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
};
function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
}
function removeButton(){
document.body.removeChild(document.getElementById('button'));
}

虽用removeChild移除了button,但是还在elements对象里保存着button的引用,DOM元素还在内存里面


2.4 请问你了解js的原型链吗?

【考点映射】

  • js原型链

【频率】★★★★★

【难度】☆☆

【参考答案】

与其他面向对象语言不同,ES6之前js没有引入类(class)的概念,js并非通过类而是直接通过构造函数来创建实例

构造函数与实例原型

在js中,每当定义一个函数(普通函数、类)时候,都会天生自带一个prototype属性,这个属性指向函数的原型对象,并且这个属性是一

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

前端岗面试求职真题解析 文章被收录于专栏

前端岗位面试求职攻略及真题解析~~

全部评论

相关推荐

代码飞升_不回私信人...:别这样贬低自己,降低预期,放平心态,跟昨天的自己比。做好自己,反而会效率更高心态更好,加油兄弟
点赞 评论 收藏
分享
bg:双非本,一段中小厂6个月测开实习今天发这个帖子主要是想聊一聊我秋招以来的一个发展我是在8月底辞职,打算秋招,可是看网上都说金九银十就想着自己就是一个普通本科生,现在九月份都是一些大神在争抢,所以9月份基本上没投,等到了10月份才开始秋招,可是这个时间好像已经有些晚了,今年秋招开启的格外早,提前到了7,8月份,我十月才开始,官网投了很多公司,没有任何一个面试机会,这个情况一直到了十月底才有了第一个面试,当时没有面试经验,所以不出意外的挂了后续就是漫长的投递,但是毫无例外没有面试,没有办法我只能另辟蹊径开始在BOSS上边投递,然后顺便也根据BOSS上边这个公司名称去浏览器搜索看看有没有官网投递渠道,毕竟官网上投递后还是可以第一时间被HR看到的,然后一直不停投递,一开始第一个星期基本上都是投的正式秋招岗位到了第二个星期才开始实习和正式一起投,到十一月底的时候已经沟通了700➕才有一共1个正式的,5个要提前实习的,3个实习的面试,最后结果是过了1个要提前实习的和2个实习的每次面试我都会复盘,发现这些小公司面试官问的五花八门,有的专问基础,有的专问项目,有的啥都问,不过自己也是看出来了一下门道,就是小公司不像大公司面试官那样能力比较强基本上你简历上边的他都会,然后会根据简历来问,小公司面试官他们更多的是看自己会什么,然后看看你简历上边哪些他也是会的然后来问,经过不断的复盘加上背各种各样面试题,到了11月底12月初才有了1个要提前实习的offer还有2个实习的offer,而且薪资待遇对我来说已经很可观了可是啊,人总是这样得了千钱想万钱,我又开始不满现状,但是此时的我面试能力经过这么多面试和复盘已经很强了,然后在十二月份运气爆棚,被极兔和小鹏补录捞起来面试,还有个百度测开的实习面试,这个时候因为有了offer所以感觉有了底气,面试也很自信,最后结果是全部都过了那个时候我感觉自己真的很厉害,我问了极兔那边的HR像我这样的双非本收到offer的在极兔有多少?他告诉我产研岗90%都是硕士,10%里边基本上都是211,985,想我这样的很少很少,那一刻感觉自己超级牛逼,小鹏就更不用说了,最后也是不出意外选择了小鹏所以我就我个人经历想对和我学历履历差不多的牛友一些建议第一:秋招一定要趁早,真到了9,10月,那个时候可能你投的结果可能还不如7,8,11月,第二:最好先拿小公司实习或者正式练练手,提升一下面试能力,我个人觉得因为小公司问的五花八门所以你会更加横向去提升自己能力,而且大公司其实面试没有那么难,除了一些非常卷的岗位,公司大神比较多会问的很难,一般好点的公司都不会问的那么难,他们也知道都是应届生不会要求那么高第三:当有一定能力后,就是坚持了,对于我们这样的学历,没有特别强的履历情况下,就是要抓住提前批和补录的机会,这个时候各方面不会卡的很严,是我们很好很好的一个机会第四:就是运气也是很重要的一部分,不过这个很难去说什么最后祝各位牛友都能收获自己满意的offer😁😁😁
秋招,不懂就问
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务