2021春招冲刺 - 01.16 prototype.toString原理、垃圾回收机制与内存泄漏等
2021春招冲刺
01.16日 js面试题汇总
1. Object.prototype.toString.call 是如何判断变量的类型的
参考博客 Object.prototype.toString在es3、es5、es6中的规范
在es6中,使用了 [[NativeBrand]] 内部属性替代原有的 [[class]] ,该属性的值对应一个标志值(tag value),可以用来区分原生对象的类型。
根据es6的方法规范,在toString方法被调用时,会执行下面的操作步骤:
- 如果this的值为undefined,则返回"[object Undefined]".
- 如果this的值为null,则返回"[object Null]".
- 让对象O成为调用ToObject(this)的结果.
- 如果O有
[[NativeBrand]]
内部属性,根据key让tag成为对应的标志值. - 否则
- 让hasTag成为调用O的
[[HasProperty]]
内部方法后的结果,参数为@@toStringTag. - 如果hasTag为false,则让tag为"Object".
- 否则,
- 让tag成为调用O的
[[Get]]
内部方法后的结果,参数为@@toStringTag. - 如果tag是一个abrupt completion,则让tag成为NormalCompletion("???").
- 让tag成为
tag.[[value]]
. - 如果Type(tag)不是字符串,则让tag成为"???".
- 如果tag的值为标志值
Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "RegExp", "String"
, "Object"中的任一个,则让tag成为字符串"~"和tag当前的值连接后的结果.
- 让tag成为调用O的
- 让hasTag成为调用O的
- 返回三个字符串"[object ", tag, and "]"连接后的新字符串.
在规范上比es3与es5进行了更丰富的完善,使得在识别Map,Set等也能够返回[object Map]、[object Set]
2. JavaScript的垃圾回收机制是怎样的
在c语言中我们往往使用malloc与free函数s手动进行内存的分配与回收,而JavaScript 中的内存管理是自动执行的。
Js的解释器可以检测到何时程序不再使用一个对象,当确定了一个对象是无用的时候,就把其所占用的内存释放掉。
最常用的垃圾回收方式为 标记清除:
- 垃圾收集器在第一阶段会给所有内存中的变量都加上标记
- 之后进行筛选,去除环境中的变量、以及被环境中的变量引用的变量的标记
- 筛选之后仍然带有标记的变量,销毁其值并回收它们所占用的内存空间。
另一种不常用的为引用计数,因为他会产生循环引用的问题。
它跟踪记录每个值被引用的次数,当声明一个变量并将一个引用类型的值赋给该变量时,这个时候的引用类型的值就会是引用次数+1。如果同一个值又被赋给另外一个变量,则该值的引用次数又+1。
相反如果包含这个值的引用的变量又取得另外一个值,即被重新赋了值,那么这个值的引用就减一。当这个值的引用次数编程0时,表示没有用到这个值,这个值也无法访问,因此环境就会收回这个值所占用的内存空间回收。这样,当垃圾收集器下次再运行时,它就会释放引用次数为0的值所占用的内存。
3. 什么是强引用与弱引用
- 强引用
就是普通引用,是通过关键字new创建的对象所关联的引用。只要有强引用指向一个对象,垃圾收集器就不会触碰这种对象,直到这个对象的所有引用失效。 - 弱引用
弱引用的对象是在正常情况下,回收器遇到就回收,是被积极回收的对象,通过WeakReference类实现。
4. Map与WeakMap的区别有哪些
- Map
作为JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用 字符串 当作键。这给它的使用带来了很大的限制。它是可遍历的,拥有许多默认方法,可以进行各种数据格式的转换。 - WeakMap
与Map结构类似,也是用于生成键值对的集合,但 只接受对象 作为键名(null 除外),不接受其他类型的值作为键名。
键名 是弱引用的,键值依然是正常引用,键名所指向的对象随时可以被垃圾回收。
不能遍历,方法有 get、set、has、delete。
5. null 与 undefined 有什么区别
null: 表示没有对象,即该处不应该有值。通常作为对象原型链的终点,而作为函数的参数时表示空,即不是对象。转number数据类型的时候转为0。
undefined: 表示缺少值,即此处应该有值,但没有定义,转number数据类型的时候转为NaN。当如下情况时,数据的值被赋为undefined。
-
变量被声明了,但没有赋值时
-
调用函数时,应该提供的参数没有提供
-
对象没有赋值的属性
-
函数没有返回值时
console.log(null==undefined); //true 因为两者都默认转换成了false console.log(typeof undefined); //"undefined" console.log(typeof null); //"object" console.log(null===undefined); //false "==="表示绝对相等,但null和undefined类型不一样
6. 常见的内存泄露场景有哪些,如何避免
参考博客 JavaScript内存泄漏的四种方式以及如何避免、
全局变量
JavaScript 处理未定义变量的方式比较宽松:未定义的变量会在全局对象创建一个新变量。由于全局变量是能够通过全局对象例如window能够访问到的,只有在窗口关闭或者刷新页面时才能够被释放,会造成意外的内存泄漏。
当创建变量时未使用 var 或let,就会创建全局变量。而当函数内部使用this创建变量,并在全局调用函数的时候也会造成全局变量。
function funct(){
name = "name";
this.global = "global";
}
funct();
//window.name="name"
//window.global = "global"
在 JavaScript 文件头部加上 'use strict',可以避免此类错误发生。启用严格模式解析 JavaScript ,避免意外的全局变量。
被遗忘的计时器
setTimeout 和 setInterval 是由浏览器专门线程来维护它的生命周期,所以当在某个页面使用了定时器,当该页面销毁时,没有手动去释放清理这些定时器的话,那么这些定时器还是存活着的。
所以当在当前页面的 js 里通过定时器注册了某个回调函数,而该回调函数内又持有当前页面某个变量或某些 DOM 元素时,就会导致即使页面销毁了,由于定时器持有该页面部分引用而造成页面无法正常被回收,从而导致内存泄漏。
所以要求在不需要定时器的时候调用clearInterval或者clearTimeout即使清理。
脱离DOM的引用
DOM 元素的生命周期正常是取决于是否挂载在 DOM 树上,当从 DOM 树上移除时,也就可以被销毁回收了。
而当创建了字典引用DOM,或者在js代码中保存了对某一个dom元素的引用,那么他的生命周期就需要额外考虑字典或js。在移除时需要将所有的引用都删除才能正常清理回收。
闭包
闭包是 JavaScript 开发的一个关键方面:匿名函数可以访问父级作用域的变量。
简单来说可以认为是可以从一个函数作用域访问另一个函数作用域而非必要在函数作用域中实现作用域链结构。闭包找到的是同一地址中父级函数中对应变量最终的值
正常来说,闭包并不是内存泄漏,因为这种持有外部函数词法环境本就是闭包的特性,就是为了让这块内存不被回收,因为可能在未来还需要用到因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,在不再需要的闭包使用结束后需要手动将其清除。
其余未详细介绍的
- 被遗忘的监听者模式
- 被遗忘的事件监听器
- 被遗忘的Map与SEt
3.JS | 场景: 请求合并 - 短时间内需要请求多个资源合并成一个请求发送
// 首先有一个接口其请求路径为 /path
// query有一个id参数支持传一个或者多个id
// /path?id=1
// /path?id=1,2,3
// /path?id=1,2
// 返回内容格式为(假设请求的query是 id=1,2)
const demoRes = {
1:{
data:{}
},
2:{
data:{}
}
}
// request的构成
request({
url:'/path',
query:{
id:''
}
})
// 下面是使用场景实现,每个方法回调最终拿到的是自己需要的内容
getArticle(3).then(res=>{})
getArticle(4).then(res=>{})
getArticle(5).then(res=>{})
getArticle(6).then(res=>{})
// 实现这个getArticle方法
这个还没写。晚一点再补。
总结:发现今天的面试题中其实除了第一道与第五道以外都可以结合起来进行学习。首先针对垃圾回收,了解到浏览器进行内存释放的方法主要为标记法。而为了使用不到的资源及时被回收,对内存泄漏进行了学习。
而弱引用与强引用的对象在回收的机制上也会有所不同,这点的不同也正是map与WeakMap的区别之一