
你好，我是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(() =&gt; {
    console.log('pro_then');
    setTimeout(() =&gt; {
        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。

最终显示顺序是这样的。

<li>**宏任务1**
<ul>
<li>微任务1
<ul>
- sync 1
- promise
- sync 2

- pro_then

- setTimeout1

- setTimeoutPromise

- last_setTimeout

- pro_timeout

**7.为什么 promise.then中的settimeout是最后打印的？不用管是宏任务依次执行吗？**

答：因为then是第一个宏任务中最后执行的微任务，所以它发起的宏任务是最后入队的，依次执行就是最后。

**8.怎么确定这个微任务属于一个宏任务呢，JavaScript主线程跑下来，遇到setTImeout会放到异步队列宏任务中，那下面的遇到的promise怎么判断出它是属于这个宏任务呢？**

答：resolve在哪个宏任务中调用，对应的then里的微任务就属于哪个宏任务。宏任务没有从异步队列中取出，中间所碰到的所有微任务都属于这个宏任务。

**9.为什么要设计微任务（micro task），我知道这样JavaScript引擎可以自主地执行任务，但这样的好处是什么？提高性能吗？**

答：不是，微任务是JavaScript引擎内部的一种机制，如果不设计微任务，那么JavaScript引擎中就完全没有异步了呀，所以必须要设计微任务。

**10.现在浏览器多数实现是从右往左匹配的，那么无法保证选择器在 DOM 树构建到当前节点时，已经可以准确判断当前节点是否被选中。现在浏览器又是怎么实现在生成DOM树，同时进行CSS属性计算？**

答：其实现代浏览器已经为`:empty`、`:last`等伪元素写了很多例外了，不过你说的从右往左匹配，左边的要么是当前节点的父元素，要么是前置元素，所以是可以保证准确判断的呀。

**11.请问老师，页面资源的预加载是不是可以用link标签实现，还有其他的方式吗？**

答：预加载的方法就多啦，还可以用JavaScript代码预加载，甚至用本地存储缓存。

**12.老师，我有一个疑问：“词法环境”和“词法作用域”这两个概念的区别是什么？希望你能帮我解惑。**

答：词法环境是运行时概念，词法作用域是语言概念，就是说，作用域指的是变量生效的那段代码，而词法环境是指运行起来之后，你这段代码访问的存储变量的内存块。

**13.想问一个问题：import 进来的引用为什么可以获取到最新的值，是类似于 getter 的机制吗?**

答：这个地方略微有些复杂，我们在运行时并没有讲import的运行时机制，这里涉及了一个叫做ImportEntry Record的机制，它比getter的实现更底层。

我想这个地方我们没有必要去深究模块的运行时机制，它很复杂而且并不是经常要用到。你如果想了解的话，可以查阅一下。

**14.请问老师，JavaScript 的call stack size是多少，这个size的单位是啥，是调用栈中函数的个数，还是一个存储单位，比如MB之类的。如果调用栈中就一个函数，这个函数的参数有100万个，浏览器端依然会溢出，看起来是存储单位，但是没得到验证。**

答：这个似乎并没有什么特别规定，我知道JSC里面这个东西是可以用C++代码来调整的，至于浏览器调用JavaScript引擎的时候会怎么做，还真不好说。

不过，从编码风格上建议，不要把这种事情用函数解决啦，真要干这样的事，数组可能都不合适了，请老老实实写ArrayBuffer吧。

**15.老师您好，我一直有一个困惑，浏览器的鼠标事件是怎么识别到的，是碰撞检测的吗？**

答：这个问题很不错，我后面在浏览器API的事件部分会详细讲，可以先简单说一下，这里的检测方式是从外到内，逐级分配给子元素，所以我们的事件会有捕获过程。

**16.有个问题，如果我javaScript代码改变了DOM树元素的位置，需要启动重新排版（位置改变的元素只会影响其他部分元素的位置，甚至不影响其他元素的位置。），这时会导致这棵DOM树的所有元素都需要重新排版、绘制和渲染吗？**

答：排版应该是会重新排的，但是如果有些元素的尺寸没有改变，那么它内部不需要重排，当然也就更不需要重新渲染了，但是绘制应该是要重绘的，目前来看，浏览器还没有那么智能。

**17.老师，我是12年左右踏进半只脚到前端领域的，后来考研就放弃了，觉得前端不够高深，和传统工程师来说觉得门槛低很多，甚至前期我都觉得自己不是个程序员。**

**直到研究生毕业，才又选择前端，这是三大框架风靡，我却有点迷惘，感觉和自己认知的前端不一样，直到现在工作了差不多两年，才悟出了点道道。**

**作为工程师，我始终觉得前端也应该熟练算法和数据结构、数据库这些所谓的后端知识，但是平时工作场景中用到又少，不知如何学习？**

答：算法和数据结构可不是什么后端知识呀，是所有程序员的基本技能。

算法主要是靠大量练习提高，数据结构可以一个一个学习，不要指望工作中用到恰巧就学了，毕竟学习要教学费而工作是领工资的，哪里会有这样的好事呢，所以还是自己多多练习呀。

**18.重学前端是夯实前端基础，那前端进阶方向在哪里?还是一定要修一门后端语言扩展服务端，希望老师可以指点迷津。**

答：我觉得任何编程相关岗位的进阶方式都是做出某某东西，而不是学会某某东西。我会在专栏课程的第四模块会讲到一些进阶可能的方向，你可以关注一下。

**19.我主业是后端，工作中也会带着做前端，自认还是能完美还原设计师的设计。但是现在感觉很多时候提前端就是vue等，而我还是在用jQuery，想请老师说说看，我是不是落伍了？**

答：落伍的问题不是你用什么框架，而是你在做什么东西，学什么东西。

框架不是赶时髦，追潮流，每个框架都有解决的问题，我觉得你该焦虑的不是你用的框架为什么这么老，而是你该知道这些新框架要解决什么问题，以及这些问题为什么在你的工作中不存在。

**最后，我们来看看我在JavaScript类型那一篇中给你留的实践问题。**

**如果我们不用原生的Number和parselnt，用JavaScript代码实践String到Number，该怎么做呢？**

答：其实这个问题我在后台没看到特别满意的答案，好像大家都很喜欢偷懒啊。

我这里给你留个例子，处理十进制整数。

```
function atoi(a){
    let chars = a.split(&quot;&quot;).map(e =&gt; e.charCodeAt(0) - &quot;0&quot;.charCodeAt(0));
    let n = 0;
    for(var char of chars) {
        n *= 10;
    	n += char;
    }
    return n;
}
atoi(&quot;1001&quot;)

```

我比较期待大家有人能写出来带小数，甚至带科学计数法的代码，你可以尝试一下。

好了，今天的答疑环节就进行到这里，你也可以把自己想要解答的问题留言。


