简答题
一、请说出下列最终的执行结果,并解释为什么。
var a = [];
for (var i = 0; i < 10; i ++) {
a[i] = function () {
console.log(i)
}
}
a[6]()
最终的执行结果是10,var生命的 i 变量存在变量提升,i 变量会提升至全局执行上下文中,当循环结束时,i的值为10,调用 a[ 6 ] () 函数后,打印出的是全局变量 i 。
二、请说出下列最终的执行结果,并解释为什么
var tpm = 123;
if (true) {
console.log(tmp)
let tmp
}
会报 tmp undefined 错误
let 声明的变量不存在变量提升,在 if 作用域块内 log语句输出的tmp并未声明。虽然if语句块外面有声明tmp,但语句块内部的声明优先级较高。
三、结合ES6新语法,用最简单的方式找出数组中的最小值
var arr = [12, 34, 32, 89, 4]
实现
var arr = [12, 34, 32, 89, 4]
var min = Math.min(...[arr])
console.log(min)
四、请详细说明 var, let, const 三种声明变量的方式之间的具体差别。
var 声明变量存在变量提升、重复声明、变量污染的问题。
let、const声明引入块级作用域的概念,解决变量提升、重复声明,变量污染的问题。
let 声明会有临时性死区问题的存在。
let 声明之后不允许再次声明,可以更改。
const 声明之后不允许更改,一般用来声明常量。这个不允许更改不是绝对的,当使用const 声明的是引用类型(数组、函数、对象)时,是可以更改对象属性的,但是不允许更改对象引用。这是因为对象存在堆中,栈中保存的是对象的引用,因此只要引用不变,对象的属性和方式是可以更改的。
五、请说出下列代码最终输出的结果,并解释为什么。
var a = 10;
var obj = {
a: 20,
fn () {
setTimeout(() => {
console.log(this.a)
})
}
}
obj.fn()
输出20
首先通过obj调用fn函数,在fn函数内部this的指向是obj。
而setTimeout中的箭头函数是没有this的指向的,里面的this依赖于上层作用域中的this,即obj.
所以这里的this.a === obj.a
六、简述 symbol 类型的用途
用来声明变量属性的唯一性
七、说说什么是浅拷贝,什么是深拷贝
- 浅拷贝:复制对象的引用,不复制对象本身。当其中一个对象属性更改时,另一个也会改变
- 浅拷贝实现
- 引用赋值
- 深拷贝:复制对象本身。当其中一个对象属性更改时,另一个不会改变。
- 深拷贝实现
- onject.assgin()
- JSON.parse(JSON.stringify())
- 自定义 递归实现
八、请简述TypeScript与JavaScript之间的关系
JS是弱类型语言,变量的声明可以随意更改。TS是JS的超集,是对JS的增强。
九、请谈谈你所认为的TypeScript优缺点
- 优点
- 增加代码可维护性
- 兼容JS语法
- 减少不必要的类型判断
- 缺点
- 增加学习成本
- 增加开发成本
- 与一些库可能不兼容
十、描述引用计数的工作原理和优缺点
当一个内存空间被一个变量使用时,它自身的引用计数器就会加一,当引用它的变量销毁时,它自身的引用计数器就会减一。引用计数器为0时,这块区域就会被当作垃圾回收掉。
- 优点:
- 发现垃圾时立即回收
- 最大限度减少程序暂停
- 缺点
- 空间的浪费(增加引用计数的变量)
- 时间开销大
- 无法回收循环引用的对象
十一、描述标记整理算法的工作流程
- 遍历所有对象标记活动对象
- 遍历所有对象,移动活动对象的位置,然后清除,没有非活动对象
- 回收相应的空间
十二、描述V8中新生代存储区垃圾回收的流程
- 回收过程采用复制算法 + 标记整理算法
- 新生代内存区分为两个等大小的空间
- 使用空间为From,空闲空间为To
- 标记整理后将活动对象拷贝至To
- From与To交换空间完成释放
回收细节说明
- 拷贝过程可能出现晋升
- 晋升就是将新生代对象移动至老生代
- 一轮GC还存活的新生代需要晋升
- To空间的使用率超过25%(避免后续存储数据时,空间不足的问题)
十三、描述增量标记算法在何时使用及工作原理
将一整段垃圾回收操作分成几个小段
垃圾回收与程序执行交替完成
程序执行一段时间后,产生垃圾对象,开始遍历对象进行标记,第一次标记可能就标记第一层活动对象,第二次标记所有活动对象,最后对空间进行回收。