最后

你要问前端开发难不难,我就得说计算机领域里常说的一句话,这句话就是『难的不会,会的不难』,对于不熟悉某领域技术的人来说,因为不了解所以产生神秘感,神秘感就会让人感觉很难,也就是『难的不会』;当学会这项技术之后,知道什么什么技术能做到什么做不到,只是做起来花多少时间的问题而已,没啥难的,所以就是『会的不难』。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

我特地针对初学者整理一套前端学习资料

前端路线图

vue.js的36个技巧

浏览器的解析器通常把工作分给两个组件——分词程序负责把输入切分成合法符号序列,解析程序负责按照句法规则分析文档结构和构建句法树。词法分析器知道如何过滤像空格,换行之类的无关字符。

解析器输出的树是由DOM元素和属性节点组成的。

DOM与标签几乎有着一一对应的关系,如下面的标签

Hello 枫

会被转换成如的DOM树:

2.2 解析CSS

CSS选择器的读取顺序是从右向左。

#molly div.haha span{color:#f00}

如上面的代码,浏览器会按照从右向左的顺序去读取选择器。

先找到span然后顺着往上找到class为“haha”的div再找到id为“molly”的元素。

成功匹配到则加入结果集,如果直到根元素html都没有匹配,则不再遍历这条路径,从下一个span开始重复这个过程。

整个过程会形成一条符合规则的索引树,树由上至下的节点是规则中从右向左的一个个选择符匹配的节点。

如果从左向右的顺序读取,在执行到左边的分支后发现没有相对应标签匹配,则会回溯到上一个节点再继续遍历,直到找到或者没有相匹配的标签才结束。

如果有100个甚至1000个分支的时候会消耗很多性能。反之从右向左查找极大的缩小的查找范围从而提高了性能。

这就解释了为什么id选择器大于类选择器,类选择器大于元素选择器。

2.3 解析JS

在浏览器中有一个js解析器的工具,专门用来解析我们的js代码。

当浏览器遇到js代码时,立马召唤“js解析器”出来工作。

解析器会找到js当中的所有变量、函数、参数等等,并且把变量赋值为未定义(undefined)。

把函数取出来成为一个函数块,然后存放到仓库当中。这件事情做完了之后才开始逐行解析代码(由上向下,由左向右),然后再去和仓库进行匹配。

再看一下这段代码

在js预解析的时候,在遇到变量和函数重名的时候,只会保留函数块。在逐行解析代码的时候表达式(+、-、*、/、%、++、–、 参数 ……)会改变仓库里对应的值。

我们来了解一个词“作用域”,现在把这个词拆分一下。

作用:读、写操作

域:空间、范围、区域…

连起来就是能够进行读写操作的一个区域。

“域”:函数、json、……都是作为一块作用域。

全局变量、局部变量、全局函数

一段 也是一块域。在域解析的时候,也是由上向下开始解析。这就解释了为什么引用的外部公共js文件(比如:jquery)应该放到自定义js上边的原因。

再来看一下这段代码

继续跟踪一下解析器的解析过程:首先函数fn()外部的a是一个全局变量,fn()里面的a是一个局部变量。fn()函数同时是一个作用域,只要是作用域,就得做预解析和逐行解析的步骤。所以第一个alert打印的是fn()作用域的仓库指向的变量a,即为undefined。第二个alert打印的是全局的变量a,即为1。

接下来继续看代码,基本雷同的代码,我改变其中一小个地方。

看到这里当解析到fn()的时候,发现里面并没有任何变量,所以也就不往仓库里面存什么,此时的仓库里面是空的,啥也没有。但是这个时候解析并没有结束,而是从函数里面向外开始找,找到全局的变量a。此时打印的正式全局变量a的值。

这里就涉及到一个作用域链的问题。整个解析过程像是一条链子一样。由上向下,由里到外。局部能够读写全局,全局无法读写局部。

来,继续看代码,基本雷同的代码,我再次改变其中一小个地方。

千万不能忘了,在预解析的时候浏览器除了要找变量和函数之外还需要找一些参数,并且赋值为未定义。所以这里的fn(a)相当于fn(var a),这个时候的逻辑就和第一段实例代码一样了。

继续搞事情,继续看代码,基本雷同的代码,我再次改变其中一小个地方。

当代码执行到fn(a);的时候调用的fn()函数并且把全局变量a作为参数传递进去。

此时打印的自然是1,要记住function fn(a)相当于function fn(var a),所以这时候a=2;改变的是局部变量a,并没有影响到全局变量a,所以第二次打印的依然是1。

3. 浏览器的垃圾回收机制


由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

JavaScript的解释器可以检测到何时程序不再使用一个对象了,当他确定了一个对象是无用的时候,他就知道不再需要这个对象,可以把它所占用的内存释放掉了。

var a = “before”;

var b = “override a”;

var a = b; //重写a

这段代码运行之后,“before”这个字符串失去了引用(之前是被a引用),系统检测到这个事实之后,就会释放该字符串的存储空间以便这些空间可以被再利用。

浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。

3.1 标记清除

这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。

垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

当对象,无法从根对象沿着引用遍历到,即不可达(unreachable),进行清除。对于上面的例子,fn() 里面的 a 和 b 在函数执行完毕后,就不能通过外面的上下文进行访问了,所以就可以清除了。

这是当前主流的GC算法,V8里面就是用这种。

不管是高级语言,还是低级语言。内存的管理都是:分配内存使用内存(读或写)释放内存前两步,大家都没有太大异议。关键是释放内存这一步,各种语言都有自己的垃圾回收(garbage collection, 简称GC)机制。

在大部分的应用场景:一个新创建的对象,生命周期通常很短。所以,V8里面,GC处理分为两大类:新生代和老生代。

新生代的堆空间为1M~8M,而且被平分成两份(to-space和from-space),通常一个新创建的对象,内存被分配在新生代。当to-space满的时候,to-space和form-space交换位置(此时,to空,from满),并执行GC。如果一个对象被断定为,未被引用,就清除;有被引用,逃逸次数+1(如果此时逃逸次数为2,就移入老生代,否则移入to-space)。

老生代的堆空间大,GC不适合像新生代那样,用平分成两个space这种空间换时间的方式。老生代的垃圾回收,分两个阶段:标记、清理(有Sweeping和Compacting这两种方式)。

标记,采用3色标记:黑、白、灰。步骤如下:

GC开始,所以对象标记为白色。

根对象标记为黑色,并开始遍历其子节点(引用的对象)。

当前被遍历的节点,标记为灰色,被放入一个叫 marking bitmap 的栈。在栈中,把当前被遍历的节点,标记为黑色,并出栈,同时,把它的子节点(如果有的话)标记为灰色,并压入栈。(大对象比较特殊,这里不展开)

当所有对象被遍历完后,就只剩下黑和白。通过Sweeping或Compacting的方式,清理掉白色,完成GC。

3.2 引用计次

引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

但是用这种方法存在着一个问题,下面来看看代码:

function problem() {

var objA = new Object();

var objB = new Object();

objA.someOtherObject = objB;

objB.anotherObject = objA;

}

在这个例子中,objA和objB通过各自的属性相互引用;也就是说这两个对象的引用次数都是2。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。

大多数浏览器已经放弃了这种回收方式。

4. 浏览器的本地存储


如果我问你,浏览器中的缓存有哪些,我相信绝大部分人会说有三种:cookie,sessionStorage,localStorage。

但是诶,我不知为什么大家都叫这三个为缓存,他们叫缓存,我们上面提到的Memory Cache等cache也叫缓存,不是很乱吗,而且浏览器把他们归到了storage里面,storage翻译过来为存储。

还有一点,这里有五种:Cookies、Local Storage、Session Storage、WebSQL 和 IndexedDB。

4.1 cookie

Cookies 是最早的本地存储,是浏览器提供的功能,并且对服务器和 JS 开放,这意味着我们可以通过服务器端和客户端保存 Cookies。不过可以存储的数据总量大小只有 4KB,如果超过了这个限制就会忽略,没法进行保存。

HTTP协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份。Cookie实际上是一小段的文本信息(key-value格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器检查该Cookie,以此来辨认用户状态。

4.2 Local Storage Session Storage

Local Storage 与 Session Storage 都属于 Web Storage。Web Storage 和 Cookies 类似,区别在于它有更大容量的存储。其中 Local Storage 是持久化的本地存储,除非我们主动删除数据,否则会一直存储在本地。Session Storage 只存在于 Session 会话中,也就是说只有在同一个 Session 的页面才能使用,当 Session 会话结束后,数据也会自动释放掉。

4.3 cookie Local Storage Session Storage比较

一般在我们面试的时候,面试官都会问cookie Local Storage Session Storage之间有什么区别。

特性

Cookie

Local Storage

Session Storage

数据的生命期

可设置失效时间,默认是关闭浏览器后失效

除非被显式清除,否则永久保存

会话级存储,仅在当前会话下有效,会话结束,关闭页面或浏览器后被清除

存放数据大小

4KB左右

5MB~10MB(浏览器不同,情况不同)

5MB~10MB(浏览器不同,情况不同)

与服务器端通信

每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能和安全问题

仅在客户端(即浏览器)中保存,不参与和服务器的通信

仅在客户端(即浏览器)中保存,不参与和服务器的通信

易用性

源生的Cookie接口不友好,开发者需要根据需求封装

源生接口良好,亦可再次封装来对Object和Array有更好的支持

源生接口良好,亦可再次封装来对Object和Array有更好的支持

应用场景

用户登录时,保存服务器返回的一段加密过的唯一辨识单一用户的code,用以判断当前用户登录状态,或者之前电商网站用来保存用户的购物车信息。

Local Storage可以替代Cookie完成用户购物车信息的前端保存功能,同时可以当作HTML5游戏的本地数据的存储空间。

当页面存在多表单的情况下,可以利用Session Storage实现表单页拆分,优化用户体验。

注意

不要将系统敏感的数据保存到Cookie,Local Storage,Session Storage中,防止XSS注入的风险。因为XSS注入可以通过控制台对你的属性值进行修改,具体可以参考我写的另一篇博客,前端黑客技术。

4.4 WebSQL

WebSQL 与 IndexedDB 都是最新的 HTML5 本地缓存技术,相比于 Local Storage 和 Session Storage 来说,存储功能更强大,支持的数据类型也更多,比如图片、视频等。

WebSQL 更准确的说是 WebSQL DB API,它是一种操作本地数据库的网页 API 接口,通过 API 可以完成客户端数据库的操作。当我们使用 WebSQL 的时候,可以方便地用 SQL 来对数据进行增删改查。而这些浏览器客户端,比如 Chrome 和 Safari 会用 SQLite 实现本地存储,微信就采用了 SQLite 作为本地聊天记录的存储。

4.5 IndexedDB

IndexedDB就是浏览器提供的本地数据库,它可以被网页脚本创建和操作。它可以存储大量数据,提供了查找接口,能够建立索引。但是不属于关系型数据库(不支持SQL查询语句,更类似于NoSQL数据库)。

IndexedDB的特点:

  1. 键值对存储:IndexedDB内部采用对象仓库(object store)存放数据。所有类型的数据都可以直接存入,包括JavaScript对象。对象仓库中,数据以“键值对”的形式保存。每一个数据记录都有对应的主键,主键是唯一的,如果重复会抛出一个错误。

  2. 异步:IndexedDB操作不会锁死浏览器,用户依然可以进行其他操作,与Local Storage的同步操作不同,异步的设计是为了大量数据的读写,拖慢页面的表现,降低用户体验。

  3. 支持事务:IndexedDB支持事务(transaction),这意味着一些列操作步骤中,只要有某个步骤出现异常,则整个事务就会消失,数据库回滚到事务发生之前的状态,不存在只写一部分数据的情况。

  4. 同源限制:IndexedDB受到同源限制,每一个数据库对应创建它的域名。网页只能访问自身域名下的数据库,而不能够访问跨域的IndexedDB数据库。

  5. 存储空间大:IndexedDB的存储空间一般不少于250MB,或者更大。

  6. 支持二进制存储:IndexedDB不仅可以储存字符串,还可以储存二进制数据(ArrayBuffer对象和Blob对象)。

事务是数据库的概念,事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

5. 浏览器的线程


我们平常使用JavaScript的时候都知道js是单线程的,而浏览器,则是多线程的。

5.1 CPU

CPU是计算机的核心,其负责承担计算机的计算任务。这里我们比喻为一个工厂。

5.2 进程

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

我们这里将进程比喻为工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。

5.3 线程

线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元。

这里把线程比喻一个车间的工人,即一个车间可以允许由多个工人协同完成一个任务。

5.4 浏览器的多线程

浏览器内核是多线程,在内核控制下各线程相互配合以保持同步,一个浏览器通常由以下常驻线程组成:

  • GUI 渲染线程

  • JavaScript引擎线程

  • 事件触发线程

  • 定时触发器线程

  • 异步http请求线程

5.4.1 GUI渲染线程

GUI渲染线程负责渲染浏览器界面HTML元素,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。

当界面需要重绘(Repaint)或由于某种操作引发回流(重排)(reflow)时,该线程就会执行。

在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被”冻结”了,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

5.4.2 JavaScript引擎线程

JavaScript引擎,也可以称为JS内核,主要负责处理Javascript脚本程序,例如V8引擎。

JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序(单线程)。

注意:GUI渲染线程和JavaScript引擎线程互斥。

由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JavaScript线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

计算机网络

  • HTTP 缓存

  • 你知道 302 状态码是什么嘛?你平时浏览网页的过程中遇到过哪些 302 的场景?

  • HTTP 常用的请求方式,区别和用途?

  • HTTPS 是什么?具体流程

  • 三次握手和四次挥手

  • 你对 TCP 滑动窗口有了解嘛?

  • WebSocket与Ajax的区别

  • 了解 WebSocket 嘛?

  • HTTP 如何实现长连接?在什么时候会超时?

  • TCP 如何保证有效传输及拥塞控制原理。

  • TCP 协议怎么保证可靠的,UDP 为什么不可靠?

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

算法

  • 链表

  • 字符串

  • 数组问题

  • 二叉树

  • 排序算法

  • 二分查找

  • 动态规划

  • BFS

  • DFS

  • 回溯算法

JS线程在运行JS程序(单线程)。

注意:GUI渲染线程和JavaScript引擎线程互斥。

由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JavaScript线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

计算机网络

  • HTTP 缓存

  • 你知道 302 状态码是什么嘛?你平时浏览网页的过程中遇到过哪些 302 的场景?

  • HTTP 常用的请求方式,区别和用途?

  • HTTPS 是什么?具体流程

  • 三次握手和四次挥手

  • 你对 TCP 滑动窗口有了解嘛?

  • WebSocket与Ajax的区别

  • 了解 WebSocket 嘛?

  • HTTP 如何实现长连接?在什么时候会超时?

  • TCP 如何保证有效传输及拥塞控制原理。

  • TCP 协议怎么保证可靠的,UDP 为什么不可靠?

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

算法

  • 链表

  • 字符串

  • 数组问题

  • 二叉树

  • 排序算法

  • 二分查找

  • 动态规划

  • BFS

  • DFS

  • 回溯算法

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐