问题1:下面这段代码,浏览器控制台上会打印什么?
答案:
使用var关键字声明的变量在JavaScript中会被提升,并在内存中开辟空间,
由于没有赋值,无法定义数值类型,所以分配默认值undefined。
var声明的变量,真正的数值初始化,是发生在你确定赋值的位置。
var声明的变量是函数作用域的,也就是我们需要区分局部变量和全局变量,
而let和const是块作用域的。所以我们这道题的运行过程是这样的:
所以答案是:undefined
问题2:如果我们使用 let 或 const 代替 var,输出是否相同
答案:
let和const声明可以让变量在其作用域上受限于它所在的块、语句或表达式中。
和var不同的地方在于,这两个声明的变量,不会被提升。并且我们会有一个称为暂时死区。
如果访问暂时死区中的变量的话,就会报ReferenceError,
因为他们的的作用域是在他们声明的位置的,不会有提升。所以必须在执行到声明的位置才能访问。
答案:ReferenceError: a is not defined
问题3:“newArray”中有哪些元素?
答案:
循环结构会给大家带来一种块级作用域的误区,在for的循环的头部使用var声明的变量,就是单个声明的变量绑定(单个存储空间)。
在循环过程中,这个var声明的i变量是会随循环变化的。
但是在循环中执行的数组push方法,最后实际上是push了i最终循环结束的3这个值。所以最后push进去的全都是3。
如果想记录每一次循环的值下来,可以使用let声明一个具有块级作用域的变量,这样为每个循环迭代创建一个新的绑定。
还有解决这个问题的另外一种解决方案就是使用闭包就好了。
答案:3,3,3
问题4:如果我们在浏览器控制台中运行’foo’函数,是否会导致堆栈溢出错误?
答案:
JavaScript的并发模式基于我们常说的”事件循环“。
浏览器是提供运行时环境来给我们执行JS代码的。浏览器的主要组成包括有调用堆栈,事件循环,任务队列和WEB API。
常用的定时器setTimeout,setInterval这些全局函数就不是JavaScript的一部分,而是WEB API给我们提供的。
JS调用栈是后进先出(LIFO)的。引擎每次从堆栈中取出一个函数,然后从上到下依次运行代码。
每当它遇到一些异步代码,如setTimeout,它就把它交给Web API(箭头1)。
每当事件被触发时,callback 都会被发送到任务队列(箭头2)。
事件循环(Event loop)不断地监视任务队列(Task Queue),并按它们排队的顺序一次处理一个回调。
每当调用堆栈(call stack)为空时,Event loop获取回调并将其放入堆栈(stack )(箭头3)中进行处理。
请记住,如果调用堆栈不是空的,则事件循环不会将任何回调推入堆栈。
- 调用 foo()会将foo函数放入调用堆栈(call stack)。
- 在处理内部代码时,JS引擎遇到setTimeout。
- 然后将foo回调函数传递给WebAPIs(箭头1)并从函数返回,调用堆栈再次为空
- 计时器被设置为0,因此foo将被发送到任务队列(箭头2)。
- 由于调用堆栈是空的,事件循环将选择foo回调并将其推入调用堆栈进行处理。
- 进程再次重复,堆栈不会溢出。
答案:堆栈不会溢出。
问题5: 如果在控制台中运行以下函数,页面(选项卡) 是否会有响应
答案:
我们是可以有多个任务列表的。由浏览器选择其中一个队列并在该队列进行处理回调。
从底层来看,JavaScript中是可以有宏认为和微任务的,比如说setTimeout回调是宏任务,而Promise回调是微任务
区别在于它们的执行方式,宏任务在单个循环周期中一次一个低堆入堆栈,但是微任务队列总是在执行后返回到事件之前清空。
所以,如果你以处理条目的速度向这个队列添加条目,那么你就永远在处理微任务。
只有当微任务队列为空时,事件循环才会重新渲染页面。
我们这段代码,每次我们去调用 foo 的时候,都会在微任务队列上加另一个 foo 的回调,
因此事件循环没办法继续去处理其他的事件了(比如说滚动,点击事件等等),直到该队列完全清空位置。因此,不会执行渲染,会被阻止。
答案:不会响应。
问题6: 我们能否以某种方式为下面的语句使用展开运算而不导致类型错误
答案:
展开语法和for-of语句去遍历iterable对象定义要遍历的数据。要使用迭代器的时候,Array和Map都是有默认迭代操作的内置迭代器的。
但是,对象是不可迭代的,这道题里,是一个对象的集合。但是可以使用iterable和iterator协议来把它变成可以迭代的。
在研究对象的时候,如果一个对象他实现了@@iterator方法,那么它就是可以迭代的。
这意味着这个对象(在他的原型链上的一个对象)必须是又@@iterator键的属性的,然后我们就可以利用这个键,通过常量Symbol.iterator获得。
答案:如上图是一种方案,可以避免TypeError异常。
问题7:运行以下代码片段时,控制台上会打印什么?
答案:
for-in循环遍历本身的可枚举属性和对象从原来的原型继承来的属性。可枚举属性是可以在for-in循环期间可以访问的属性。
答案:a、b、c
问题8:xGetter() 会打印什么值?
答案:
首先我们可以看到var x是一个全局遍历,在不是严格模式下,这个X就直接是window对象的属性了。
最重要是要理解this的对象指向问题,this始终是指向调用方法的对象的。
所以,在foo,xGetter()的情况下,this指向的是foo对象,返回的就是在foo中的属性x,值就是90。
但是在xGetter()的情况下,他是直接调用的foo的getx()方法,
但是其中this的指向是在xGetter的作用域,就是指向的window对象中,这时指向的就是全局变量x了,值也就是10。