简答题
1. 请说出下列最终执行结果,并解释为什么?
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
答:执行结果是 10,因为执行顺序是先循环 10 次,给 a 数组中保存 10 个函数元素。当循环结束以后,全局变量 i 的值已经变为了 10,这个时候无论调用数组中的哪一个,打印结果都是 10.可以改为下面两种:
第一种:使用闭包
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function () {
console.log(i);
})(i);
}
//依次打印出来 0 1 2 3 4 5 6 7 8 9
第二种:使用 let 因为 let 的块级作用域
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); //6
2. 请说书此案列最终执行结果,并解释为什么?
var tmp = 123;
if (true) {
console.log(tmp);
let tmp;
}
答:会报错 因为 let 有块级作用域,块级作用域中的变量只能在声明以后才能使用,否则报错。这里 console.log 打印的是块级作用域中的 tmp,应该在声明之后使用。
3. 结合 ES6 语法,用最简单的方式找出数组中的最小值
var arr = [12, 34, 32, 89, 4];
console.log(Math.min(...arr)); //最简单
let minNum = (args) => Math.min(...args); //args
console.log(minNum(arr));
4. 请详细说明 var、let、const 三种声明变量的方式之间的具体差别
答:var 没有块级作用域,只有函数作用域和全局作用域;有变量提升:即可以先使用再声明。但是 let 和 const 有块级作用域,即{}。没有变量提升,需要在声明之后才可使用,否则会报错。同一个作用域内,let 和 const 不允许重复声明。const 声明常量,声明的时候就得赋值,且不可重新赋值。用 var 声明的全局变量,是顶层对象的属性;而用 let const 声明的全局变量不是顶层对象的属性。
5. 请说出下列代码最终输出结果,并解释为什么?
var a = 10;
var obj = {
a: 20,
fn() {
setTimeout(() => {
console.log(this.a);
});
},
};
obj.fn();
答:输出 20 原因:因为是 obj 调用的 fn 函数 所以 this 指向 obj this.a 即为 obj.a 并且箭头函数中的 this 指向声明时所在的作用域上下文的 this 这里是 obj.
6. 简述 Symbol 类型的用途
答:主要为对象添加独一无二的属性名,避免被覆盖。另外还可以实现对象中对私有成员的访问。
7. 说说什么是浅拷贝,什么是深拷贝?
答:浅拷贝是只拷贝一层,深层次对象级别的拷贝只拷贝引用,即:如果原对象的值发生了改变,那么浅拷贝中对应的值也会改变;深层次拷贝是每一层的数据都会拷贝出来,原对象的值改变,并不会影响拷贝的值。浅拷贝就是两个对象引用的是同一个内存空间,一个改变都改变;深拷贝就是两个对象,原对象还是用原来的存储空间。新对象自己开辟新空间,两个是互相独立的。互不影响。
//实现浅拷贝的方法一
function simpleClone(initialObj) {
let obj = {};
for (let key in initialObj) {
obj[key] = initialObj[key];
}
return obj;
}
//实现浅拷贝的方法二
Object.assign(target, ...sources);
//实现深拷贝的方法一
function deepClone(initialObj) {
return JSON.parse(JSON.stringify(obj));
}
//注:JSON不能够识别Function类型 深拷贝以后 Function会返回undefined 所以这个方法只能用于只有数据的对象
//递归方法实现深拷贝
function deeepClone(obj) {
var newObj = obj.constructor === Array ? [] : {};
//如果不是对象类型 就返回
if (typeof obj !== "object") {
return;
}
for (var i in obj) {
newObj[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i];
}
return newObj;
}
8. 请简述 TypeScript 与 JavaScript 之间的关系?
答:官方文档说:TypScript 是 JavaScript 的超集。我的理解是:第一:相比于 Python 或者 Java 等,JS 是一种弱类型的动态语言,因此只有在运行代码的时候,才会发现一些类型错误。所以有了 TS 语言,它在 JS 的基础上规范了类型注解和类型检查,要求开发者在书写时就写好类型注解,从而在编译时就能够有错误提示,降低了运行时出现的错误。第二:TS 能够具有和 babel 工具同样的作用,将 ES6+的语法降级,转成 ES5 等。
9. 请谈谈你所认为的 typescript 优缺点
答:优点:强化了 JS 的类型规范,有效规避 js 只有在运行时出现的错误,比 JS 少了类型判断的逻辑,可以将 ES6+的语法转换成 ES5 的语法,比较适合大型项目或者重构。缺点:如果是小型且紧急的项目,写类型注解比较耗时。
10. 描述引用计数的工作原理和优缺点
答:引用计数是 JS 垃圾回收的一种算法,工作原理:给变量设置引用数,判断它的引用数是否为 0,当关系改变时,修改引用数字。当引用数字为 0 时,立即进行垃圾回收。优点:当引用数字为 0 时,即发现垃圾时就立即回收。因此最大程度地减少了程序暂停。缺点:无法回收循环引用的对象且时间开销比较大。
11. 描述标记整理算法的工作流程
答:先遍历所有对象标记活动的对象,然后再一次遍历所有对象,先执行整理,移动对象的位置,将所有没有标记的对象地址连续起来,避免产生碎片化空间,然后清除这些没有标记的对象,回收相应的空间。
12.描述 V8 中新生代存储区垃圾回收的流程。
答:新生代的内存区分为两个同等大小的空间:使用空间为 from,空闲时间为 to。活动对象存储于 from 空间,采用标记整理法将活动对象复制给 to,from 和 to 交换空间完成空间释放。新生代垃圾回收过程采用复制算法 + 标记整理。
13. 描述增量标记算法在何时使用及工作原理
答:标记增量在老生代存储区中使用。先使用标记清除完成垃圾回收,再采用标记整理优化空间,并且在程序执行的间隙进行增量标记优化效率。