运行结果:
10原因:
代码中存在两个全局变量,数组a与循环变量i(变量i是var声明的,因此在全局范围内都有效)。每一次循环,新的i值都会覆盖旧值(循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i)。这就导致最后输出的是最后一轮的i的值。 而使用let声明的变量仅在块级作用域内有效,则不会出现这个问题。
运行失败:
ReferenceError: Cannot access 'tmp' before initialization原因:
代码中的 let tmp 创建过程会提升到block(块级作用域)第一行。因此 console.log(tmp) 中的 tmp 指的是下面的tmp,而不是全局的tmp。执行 console.log(tmp) 时,tmp 的初始化还没有被提升,也就是出现了暂时死区,因此不能用。解答:
console.log(Math.min(...arr))但是值得注意的是:var, let/const 都有变量提升。
console.log(foo); // Uncaught ReferenceError: Cannot access 'foo' before initialization let foo = 123;发生了引用错误,但是这是因为let定义的变量不存在提升导致的么?
作为对照,我们直接运行下面的代码,看看会出现什么错误:
console.log(foo); // Uncaught ReferenceError: foo is not defined错误原因:foo没有定义。
如果说let foo不存在变量提升,那么在运行
console.log(foo); let foo = 123;也应该报出foo没有定义这个错误的,但是错误却是因为foo没有被初始化,所以不能使用(也就是所谓的暂时死区,也正是由于存在tdz死区的限制,所以在块内使用let const声明前访问,会报错)。
其实 let 和 const 定义的变量都会被提升,但是不会被初始化,不能被引用,不会像var定义的变量那样,初始值为undefined。
为什么let和const定义的变量不会被初始化呢?主要是因为const。const,顾名思义:常量,const的引用不应被改变。如果编译器把const初始化为undefined,之后,又让它等于我们定义的那个值,就改变了const的引用。因此,委员会决定let和const虽然也会发生变量提升,但是没有任何初始值。
对于以上变量提升的问题,如果还有疑问,可以看这篇let/const 的变量提升与暂时性死区,以及这篇我用了两个月的时间才理解 let
运行结果:
20原因:
这道题与this的指向问题有关,首先,通过obj.fn()调用,fn()函数内部的this指向为obj。而setTimeout函数中的箭头函数没有this指向,它会根据外部作用域而定,而这里的this就指向obj对象,因此obj.a的结果就为20。
考虑到开发过程中的调试,Symbol允许传入一个字符串作为这个值的描述文本。它除了可以避免对象属性名重复产生的问题,我们还可以借助这种类型的特点模拟对象的私有成员。
补充 通过Symbol创建的值一定是一个唯一的值,不管传入的描述文本是否相同。使用Symbol类型提供的静态方法for()在全局去复用一个相同的Symbol值。这个方法内部维护了一个全局的注册表,为字符串和Symbol值提供了一一对应的关系。如果传入的不是字符串,方法内部会将其自动转化为字符串。除了定义自己使用的Symbol值以外,ES2015还提供了11个内置Symbol值,指向语言内部使用的方法。Object对象里面的getOwnPropertySymbols()方法可以获取到Symbol类型的属性名。1.TypeScript非常包容 TypeScript是JavaScript的超集,.js文件直接重命名为.ts即可。即使不显式的定义类型也能够自动做出类型推断。可以定义从简单到复杂的几乎一切的类型。即使TypeScript编译报错,也可以生成JavaScript文件。TypeScript是开源的。
2.更好的协作 类型安全是一种在编码期间检测错误的功能,而不是在编译项目时检测错误,这为开发团队创建了一个更高效的编码和调试过程。
3.更强的生产力 干净的 ECMAScript 6 代码,自动完成和动态输入等因素有助于提高开发人员的工作效率。增强了编辑器和IDE的功能,包括代码补全、接口提示、跳转到定义、重构等。
缺点:1.增加学习成本 语言本身多了很多概念,例如接口(Interfaces)、泛型(Generics)、类(Classer)、枚举类型(Enums)等非前端概念,学习成本增加。
2.项目初期会增加一些成本 毕竟要多写一些类型的定义,开发成本的增加是不可避免的。不过对于一些需要长期维护的项目,TypeScript能够减少其维护成本。
3.可能和一些库结合的不是很完美 在实际开发中需要使用到第三方npm模块,而这些模块不一定是通过TypeScript编写的,所以它提供的成员就不会有强类型体验。但是目前越来越多的模块已经在内部集成类型声明文件了。
引用计数算法的工作原理
核心思想:设置引用数,判断当前引用数是否为0引用计数器引用关系改变时修改引用数字引用数字为0时立即回收引用计数算法的优缺点
引用计数算法优点 发现垃圾时立即回收最大限度减少程序暂停 引用计数算法缺点 无法回收循环引用的对象时间开销大(资源消耗较大)标记整理算法的工作流程
核心思想:分标记和清除两个阶段完成遍历所有对象找标记活动对象(可达对象)清除阶段会先执行整理,移动对象位置遍历所有对象清除没有标记对象回收相应的空间到空闲列表(连续)新生代对象回收实现
回收过程采用复制算法+标记整理新生代内存区分为两个等大小空间使用空间From,空闲空间To活动对象存储于From空间标记整理后将活动对象拷贝至ToFrom与To交换空间完成释放回收细节说明
拷贝过程中可能会出现晋升晋升就是将新生代对象移动至老生代一轮 GC 还存活的新生代需要晋升To 空间的使用率超过25%需要晋升增量标记算法在何时使用
由于 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行,称全停顿(Stop-The-World)在 V8 新生代的垃圾回收中,因其空间较小,且存活对象较少,所以全停顿的影响不大在 V8 老年代的垃圾回收中,为了降低老生代的垃圾回收而造成的卡顿,使用增量标记算法增量标记算法的工作原理
V8 将标记过程分为一个个的子标记过程让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成这些小的垃圾回收任务执行时间比较短,可以最大限度减少程序暂停时间标记完后执行清除,程序继续执行