理解 JavaScript 中的执行上下文和执行栈-转载
- 什么是执行上下文?
简而言之,执行上下文是评估和执行 JavaScript 代码的环境的抽象概念。每当 Javascript 代码在运行的时候,它都是在执行上下文中运行。 - 执行上下文的类型
- 全局执行上下文
- 任何不在函数内部的代码都在全局上下文中。
- 函数执行上下文
- 每当一个函数被调用时, 都会为该函数创建一个新的上下文。
- 全局执行上下文
- 执行栈
- 执行栈,也就是在其它编程语言中所说的“调用栈”,是一种拥有 LIFO(后进先出)数据结构的栈,被用来存储代码运行时创建的所有执行上下文。
let a = 'Hello World!';
function first() {
console.log('Inside first function');
second();
console.log('Again inside first function');
}
function second() {
console.log('Inside second function');
}
first();
console.log('Inside Global Execution Context');
image.png
- 怎么创建执行上下文?
- 创建执行上下文有两个阶段:
- 1 创建阶段
- 2 执行阶段。
- 执行上下文在概念表示上如下:
ExecutionContext = {
//this的绑定
ThisBinding = <this value>,
//词法环境
LexicalEnvironment = { ... },
//var声明的变量的环境
VariableEnvironment = { ... },
}
在 JavaScript 代码执行前,执行上下文将经历创建阶段。在创建阶段会发生三件事:
- 1.this 值的决定,即我们所熟知的 This 绑定。
- 在全局执行上下文中,this 的值指向全局对象
- 在函数执行上下文中,this 的值取决于该函数是如何被调用的。
- 2.创建词法环境组件。
- 简单来说词法环境是一种持有标识符—变量映射的结构。包含了两个组件
- 环境记录器是存储变量和函数声明的实际位置。
- 外部环境的引用意味着它可以访问其父级词法环境(作用域)。
- 简单来说词法环境是一种持有标识符—变量映射的结构。包含了两个组件
- 3.创建变量环境组件。
- 它同样是一个词法环境,其环境记录器持有变量声明语句在执行上下文中创建的绑定关系
- 在 ES6 中,词法环境组件和变量环境的一个不同就是前者被用来存储函数声明和变量(let 和 const)绑定,而后者只用来存储 var 变量绑定。
- 下面有一段代码,我们写出执行上下文的伪代码
let a = 20;
const b = 30;
var c;
function multiply(e, f) {
var g = 20;
return e * f * g;
}
c = multiply(20, 30);
执行上下文看起来像这样:
//全局执行上下文
GlobalExectionContext = {
ThisBinding: <Global Object>,
LexicalEnvironment: {
//环境记录器1(全局环境是对象环境记录器,用来定义出现在全局上下文中的变量和函数的关系。)
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
a: < uninitialized >,
b: < uninitialized >,
multiply: < func >
}
outer: <null>
},
//var声明的变量
VariableEnvironment: {
EnvironmentRecord: {
Type: "Object",
// 在这里绑定标识符
c: undefined,
}
outer: <null>
}
}
//函数执行上下文
FunctionExectionContext = {
ThisBinding: <Global Object>,
//词汇环境
LexicalEnvironment: {
//环境记录器2 函数环境是声明式环境记录器,用来存储变量、函数和参数。函数环境,声明式环境记录器还包含了一个传递给函数的 arguments 对象(此对象存储索引和参数的映射)和传递给函数的参数的 length。
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符函数中会有一个arguments
Arguments: {0: 20, 1: 30, length: 2},
},
outer: <GlobalLexicalEnvironment>
},
VariableEnvironment: {
EnvironmentRecord: {
Type: "Declarative",
// 在这里绑定标识符
g: undefined
},
outer: <GlobalLexicalEnvironment>
}
}
执行阶段
这是整篇文章中最简单的部分。在此阶段,完成对所有这些变量的分配,最后执行代码。
注意 — 在执行阶段,如果 JavaScript 引擎不能在源码中声明的实际位置找到 let 变量的值,它会被赋值为 undefined。
postcss是什么
BFC元素的特点
- BFC的原理(即渲染规则)
- BFC元素内部子元素垂直方向上的边距会发生重叠。
- BFC的区域不会与浮动元素的box重叠。
- BFC在页面是一个独立的容器,外面的元素不会影响里面的元素,里面的元素也不会影响外面的元素。
- 计算BFC高度的时候 浮动元素也会参与计算。
- 怎么创建一个BFC
- 方式一:float值不会none;
- 方式二:posttion的值不为static或者relative;
- 方式三:display的值为table相关的几个,比如table、table-cell等。
- 方式四:ovflow:hidden/scroll;
DOM事件级别
- DOM0 element.onclick = function(){}
- DOM2 element.addEventListener("click",function(){},false)
- DOM3 element.addEventListener("keyup",function(){},false)
事件流(三个阶段)
- 第一阶段 捕获
- 第二阶段 目标阶段
- 第三阶段 冒泡
比如点击鼠标,会先通过捕获找到点击的元素,然后再冒泡到window
event对象
- event.preventDefault() 阻止默认事件
- event.stopPropagation() 阻止冒泡
- event.stopimmediatePropagation() 事件响应优先级
- event.currentTarget
- event.target
自定义事件
//方式一 使用Event
var eve = new Event("自定义事件名");
element.addEventListener("自定义事件名",function(){})
element.dispatch(eve);
//方式二 使用CustomEvent,可以额外传一些自定义的参数
element.addEventListener("cat", function(e) { process(e.detail) });
// create and dispatch the event
var event = new CustomEvent("cat", {
detail: {
hazcheeseburger: true
}
});
element.dispatchEvent(event);
类型转换
显示类型转换
- Number函数
- 数字转换:本身
- 字符串转换:数字/NaN/0 空字符串转成0
- 布尔值转换:true为1,false为0
- undefined转换:NaN
- null转换:0
- 对象类型转换
- 先调用对象自身的valueOf方法,如果得到的结果是原始数据类型(即基本类型),就进行Number显示转换
- 如果得到的是复合类型(即引用类型),再调用对象自身的toString方法,如果得到的结果是基本类型,就进行Number显示转换
- 如果得到的是复合类型,则报错。
- String函数
- 数字转换:字符串
- 字符串转换:字符串
- 布尔值转换:true为"true",false为"false"
- undefined转换:"undefined"
- null转换:"null"
- 引用类型转换
- 先调用对象自身的toString方法,如果得到的结果是原始数据类型(即基本类型),就进行Number显示转换
- 如果得到的是复合类型(即引用类型),再调用对象自身的valueOf方法,如果得到的结果是基本类型,就进行Number显示转换
- 如果得到的是复合类型,则报错。
- Boolean函数
- undefined、null、0、NaN、''(空字符串) => false,其他都是true
隐式类型转换
- 四则运算
- 判断语句
- Native调用
- 比如console.log() alert()都会把里面的值通过调用String函数转换成字符串类型
常见题目
[] + [] //""
[] + {} //"[object Object]"
{} + [] //0 注意在chrome中,此处{}会被解析成代码块,所以最后运算的是+[],会对[]进行Number转换,得到的就是0
{} + {} //"[object Object][object Object]"
true + true //2
1 + {a:1} //"1[object Object]"
http协议
http报文组成
image.png请求示例
image.png响应示例
image.png请求方法
- get 获取资源
- post 传输资源
- put 更新资源
- delete 删除资源
- head 获取报文首部
get和post区别
- get在浏览器回退的时候是无害的,而post会再次提交请求
- get产生的url地址可以被收藏,post的不可以
- get请求会被浏览器主动缓存,post不会,除非手动设置
- get请求只能进行url编码,post支持多种编码方式
- get请求参数会被完整保留在浏览器历史记录中,post的参数不会被保留
- get请求在url中传输的参数长度是有限制的,一般是2kb,每个浏览器不尽相同,而post没有限制
- 对参数的数据类型,get只接收ASCII字符,而post没有限制
- get比post更加不安全,参数直接暴露在url地址后面,所以不能用来传敏感信息
- get参数通过url传递,post放在request body中
http状态码
- 200 OK :客户端成功返回
- 206 Partial(局部的) Content:客户端发了一个带有Range头的get请求,服务器完成了它
- 客户端请求了一部分内容,比如0~1000字节,服务器就返回一个206,服务器存放的是一个完整文件,看到有range头,就按照range头截取一部分响应给客户端,也就是说响应体中只有range头制定的这部分内容,这种情况状态码就是206。比如vedio标签和audio标签,当要播放的文件很大的时候,基本返回的就是206。
- 301 Moved Permanently: 所有的请求页面被永久转移到了新的url
- 302 Found: 所请求的页面临时转移到新的url
- 304 Not Modified: 客户端有缓冲的文档发送了一条件性的请求,服务器告诉客户,原来的缓冲文档还可以继续使用。
- 400 Bad Request:客户端请求有语法错误,不能被服务器所理解。
- 401 Unauthorized:请求未经授权,这个状态码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden:资源禁止被访问
- 404 Not Found:资源不存在
- 500 Internal Server Error:服务器发生不可预期的错误原来缓冲的文档还可以继续使用
- 503 Server Unavaliable:请求未完成,服务器临时过载或当机,一段时间后可能恢复正常。
管线化
- 在使用持久连接的情况下,某个链接消息传递类似于
请求1->响应1->请求2->响应2->请求3->响应3
- 管线化就是客户端把请求打包一次传输过去,服务器把响应打包一次传输过来。所以某个连接上的消息变成了下面的样子
请求1->请求2->请求3->响应1->响应2->响应3
- 管线化的特点
- 管线化通过持久连接完成,仅http/1.1支持此技术。
- 只有get和head请求可以进行管线化,而post有所限制。
- 初次创建连接时,不应该启动管线化机制,因为服务器不一定支持http/1.1版本的协议。
- 管线化不会影响到来的顺序,如上面例子所示,响应返回的顺序并未改变。
- http/1.1要求服务器支持管线化,但并不要求服务端也对管线化进行处理,知识要求对于管线化的请求不失败即可。
- 由于上面提到的服务端的问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务端和代理程序对管线化的支持并不好,现代浏览器如chrome和firefox默认并未开启管线化。
通信类
- 什么是同源策略限制
- 前后端如何通信
- 跨域通信的几种方式
同源策略
同源策略限制从一个源加载的文档或者脚本如何与另一个源的资源进行交互。只是一个用于隔离潜在恶意文件的安全机制。
- cookie、localStorage和indexDB无法读取
- DOM无法获取
- ajax请求不能发送
前后端如何通信
- ajax
- webSocket
- CORS(通信标准,跨域资源共享,不受同源策略的限制,支持同源也支持不同源)
跨域通信的几种方式
- jsonp
- hash(注意hash改变页面不会跳转,search改变页面会跳转)
- postMessage
- WebScoket
- cors
安全类
csrf Cross-site request forgery 跨站请求伪造
-
利用本身的漏洞去调一些他想要的接口,依赖于用户必须要登录,诱导用户做一些事情(新浪就发生过通过点击链接增加粉丝数目)
image.png - 防御措施
- token验证
- Referer验证
- 隐藏令牌
xss cross-site scripting 跨域脚本攻击
- 原理 向页面注入js脚本做一些事情
- 防御措施 宗旨就是让脚本不可执行
算法
-
排序
image.png - 堆栈、队列、链表
- 递归
- 波兰式和逆波兰式
二面/三面
渲染机制类
- 什么是DOCTYPE以及作用
-
浏览器的渲染过程
image.png
image.png
image.png
image.png
image.png - 重排Reflow
- 定义:DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式来计算并根据计算结果将元素放到它该出现的位置这个过程称为reflow。
- 触发Reflow
- 增加、删除、修改DOM节点时,会导致Reflow或Repaint。
- 移动DOM的位置,或是搞个动画的时候
- 当你修改css样式的时候
- 当你Resize窗口的时候(移动端没有这个问题),或是滚动的时候。
- 打你修改页面的默认字体时
- 重绘Repaint
- 定义:当个中盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器便把这些元素按照自己的特性绘制了一遍,于是页面的内容出现了,整个称之为repaint。
- 触发repaint
- DOM修改
- css改动
- 布局Layout
提升页面性能
- 1.资源压缩合并,减少http请求
- 2.非核心代码异步加载
- 3.利用浏览器缓存
- 4.使用cdn
- 5.预解析DNS
//强制开启a标签的dns预解析(在https协议中a标签的预解析是关闭的)
<meta http-equiv="x-dns-prefetch-control" content="on">
//dns预解析
<link rel="dns-prefetch" href="//host_name_to_prefetch.com">
异步加载
- 1.动态脚本加载(动态创建script标签)
- 2.defer
- 3.async
- 异步加载的区别:
- 1)defer是在html解析完成之后才会执行,如果是多个,按照加载的顺序依次执行。
- 2)async是在自身加载完之后立即执行,如果是多个,先回来限制性,执行顺序和加载顺序无关。
浏览器缓存
- 缓存就是资源文件在浏览器中存在的备份(副本),比如请求了某个图片资源,缓存以后该图片资源是放在磁盘上的,下次请求相当于从磁盘上直接读取。
- 1.强缓存(不问服务器直接就用)
- expires Expires:Thu,21 Jan 2017 23:39:02 GMT 这是个服务器下发的绝对时间,有可能客户端时间和服务器时间不一致,所以增加了Cache-Control。
- Cache-Control Cache-Control:max-age=3600(单位是秒) 相对时间
- 不管是相对时间也好,绝对时间也好,在这之前不会再去询问服务器,直接拿过来缓存资源去用;如果两个时间都下发了,以Cache-Control的相对时间为准。
- 2.协商缓存(和服务器协商一下我的缓存还能不能用,是否过期)
-
Last-Modified if-Modified-Sinc
当强缓存的时间过了,客户端重新发送请求到服务端,会在资源响应的响应头中给一个Last-Modified:Wed,26 Jan 2017 00:35:11 GMT;客户端再次请求资源的时候会在请求头中带上if-Modified-Since,if-Modified-Since的值是上次服务器下发的Last-Modified的值,对比两个时间后看是否用缓存。 -
Etag if-None-Match
解决Last-Modified if-Modified-Sinc存在的问题,会存在请求的资源时间可能改了,但是内容可能并没有修改的情况,这种情况应该从缓存副本中拿更高效。所以便有了Etag和if-None-Match;
客户端请求服务器的时候服务器响应资源会在响应头中加上Etag,Etag的值是唯一的hash值;下次客户端请求资源的时候会加上请求头if-None-Match,if-None-Match的值就是上次服务器给的Etag的值,对比两个hash值后看是否用缓存。
-
Last-Modified if-Modified-Sinc
错误监控类
- 前端的错误分类
- 即时运行错误:代码错误
- 1)try...catch
- 2)window.onerror
- 资源加载错误(注意资源加载的错误无法冒泡,所以不会冒泡到window.onerror,但是不阻止事件捕获)
- 1)object.onerror
- 2)Error事件捕获
- 3)performance.getEntries()
//performance.getEntries() 得到的是一个数组,包含了当前页面的所有成功的资源请求 performance.getEntries().forEach(item=>console.log(item.name)) //打印图片资源 ,对比就知道哪些图片没能正确加载 [...document.getElementsByTagName("img")].forEach(item=>console.log(item.src))
- 即时运行错误:代码错误
image.png
上报错误的基本原理
- 1.采用ajax通信的方式(不推荐)
- 2.利用image对象上报
(new Image()).src = "http://api/errorWatch?url=xxx"
网友评论