你好,我是winter。 随着专栏进度过半,我们专栏的评论区留言量也日渐上涨。除了大家的小作业和学习心得,我还看见很多同学们在学习过程中提出了不少问题。 这其实是一种很好的学习方式,通过问题,我们可以对这部分知识记得更为牢固。 所以,我鼓励你在阅读文章之外,多思考,多提问,把自己不懂的地方暴露出来,及时查缺补漏,这样可以更好地吸收知识。同时,你也可以通过回答别人的问题来检验自己对知识的掌握情况。 我们一起来看看,大家都提出什么问题。 **1.老师你好!我语义化标签用得很少,多数用到的是header、footer、 nav等语义化标签,想问老师section和div混合使用,会不会效果不好呢?** 答:不会效果不好的,因为本来就是这么用的。遇到不确定的情况,请千万不要乱用标签,用div和span就好。 **2.我一直看见闭包这个词,但是一直也没有弄清楚它是什么东西,老师可以简单概括一下什么是闭包吗?** 答:你可以这样理解,闭包其实就是函数,还是能访问外面变量的函数。 **3.“事实上,JavaScript 中的“类”仅仅是运行时对象的一个私有属性,而 JavaScript 中是无法自定义类型的。”** - **文中说“类”是私有属性,可以具体表现是什么,不是很能理解具体含义?** 答:私有属性当然是你无法访问的属性了,但是具体表现的话,还是有的,那就是Object.prorotype.toString.call(x) 的行为。 - **无法自定义类型?请问如下编码是属于什么操作,应该怎么理解这个“类”?** ``` function Person(){} var person = new Person(); ``` 答:这个代码是定义类的操作,这里注意一下,你千万不要把类和类型的概念混淆。 **4.请教老师在对象中`name(){}` 等同于`name: function() {}` ,这两个写法有什么区别呢?** 答:这两个写法在使用上基本没什么区别。只有一点区别,就是函数的name属性不一样。可以看下这段代码: ``` var o = { myfunc(){} } console.log(o.myfunc.name) ``` 我们这里按照你的第一种方法定义了方法,然后输出它的name属性,我们看到name属性是"myfunc"。 值得一提的是,如果我们给你的第二种方法添加了名字,行为还是不一样,区别在于能否在函数内用名字递归,我们看看代码: ``` var o2 = { myfunc(){ consoe.log(myfunc); //error } } var o1 = { myfunc: function myfunc(){ consoe.log(myfunc); //function myfunc } } o1.myfunc(); o2.myfunc(); ``` 这段代码中,我们试着在用两种方式定义的方法中输出函数自身的名字变量,结果是不一样的。 不过现实中,我们几乎不会关心函数的name属性,所以不用太在意两种定义方式的区别。 **5.我对于JavaScript中Number安全整数有个疑问。** **MDN中是(-(2^53-1)~(2^53-1)), 犀牛书中是(-2^53~2^53)感觉都有道理。** **JavaScript中采用IEEE754浮点数标准进行存储, 1个符号位,11位指数位, 52位尾数位。** **按照分析,不考虑符号位,尾数位取值52个1就是表示的最大值了,不会有精度损失,此时指数位代表数值是52+1023=1075,此时即为(-(2^53-1)~(2^53-1))。** **但是2^53这个值,存储的时候尾数是52个0, 指数位为53+1023=1076,这个值也是刚好没有精度损失的,这时表示的就是(-2^53~2^53)。** **用Math.isSafeInteger()判断安全数范围和MDN中描述一样。****所以被问到这个的时候, 感觉两个都是有道理的吧!老师你说对吗?** 答:你分析得非常好,我觉得我都没啥可补充的了。这个地方JavaScript标准写得也非常模糊,我简单瞄了一下,似乎是用实验的方式来给出的安全数范围。考虑到犀牛书的时效性肯定不如MDN,应该是参考了某一版本旧引擎给出来的数据。 所以,这类行为我们还是以实测为准吧,我们不必纠结。 **6.老师您好,下面这个自己练习的例子希望您能帮解答:** ``` console.log('sync1'); setTimeout(function () { console.log('setTimeout1') }, 0); var promise = new Promise(function (resolve, reject) { setTimeout(function () { console.log('setTimeoutPromise') }, 0); console.log('promise'); resolve(); }); promise.then(() => { console.log('pro_then'); setTimeout(() => { console.log('pro_timeout'); }, 0) }) setTimeout(function () { console.log('last_setTimeout') }, 0); console.log('sync2'); ``` 答:这个例子挺经典的,虽然我觉得这样设计面试题非常不合适,但是我们可以以它为例,学习一下分析异步的方法。 首先我们看第一遍同步执行,这是第一个宏任务。 第一个宏任务中,调用了三次setTimeout(Promise中的代码也是同步执行的),调用了一次resolve,打印了三次。 所以它产生了三个宏任务,一个微任务,两次打印。 那么,首先显示的就是 sync1、promise 和 sync2。这时,setTimeout1,setTimeoutPromise,last_setTimeout在宏任务队列中,pro_then在微任务队列中。 接下来,因为微任务队列没空,第一个宏任务没有结束,继续执行微任务队列,所以pro_then,被显示出来,然后又调用了一次setTimeout,所以pro_timeout进入宏任务队列,成为第5个宏任务。 然后,没有微任务了,执行第二个宏任务,所以接下来顺次执行宏任务,显示setTimeout1,setTimeoutPromise,last_setTimeout,pro_timeout。 最终显示顺序是这样的。
  • **宏任务1**