var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i)
}
}
a[6]()
- 最终执行结果是 10
- 变量
i是由var声明的,其所在的作用域是全局作用域,所以循环结束之后,i的值为 10 - 后续通过执行全局下生成的函数
a[6],由于当前函数执行时创建的私有执行上下文没有变量i,其向作用域链中的上一层作用域即全局作用域寻找i, 所以获得的值 为 10
var tmp = 123;
if (true) {
console.log(tmp);
let tmp;
}
- 最终执行结果为
Uncaught ReferenceError: Cannot access 'tmp' before initialization at <anonymous> - 由于在if 语句的块作用域中 存在使用
let声明的tmp, 此时,在这个块级作用域中存在暂存性死区,所以当在 变量tmp声明定义之前访问 变量tmp时,尽管全局作用域中存在tmp这个变量,其也是访问不到全局作用域的tmp, 而是报错 - 可以认为在if 语句块中,
tmp已经在词法环境中被创建了(存在提升,但不同于var提升的同时被初始化为undefined),但是还没有到达它的初始化(这是语句本身的一部分)这个区间就是暂存性死区
结合ES6语法,用最简单的方式找出数组中的最小值
var arr = [12, 34, 32, 89, 4]
let min = arr.sort((a, b) => a - b)[0]
请详细说明var、let、const三种声明变量的方式之间的具体差别
var声明提升,初始化为undefinedlet仅声明提升,未初始化const仅声明提升,未初始化
var作用于全局作用域或者函数作用域let作用于块级作用域const必须在声明时初始化
var可以仅声明不初始化let可以仅声明不初始化const作用于块级作用域
var可以重复定义let不可以重复定义const不可以重复定义
var可以多次赋值let可以多次赋值const基本数据类型不可以重新赋值,引用数据类型仅可以改变值
var可以在声明前访问let不可以在声明前访问const不可以在声明前访问
请说出下列代码最终输出结果,并解释为什么?
var a = 10;
var obj = {
a: 20,
fn() {
setTimeout(() => {
console.log(this.a)
})
}
}
obj.fn()
- 最终输出 20
obj调用fn函数时,fn中的this指向obj,定时器为箭头函数,this指向最近的函数this指向,所以this也指向obj,所以输出 20
简述Symbol类型的用途
- Symbol 最主要的作用就是为对象添加独一无二的属性名
- 定义私有属性,外部无法进行访问,只能通过类中的方法进行访问。
说说什么是浅拷贝,什么是深拷贝?
- 浅拷贝:只拷贝第一层的原始类型值,和第一层的引用类型地址。
- 深拷贝:拷贝所有的属性值,以及属性地址指向的值的内存空间,拷贝的对象和被拷贝对象互不影响
请简述TypeScript与JavaScript之间的关系?
TypeScript是JavaScript的超集JavaScript+ES6+类型系统=TypeScript(最终编译成JavaScript)- 任何一种
JavaScript运行环境都支持使用TypeScript
请谈谈你所认为的typescript优缺点
优点
- 增强代码的可读性和可维护性
- 在编译时即可发现大部分的错误,增强编辑器的功能
- 包容性,js文件可以直接改成 ts 文件,不定义类型可以使用
TypeScript隐式类型推断,可以定义几乎一切类型,ts 编译报错时也可以生成 js 文件,兼容第三方库,即使不是用ts编写的 - 有活跃的社区,大多数的第三方库都可提供给 ts 的类型定义文件,完全支持
es6 规范
缺点
- 短期增加开发成本,增加类型定义,但减少维护成本
- ts 集成到构建流程需要一定的工作量
- 和有些库结合时不是很完美(需要自己手动输出类型定义文件)
描述引用计数的工作原理和优缺点
引用计数
引用计数的核心思想就是设置引用次数,判断当前引用次数是否为0,当引用数字为 0 立即回收
优点
- 发现垃圾立即回收
- 最大限度减少程序的暂停
缺点
- 无法回收循环引用的对象
- 时间开销大(监控对象的修改耗时)
- 需要维护表来存储引用数,如果引用数过多,则会带来一定的损耗
描述标记整理算法的工作流程
- 标记整理算法可以看作标记清除的增强,分为3个阶段:标记、清除、整理
- 标记阶段,
collector从mutator根对象开始进行遍历,从根上可以访问到的对象都打上一个标识,将其记录为可达对象 - 清除阶段,
collector对堆内存从头到尾进行线性的遍历,如果没有标记为可达对象,则将对象的地址进行移动,使其在地址上连续,然后回收,避免空间碎片化
描述V8中新生代存储区垃圾回收的流程
- 新生代内存区分为两个等大小空间
- 使用空间为
From, 空闲空间为To - 活动对象存储于
From空间中 - 标记整理后将活动对象拷贝至
To - 最后
From与To交换空间完成释放
描述增量标记算法在何时使用及工作原理
- 程序执行的过程中,不进行垃圾回收
- 当GC要工作的时候,程序会停下来,首先遍历对象进行标记
- 但是这个标记和标记清除的标记有所不同,增量标记算法中的标记的过程并不是连贯的
- 它会将标记的过程拆分为多个小的过程分开进行标记
- 这样子就不会因为一次性标记太多可达对象造成js 执行卡顿
- 虽然这样子程序执行和垃圾回收存在多次交替工作,但是 V8引擎 达到最大垃圾存储的约1.5G时,采用非增量标记的垃圾回收形式,时间也没有超过一秒钟
- 所以这个过程中的交替间断时间是合理的,而且将原本需要停止很长的一段时间,拆分成很小的时间块,这样子对用户更加友好