JS面试题汇总(四)

参考答案:

1. 块级作用域

JS 中作用域有:全局作用域、函数作用域。没有块作用域的概念。ECMAScript 6(简称 ES6)中新增了块级作用域。块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作用域。

2. 变量提升

  • 如果变量声明在函数里面,则将变量声明提升到函数的开头

  • 如果变量声明是一个全局变量,则将变量声明提升到全局作用域的开头

解析:

< script type = “text/javascript” > {

var a = 1;

console.log(a); // 1

}

console.log(a); // 1

// 可见,通过var定义的变量可以跨块作用域访问到。

(function A() {

var b = 2;

console.log(b); // 2

})();

// console.log(b); // 报错,

// 可见,通过var定义的变量不能跨函数作用域访问到

if (true) {

var c = 3;

}

console.log©; // 3

for (var i = 0; i < 4; i++) {

var d = 5;

};

console.log(i); // 4 (循环结束i已经是4,所以此处i为4)

console.log(d); // 5

// if语句和for语句中用var定义的变量可以在外面访问到,

// 可见,if语句和for语句属于块作用域,不属于函数作用域。

{

var a = 1;

let b = 2;

const c = 3;

{

console.log(a); // 1 子作用域可以访问到父作用域的变量

console.log(b); // 2 子作用域可以访问到父作用域的变量

console.log©; // 3 子作用域可以访问到父作用域的变量

var aa = 11;

let bb = 22;

const cc = 33;

}

console.log(aa); // 11 // 可以跨块访问到子 块作用域 的变量

// console.log(bb); // 报错 bb is not defined

// console.log(cc); // 报错 cc is not defined

} <

/script>

拓展:

var、let、const 的区别

  • var 定义的变量,没有块的概念,可以跨块访问, 不能跨函数访问。

  • let 定义的变量,只能在块作用域里访问,不能跨块访问,也不能跨函数访问。

  • const 用来定义常量,使用时必须初始化(即必须赋值),只能在块作用域里访问,而且不能修改。

  • 同一个变量只能使用一种方式声明,不然会报错

< script type = “text/javascript” >

// 块作用域

{

var a = 1;

let b = 2;

const c = 3;

// c = 4; // 报错

// let a = ‘a’; // 报错 注:是上面 var a = 1; 那行报错

// var b = ‘b’; // 报错:本行报错

// const a = ‘a1’; // 报错 注:是上面 var a = 1; 那行报错

// let c = ‘c’; // 报错:本行报错

var aa;

let bb;

// const cc; // 报错

console.log(a); // 1

console.log(b); // 2

console.log©; // 3

console.log(aa); // undefined

console.log(bb); // undefined

}

console.log(a); // 1

// console.log(b); // 报错

// console.log©; // 报错

// 函数作用域

(function A() {

var d = 5;

let e = 6;

const f = 7;

console.log(d); // 5

console.log(e); // 6 (在同一个{ }中,也属于同一个块,可以正常访问到)

console.log(f); // 7 (在同一个{ }中,也属于同一个块,可以正常访问到)

})();

// console.log(d); // 报错

// console.log(e); // 报错

// console.log(f); // 报错

<

/script>

34. null/undefined 的区别

参考答案:

null: Null 类型,代表“空值",代表一个空对象指针,使用 typeof 运算得到 “object",所以你可以认为它是一个特殊的对象值。

undefined: Undefined 类型,当一个声明了一个变量未初始化时,得到的就是 undefined。

35. JS 哪些操作会造成内存泄露

参考答案:

1)意外的全局变量引起的内存泄露

function leak() {

leak = “xxx”; //leak成为一个全局变量,不会被回收

}

2)闭包引起的内存泄露

function bindEvent() {

var obj = document.createElement(“XXX”);

obj.οnclick = function() {

//Even if it’s a empty function

};

}

闭包可以维持函数内局部变量,使其得不到释放。 上例定义事件回调时,由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。

解决之道,将事件处理函数定义在外部,解除闭包, 或者在定义事件处理函数的外部函数中,删除对 dom 的引用。

//将事件处理函数定义在外部

function onclickHandler() {

//do something

}

function bindEvent() {

var obj = document.createElement(“XXX”);

obj.οnclick = onclickHandler;

}

//在定义事件处理函数的外部函数中,删除对dom的引用

function bindEvent() {

var obj = document.createElement(“XXX”);

obj.οnclick = function() {

//Even if it’s a empty function

};

obj = null;

}

3)没有清理的 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’))

}

4)被遗忘的定时器或者回调

var someResouce = getData();

setInterval(function() {

var node = document.getElementById(“Node”);

if (node) {

node.innerHTML = JSON.stringify(someResouce);

}

}, 1000);

这样的代码很常见, 如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someResource 的引用, 定时器外面的 someResource 也不会被释放。

5)子元素存在引起的内存泄露

在这里插入图片描述

黄色是指直接被 js 变量所引用,在内存里,红色是指间接被 js 变量所引用,如上图,refB 被 refA 间接引用,导致即使 refB 变量被清空,也是不会被回收的子元素 refB 由于 parentNode 的间接引用,只要它不被删除,它所有的父元素(图中红色部分)都不会被删除。

6)IE7/8 引用计数使用循环引用产生的问题

function fn() {

var a = {};

var b = {};

a.pro = b;

b.pro = a;

}

fn();

fn()执行完毕后,两个对象都已经离开环境,在标记清除方式下是没有问题的,但是在引用计数策略下,因为 a 和 b 的引用次数不为 0,所以不会被垃圾回收器回收内存,如果 fn 函数被大量调用,就会造成内存泄漏。在 IE7 与 IE8 上,内存直线上升。

IE 中有一部分对象并不是原生 js 对象。例如,其内存泄漏 DOM 和 BOM 中的对象就是使用 C++以 COM 对象的形式实现的,而 COM 对象的垃圾回收机制采用的就是引用计数策略。因此,即使 IE 的 js 引擎采用标记清除策略来实现,但 js 访问的 COM 对象依然是基于引用计数策略的。换句话说,只要在 IE 中涉及 COM 对象,就会存在循环引用的问题。

var element = document.getElementById(“some_element”);

var myObject = new Object();

myObject.e = element;

element.o = myObject;

上面的例子在一个 DOM 元素(element)与一个原生 js 对象(myObject)之间创建了循环引用。其中,变量 myObject 有一个名为 e 的属性指向 element 对象;而变量 element 也有一个属性名为 o 回指 myObject。由于存在这个循环引用,即使例子中的 DOM 从页面中移除,它也永远不会被回收。

看上面的例子,有人会觉得太弱了,谁会做这样无聊的事情,但是其实我们经常会这样做

window.οnlοad = function outerFunction() {

var obj = document.getElementById(“element”):

obj.οnclick = function innerFunction() {};

};

这段代码看起来没什么问题,但是 obj 引用了 document. getElementById(“element”),而 document. getElementById(“element”)的 onclick 方法会引用外部环境中的变量,自然也包括 obj,是不是很隐蔽啊。

最简单的解决方式就是自己手工解除循环引用,比如刚才的函数可以这样

myObject.element = null;

element.o = null;

window.οnlοad = function outerFunction() {

var obj = document.getElementById(“element”):

obj.οnclick = function innerFunction() {};

obj = null;

};

将变量设置为 null 意味着切断变量与它此前引用的值之间的连接。当垃圾回收器下次运行时,就会删除这些值并回收它们占用的内存。 要注意的是,IE9+并不存在循环引用导致 Dom 内存泄漏问题,可能是微软做了优化,或者 Dom 的回收方式已经改变

解析:

1、JS 的回收机制

JavaScript 垃圾回收的机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但是这个过程不是实时的,因为其开销比较大,所以垃圾回收系统(GC)会按照固定的时间间隔, 周期性的执行。

到底哪个变量是没有用的?所以垃圾收集器必须跟踪到底哪个变量没用,对于不再有用的变量打上标记,以备将来收回其内存。用于标记的无用变量的策略可能因实现而有所区别,通常情况下有两种实现方式:标记清除和引用计数。引用计数不太常用,标记清除较为常用。

2、标记清除(mark and sweep)

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

function test() {

var a = 10; //被标记,进入环境

var b = 20; //被标记,进入环境

}

test(); //执行完毕之后a、b又被标记离开环境,被回收

3、引用计数(reference counting)

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值(function object array)赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存。

function test() {

var a = {}; //a的引用次数为0

var b = a; //a的引用次数加1,为1

var c = a; //a的引用次数加1,为2

var b = {}; //a的引用次数减1,为1

}

4、如何分析内存的使用情况

Google Chrome 浏览器提供了非常强大的 JS 调试工具,Memory 视图 profiles 视图让你可以对 JavaScript 代码运行时的内存进行快照,并且可以比较这些内存快照。它还让你可以记录一段时间内的内存分配情况。在每一个结果视图中都可以展示不同类型的列表,但是对我们最有用的是 summary 列表和 comparison 列表。 summary 视图提供了不同类型的分配对象以及它们的合计大小:shallow size (一个特定类型的所有对象的总和)和 retained size (shallow size 加上保留此对象的其它对象的大小)。distance 显示了对象到达 GC 根(校者注:最初引用的那块内存,具体内容可自行搜索该术语)的最短距离。 comparison 视图提供了同样的信息但是允许对比不同的快照。这对于找到泄漏很有帮助。

5、怎样避免内存泄露

1)减少不必要的全局变量,或者生命周期较长的对象,及时对无用的数据进行垃圾回收;

2)注意程序逻辑,避免“死循环”之类的 ;

3)避免创建过多的对象 原则:不用了的东西要及时归还。

参考

36. 重排与重绘的区别,什么情况下会触发?

参考答案:

1. 简述重排的概念

浏览器下载完页面中的所有组件(HTML、JavaScript、CSS、图片)之后会解析生成两个内部数据结构(DOM 树和渲染树),DOM 树表示页面结构,渲染树表示 DOM 节点如何显示。重排是 DOM 元素的几何属性变化,DOM 树的结构变化,渲染树需要重新计算。

2. 简述重绘的概念

重绘是一个元素外观的改变所触发的浏览器行为,例如改变 visibility、outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。由于浏览器的流布局,对渲染树的计算通常只需要遍历一次就可以完成。但 table 及其内部元素除外,它可能需要多次计算才能确定好其在渲染树中节点的属性值,比同等元素要多花两倍时间,这就是我们尽量避免使用 table 布局页面的原因之一。

3. 简述重绘和重排的关系

重绘不会引起重排,但重排一定会引起重绘,一个元素的重排通常会带来一系列的反应,甚至触发整个文档的重排和重绘,性能代价是高昂的。

4. 什么情况下会触发重排?

  • 页面渲染初始化时;(这个无法避免)

  • 浏览器窗口改变尺寸;

  • 元素尺寸改变时;

  • 元素位置改变时;

  • 元素内容改变时;

  • 添加或删除可见的 DOM 元素时。
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里免费获取

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-EwWy7aud-1713516842850)]

[外链图片转存中…(img-Eut7uuI3-1713516842851)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-yjaMDOOn-1713516842851)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-vIUH0iuB-1713516842851)]

结尾

学习html5、css、javascript这些基础知识,学习的渠道很多,就不多说了,例如,一些其他的优秀博客。但是本人觉得看书也很必要,可以节省很多时间,常见的javascript的书,例如:javascript的高级程序设计,是每位前端工程师必不可少的一本书,边看边用,了解js的一些基本知识,基本上很全面了,如果有时间可以读一些,js性能相关的书籍,以及设计者模式,在实践中都会用的到。

资料领取方式:戳这里免费获取

html5

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值