手撕JS八股文

一、事件循环
在了解事件循环之前 先要了解什么是同步任务什么是异步任务。我们都知道js代码在执行的时候是按顺序执行,在上一行代码没执行完毕是不能去执行下一行代码的。这也就意味着js在无法同时做多件事情。而异步任务就是为了解决这一问题。

异步任务又分为宏任务和微任务。而事件循环指的就是宏任务和微任务的执行所产生的一个循环机制。

常见的宏任务有script(整体代码)、setTimeout、setInterval、setImmediate(nodejs独有)、requestAnimationFrame(浏览器独有)、IO、UIrender(浏览器独有)

常见的微任务有:process.nextTick(nodejs独有)、Promise.then()、Object.observe、MutationObserver

当一段js代码在执行的时候 会维护宏任务和微任务两个队列。遇到了宏任务或者微任务就会将他压入对应的对列中。等当前代码执行完毕后就会将微任务队列里得任务拿出来执行。如果此过程中产生了新的微任务 则又维护一个新的微任务队列,等当前微任务队列清空后再去执行新的微任务队列里得任务,当所有得微任务都执行完之后就会开始执行宏任务队列里得任务。如果此过程中产生了微任务则又会开始执行微任务,以此循环直到两个队列都被清空。这就是事件循环得全过程。ps:微任务会在dom渲染之前执行,因为js引擎线程和GUI线程互斥的原因,导致js在执行,就不会去渲染页面。
下面让我们看一道练习题来巩固一下知识

setTimeout(function () {
    console.log("setTimeout1");

    new Promise(function (resolve) {
        resolve();
    }).then(function () {
        new Promise(function (resolve) {
            resolve();
        }).then(function () {
            console.log("then4");
        });
        console.log("then2");
    });
});

new Promise(function (resolve) {
    console.log("promise1");
    resolve();
}).then(function () {
    console.log("then1");
});

setTimeout(function () {
    console.log("setTimeout2");
});

console.log(2);

new Promise(function (resolve) {
    resolve();
}).then(function () {
    console.log("then3");
});

首先代码遇到setTimeout 将他加入宏任务队列,然后执行道第一个promise 输出其中得同步代码 promise1
然后将.then加入微任务队列。
随后又遇到了setTimeout 将其加入宏任务队列
紧接着输出2
最后又是一个promise 而这个promise里没有同步代码 所以直接将then加入微任务队列。
此时已经输出了promise1,2。
而当前得宏任务队列里有两个定时器 微任务队列里有两个promise.then
现在开始执行微任务,先输出then1 紧接着输出then3.
当前微任务队列已经空了 开始执行宏任务队列。
进入第一个定时器 输出setTimeout1,紧接着又遇到了微任务,所以又开始执行微任务。
输出then2 而这个微任务里又有一个微任务 所以接着执行他 输出then4
微任务清空了 接着去执行宏任务 输出setTimeout2.

所以最终得输出顺序应该是
promise1,2,then1,then3,setTimeout1,then2,then4,setTimeout2

二、作用域,作用域链,执行上下文
作用域指的就是一个变量被定义得区域,他决定了如何查找这个变量以及变量可被访问的权限。
作用域分为全局作用域 函数作用域 块级作用域。
在es6之前js是没有块级作用域得 所以除函数作用域外var声明得变量都会提升到全局,变成全局变量。es6引进了let和const两个关键字来声明变量。从而就有了块级作用域的概念 。在一个花括号内使用let和const声明的变量只能在这个块内被访问,外部是无法访问的。const用来声明一个不可改变的常量

js采用的是静态作用域,也就是变量得作用域在他被定义得时候就已经决定了。而与之对应得动态作用域则是在哪儿被调用 作用域就在哪儿。

执行上下文
当一个函数被调用得时候,js就会进行准备工作, 这个准备工作指的就是创建执行上下文。
一个执行上下文包括 变量对象(vo) 作用域链 this
执行上下文也分为全局执行上下文和函数执行上下文。

js有一个专门维护执行上下文得栈 全局执行上下文始终在栈得最底部 每个函数被调用时 他得执行上下文就会被压入栈中 等他执行完毕后 这个执行上下文又会被弹出。

变量对象包括函数得形参 以及函数内部所声明得所有变量。当在函数中访问一个变量时 js会现在当前函数得执行上下文得变量对象中查找,如果没有找到 则会去查找父级作用域得变量对象。一直到全局上下文得变量对象为止。这个查找得过程就是作用域链。

三、js垃圾回收机制
js大概有两种垃圾回收机制
1、标记清除
js引擎会给所有的对象打上一个标记,当这个对象被引用时 就会把标记清除掉,最后再把所有还保留这标记的变量回收 释放内存。这种方法会导致有很多内存碎片。
2、引用计数
采用计数的方式记录一个变量是否可以被回收,初始赋值的时候数值+1当变量被赋值给别人的时候 数量+1 当给变量赋值的时候数量-1,如果变量的计数为0则说明没有人引用这个变量了。则可以回收此变量。直接给变量赋值null此变量就会被回收。

四、模块化相关
模块化有利于代码的提高代码复用性和可维护性,防止命名冲突以及全局污染。

js的模块化方案大致有以下几种:
1、AMD:异步加载模块,所有的模块代码都写在回调函数中,只有当模块加载完毕才会去执行回调函数里的代码,此模式适用于浏览器端
2、CMD:可异步也可同步的模块加载方案,他和AMD不同的是执行了require就会去加载模块,支持同步也支持异步,支持浏览器也支持node
3、UMD:AMD和CMD的整合
4、Commonjs:这是nodejs的模块化方案他的每个js都是一个模块,每个js中它提供了几个核心变量:exports、module.exports、require。前两个用来暴露出变量,最后一个用来引入。
-4.1、require的加载流程:首先被加载的内容分为 node核心模块,自定义模块,文件模块。 像我们通过路劲引入的则是文件模块,而直接通过包名引入的话 commonjs首先会去查找是否是node自带的模块,如果不是则去node_modules里查找这个包,如果还没有则往父级的依赖文件夹里找 直到根目录为止。
那commonjs是如何避免循环引用和重复加载的呢?
要搞清楚这个问题得直到module和Module这两个东西,上文说了每个js文件都是一个module,这个module身上不仅有导入导出的方法,还有一个loaded属性,表示这个模块是否被加载过。Module则是nodejs在运行的时候创建的一个保存所有模块值得变量,每个模块在被加载之前都会去Module身上查找是否有缓存,没有的话就会存下。

有了这两个东西之后commonjs就可以做到避免重复加载和循环引用的问题。

5、ESM:Es Module是JS的es6规范才提出的模块化方案,是一个真正属于js自己的模块化规范

他的诞生有很多优势:
借助 Es Module 的静态导入导出的优势,实现了 tree shaking。
Es Module 还可以 import() 懒加载方式实现代码分割。

Es Module 中用 export 用来导出模块,import 用来导入模块。但是 export 配合 import 会有很多种组合情况,接下来我们逐一分析一下。

ESM的导入导出都是静态的,import会自动提升到代码顶部,并且他的语法都不能写在函数作用域和块级作用域内,也不能写在条件判断内。

正因为这种静态的语法,所有他会在代码编译阶段就确定依赖关系,也是借助这个特性,可以做摇树优化。

因为import会提升到最顶部,所以esm的执行顺序是先子后父。

下面总结一下ESM和Commonjs:

Commonjs:

1、代码运行时加载
2、导出的是值得浅拷贝,值一旦输出,在模块内到处方法是无法修改已导出的值的
3、可以动态加载,对每个导出的值都有缓存,能有效解决循环引用的问题
4、加载的时候才执行

ESM

1、代码编译时加载
2、导出的值可以被模块内的方法修改,但不支持外界主动修改
3、写法灵活,可以单个导入导出混合导入导出
4、因为import会提升,所以esm的模块会提前加载并执行

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342