1.双等和三等的区别
双等会先进行转换再比较 ( == );
三等仅比较而不会转换 ( === );
双等在进行类型转换时,遵循以下三个原则:
(1) 如果有一个操作数是布尔值类型,会先将布尔值转换成对应的数值类型0和1
(2) 如果一个操作数是数值,一个操作数是字符串,会先将字符串转化成数值,再进行两者的比较
(3) 如果一个操作数是对象,另外一个操作数不是,那么会先调用对象的valueOf()方法,用最终得到的基本类型再按照前面的规则进行比较
看下面的例子,如果一个自定义对象重写了valueOf()方法
使用new String()创建新的字符串
let num1 = '123';
typeof num1; //string
let num2 = new String('123');
typeof num2; // object
当使用双等和三等进行比较时的不用结果,以及双等实际发生了什么,如下:
(使用new String()创建出来的字符串其实是一个对象类型的字符串,在进行双等比较时,会隐式的调用Object.prototype.valueOf()方法,转化成string类型,再进行比较)
2. async和defer的区别
都是为了防止js文件的加载影响页面的解析和渲染的,使js文件可以被并行下载,动态下载js脚本的时候,不会阻塞UI线程的执行;动态创建的< script >标签隐含 async 属性;如果 script 无 src 属性,则 defer, async 会被忽略
正常情况下,我们创建的script标签是用于加载脚本和执行脚本的,html会按照代码的顺序,从上到下依次执行,如果将script标签放在页面的顶层部分,就会阻塞后面DOM的解析和渲染,如果script标签的加载遇到问题,最终的结果就是造成页面白屏。
解决这个问题,我们有两个大方向: 一是将script标签放在页面的底部,二是使用async或defer使script标签异步加载。当我们采用第二种方式的时候,就需要了解这两个的作用区别了
defer
defer是异步加载js脚本,且不会影响后续DOM的加载渲染,如果有多个设置了defer的script标签存在,几个脚步会同时异步加载,但是最后只会按照顺序执行所有的script;defer脚本会在文档渲染完毕后,DOMContentLoaded事件调用前执行。优点是完全不会阻塞页面的加载,并且注重script标签的加载顺序,尤其当脚本之间的是依赖关系时,优先使用defer
async
async同样是异步加载js脚本,几个脚步会同时异步加载,但是脚本的执行并不是按准代码的顺序,而是谁先加载完就立即执行谁,立即执行的时候可能会阻塞页面的加载,DOMContentLoaded事件的触发并不受async脚本加载的影响,可能在它之前,也可能在它之后。
在不支持async属性的浏览器里,可以通过动态创建script元素并插入文档中,实现脚本的异步载入和执行,requirejs就是使用这个方法实现的。
3.宏任务和微任务
在此之前我们需要知道什么是 事件循环(Event Loop)机制:
- 1.执行一个宏任务(栈中没有就从事件队列中获取);
- 2.执行过程中如果遇到微任务,就将它添加到微任务的任务队列中;
- 3.宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行,会执行至微任务队列为空;
- 4.如果宿主为浏览器,开始检查渲染,然后GUI线程接管渲染;
- 5.渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取),比如执行宏任务中的异步代码(setTimeout等回调);
微任务会先于页面渲染执行;
宏任务Task是由宿主(一般为Node或者浏览器)发起的,微任务Jobs是由js引擎发起的;
在浏览器中有两个线程,js引擎线程和渲染线程,两个线程互斥;但是Node只有js引擎线程;
注意⚠️两点:
(1)promise构造函数是同步执行的,then方法才是异步执行的;
(2)微任务(入队的promise回调)的优先级高于下一轮的宏任务(入队的setTimeout()回调);
总结:在执行宏任务之前,要先把已入队的 微任务 全部执行完,再执行下一条 宏任务。