一个进程就是一个程序的运行实例,
每启动一个应用程序,操作系统都会为此程序创建一块内存,用来存放代码、数据数据、一个执行任务的主线程,我们把这样的一个运行环境叫进程。
一个进程关闭,操作系统则会回收为该进程分配的内存空间
线程是依附于进程的,而进程中使用多线程并行处理能提升运算效率。
进程与线程关系图.png
进程与线程的之间的关系: (进程是火车,线程是每节车厢)
* 默认情况下,每个tab页面一个进程,互不影响 -- 特殊情况1:如多个空白tab会合并成一个进程;undefined-- 特殊情况2:从一个标签页中打开了另一个新标签页,当新标签页和当前标签页属于同一站点的话,那么新标签页会复用当前标签页的渲染进程* 核心任务是将 HTML、CSS 和 JavaScript 转换为网页图层,通知浏览器主线程进行界面显示;* 渲染进程都是运行在沙箱模式下* 渲染进程中包含以下线程: (1).GUI渲染线程undefined(2) Javascript引擎线程undefined(3) 事件触发线程(归属于浏览器而不是JS引擎)undefined(4)定时触发器线程undefined(5)异步http请求线程undefined(6)合成线程undefined(7)IO线程:处理和其他进程进行通信undefined各进程之间通过 IPC 来通信
css解析和HTML解析并行,不会阻塞HTML解析,但是会阻塞页面渲染(但是Javascript执行,会导致CSS的解析增加HTML解析的时间)
根据DOM和styleSheets生成LayoutTree布局树(渲染树),所有不可见的元素会被忽略,如head标签 ,
display:none的元素,script标签等
布局树.png
渲染引擎根据布局树生成图层树,
GPU进程根据不同图块生成位图,还给合成线程
l浏览器渲染流程图.png
渲染流程中的特殊情况:
指修改了元素几何属性,如位置、尺寸、内容、结构等变化,引发元素几何位置变化,浏览器需要重新计算样式、构建布局树,开始之后的一系列子阶段,这个过程就叫重排。
触发重排的情况:(Javascript操作DOM,引发不同渲染流水线重新工作)
指修改了元素的外观样式,不会引起几何位置变化,直接入绘制阶段,生成绘制列表,然后执行之后的一系列子阶段,这个过程就叫重绘。如背景颜色、边框颜色,文字颜色等
指更改一个既不要布局也不要绘制的属性,直接分割图块阶段,然后交给浏览器主进程并不线上显示,这个过程叫做直接合成。
如 transform:translate(100px, 100px)
相对于重绘和重排,直接合成能大大提升效率
减少重排(回流)、重绘, 方法:
编译结果为两部分:执行上下文、可执行代码
showName();//函数showName被执行console.log(myname);//undefinedvar myname = '小白'function showName() { console.log('我是小白');}编译时的执行上下文如下:(变量环境部分)
{ showName: xxx, //showName 函数在堆内存的引用地址 myname: undefined}可执行上下文如下:
showName(); console.log(myname);//undefined myname = '小白'1. 变量环境
2. 词法环境
3. 外部环境,即当前执行上下文中变量的外部引用,用来指向外部的执行上下文,也称为 outer
4. this,this的指向在于当前函数的调用方式 -直接调用指向全局对象window (严格模式下则是undefined)undefined-通过对象调用,this指向该对象undefined-通过apply、call、bind等方法调用则指向第一个参数对象undefined-箭头函数中的this指向外层函数的this(解析箭头函数不会创建执行上下文) let userInfo = { userName: "小白", age: 18, sayHello: function () { setTimeout(function () { console.log(`${this.userName},你好`) //undefined }, 100) }}userInfo.sayHello()修改一个函数this指向的方法:
问题:
解决: 引入let、const、块级作用域
一段代码解析完成,即执行上下文创建完成,就立即执行可执行代码
var a = 2function add(b,c){ return b+c}function addAll(b,c){ var d = 10 result = add(b,c) return a+result+d}addAll(3,6)第一步,解析全局代码,创建全局执行上下文,压入调用栈,并全局的执行可执行代码
执行上下文栈.png
第二步,执行到addAll调用时,生成addAll函数的执行上下文,压入上下文,并执执行addAll函数内部的可执行代码
执行上下文栈.png
第三步,执行到add 函数调用,生成add 函数的执行上下文,压入调用栈
执行上下文栈.png
执行add 函数内部的可执行代码,return 结果,然后add函数执行上下文销毁,弹出调用栈
第四部,执行addAll后续可执行代码,return 结果,addAll函数上下文销毁,弹出调用栈,最后只剩下全局执行上下文,伴随页面整个生命周期
问题: 栈溢出(递归函数)
var 、 let、const的区别:
function foo(){ var a = 1 let b = 2 { let b = 3 var c = 4 let d = 5 console.log(a); //1 console.log(b); //3 } console.log(b) ;//2 console.log(c); //4 console.log(d); //报错:d is not defined} foo()函数的作用域由词法作用域决定
词法作用域:是指作用域是函数声明的位置来决定的,和函数怎么调用无关
当函数执行完毕时,函数体内的定义的变量会随着函数执行上下文立即销毁,但是当外部函数包含内部函数,且内部函数使用了外部函数中定义的变量,这些变量就不会销毁,仍然保存在内存,这些变量和内部函数就形成了闭包
闭包的形成条件:
function foo() { var myName = "小白"; var age = 18; function sayHello(){ console.log (`你好,我的名字是:${myName},今年${age}`) } return sayHello;}let hello = foo();hello()// myName和age就是foo函数的闭包1. 引用闭包的函数是全局变量时,闭包则会一直保存在内存中,直到页面关闭
2. 引用闭包的内部函是局部变量时,内部函数执行结束后,内部函数就会立即销毁,下次JavaScript 引擎的执行垃圾回收时,判断不再使用,则销毁闭包,回收内存问题:内存泄露( 该回收的内存未被及时回收 )
数据被使用之后,不再需要了,就称为垃圾数据,垃圾数据要及时销毁,释放内存空间,否则会内存泄漏。
(1)栈内存回收
当Javascript代码执行时,记录当前执行状态的指针(称为
ESP),指向当前执行上下文的指针,当前函数代码之前完毕,指针下移指向下一个要执行的函数执行上下文,当前执行上下文弹出调用栈进行销毁,这个过程就是该函数栈内存回收的过程
function foo(){ var a = 1 var b = {name:"极客邦"} function showName(){ var c = 2 var d = {name:"极客时间"} } showName()}foo()调用栈.png
(2)堆内存回收
垃圾回收器:
第一步,标记堆内存中活动对象和非活动对象
第二步,回收非活动数据所占据的内存
在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象
第三步,做内存整理
每个渲染进程都有一个 主线程 ,处理以下事件:
消息队列和循环机制保证了页面有条不紊地运行
同步任务 :直接进入主线程执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步任务 :以回调函数实现,先在其他的任务队列中排队,等待同步任务执行完成,该任务才会进入主线程执行,分为宏任务、微任务
宏任务队列 :宏任务执行队列,回调函数里要执行的任务
微任务队列 :JavaScript 执行一段脚本,V8 引擎会首先创建一个全局执行上下文,同时也会创建一个专为V8 引擎内部使用的微任务队列
宏任务 主要有以下几种:
Javascript脚本执行本身就也是一个宏任务,宏任务中又包含同步任务、微任务、宏任务
console.log(1);setTimeout(()=>{ console.log(3); Promise.resolve(4).then((data) => { console.log(data) }) setTimeout(() =>{ console.log(5) },0)}, 0)Promise.resolve(2).then((data) => { console.log(data)})//执行结果:1, 2, 3,5微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列
微任务早于宏任务执行
微任务的执行时长会影响到当前宏任务的时长
微任务主要有:
(2)执行过状态不可逆,不会再变
要么pending ->fulfilled
要么pending -> rejected
(3) Promise实现原理:
- 回调函数延迟绑定(微任务)
- 回调函数返回值穿透,then回调函数中的返回值,可以穿透到最外层
- 错误“冒泡”,通过链式调用then、catch,不论在那一层出错,都会“冒泡”至catch
//封装一个函数,简单模拟promise function MyPomise(executor) { let _this = this; let _onResolve = null; this.then = function (onResolve) { _onResolve = onResolve; } this.resolve = function (value) { //此处用setTimeout模拟延迟绑定回调任务,也是微任务出现的原因 setTimeout(() => { _onResolve(value) }, 0) } executor(this.resolve, this.reject); } let demo = new MyPomise((resolve, reject) => { resolve(200) }) demo.then((data) => { console.log(data) })(4)Promise.resolve(value):返回一个以给定值解析后的Promise对象
-- 参数是一个 Promise对象的实例 ,直接返回这个 实例
--
参数是一个thenable对象(即带有then方法),Promise.resolve()返回的是一个执行then方法之后的Promise对象,并且采用执行之后的状态
let thenable = { then: function(resolve, reject) { resolve(200) }}let p1 = Promise.resolve(thenable); //200,因为p1已经是fulfilled状态,因此直接then,可以获取到返回值p1.then((data) => { console.log(data)})-- 参数是一个普通值或对象,则直接返回新的 Promise 对象,状态为fulfilled(值为参数本身)
-- 参数为空,直接返回一个fulfilled状态的 Promise 对象,(值为undefined)
(5)链式调用时,
then回调函数执行成功,返回的是一个fulfilled状态的promise,会进入后面的then
then执行失败,返回的是一个rejected的promise,会进入后面的catch
catch回调函数执行成功,返回的也是一个fulfilled状态的promise,进入后面的then
catch执行失败,返回的是一个rejected的promise,进入后面的catch
async function foo() { console.log(1); let a = await 100; // await之后的代码相当于then函数里的代码 console.log(a); console.log(2);}console.log(0);foo();console.log(3);//执行顺序:0,1,3,100,2async函数实现原理.png
生成器函数 :是一个带星号函数,是可以暂停执行和恢复执行的
执行器 :执行生成器函数的函数,则成为执行器
协程 : 是一种比线程更加轻量级的存在,
function* genDemo() { console.log("开始执行第一段"); yield 'generator 1';// 遇到yield 关键字,JavaScript 引擎会暂停该函数的执行,并将关键字后面的内容返回给外部,外部函数可以通过next()恢复继续执行 console.log("开始执行第二段"); yield 'generator 2; }console.log('main 0')let gen = genDemo(); //创建了一个gen协程,但是并没有执行console.log(gen.next().value) ; //generator 1console.log('main 1')console.log(gen.next().value); //generator 2console.log('main 2')参考文档https://www.jianshu.com/p/12b9f73c5a4f/,这个应该是讲的比较详细的
栈的概念理解(3种):
事件循环执行过程 :
setTimeout(function () { console.log('timeout1'); },0) new Promise(function (resolve) { console.log('promise1'); resolve(100) }).then(() => { console.log('then1'); }) new Promise(function (resolve) { console.log('promise2'); resolve(200) }).then(() => { console.log('then2'); }) setTimeout(function () { console.log('timeout2'); },0) console.log('global1');同步任务执行完成时的执行栈.png
页面的生命周期:
从页面的生命周期方向思考:
关键资源(核心资源):阻塞页面首次渲染的资源称为页面的关键资源,HTML、CSS、Javascript
具体优化方法 :
(1)压缩HTML文件,移除 不必要注释
(2)合并并压缩CSS 、JavaScript等文件 ,script 标签加上 async 或 defer属性
(3)避免使用table布局
(4)缓存(第二次请求命中缓存则直接读取缓存)
目标是减少页面渲染过程的重排、重绘
具体优化方法 :
(1)减少DOM操作,将多次操作DOM合并为一次,如插入元素节点
(2)减少逐项更改样式,最好一次性更改style,或者将样式定义为class并一次性更新
(3)前端框架Vue、React(虚拟DOM和Diff算法等)
(3)避免多次读取offset等属性,使用变量做缓存
(4)防抖、节流
(5)做动画效果时,使用will-change和transform 做优化
引入虚拟DOM树执行流程.png
XSS 攻击是指黑客往 HTML 文件中或者 DOM 中注入恶意 JavaScript 脚本,在用户浏览页面用户实施攻击的一种手段
如:<script> --><script>目的是利用服务器的漏洞和用户的登录状态来实施攻击
发起CSRF攻击的方式:
解决方法:
-- SameSite=Strict,限制此Cookie不能随着跳转链接跨站发送
-- 验证请求来源站点
-- 使用Token验证
服务器第一次返回时生成一个Token
再次请求客户端带着对应的Token,进行验证
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。