混子前端汇总面试常问知识点

混子前端分享一些面试常问干货,同时也帮助你们复习和巩固一波基础

ps:文章所有由我的总结,知识点分析不全面请自行查漏补缺javascript

页面布局

你必须知道的经典问题:

假设高度已知,写出三栏布局,其中左栏/右栏宽度各为300px;中间自适应。css

(ps:此题做为一个小白也要至少答出3种方法,如下分享连混子前端都知道的5种方法)html

  • 浮动float  [优势:兼容性好;缺点:脱离文档流]

HTML部分:
<section class="layout float">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="right"></div>
        <div class="center"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.float .left{
    float: left;
    width: 300px;
    background: red;
}
.layout.float .right{
    float: right;
    width: 300px;
    background: blue;
}
.layout.float .center{
    background: yellow;
}
复制代码

  • 定位absolute  [优势:快捷配合JS使用;缺点:子元素脱离文档流,可以使用性差]

HTML部分:
<section class="layout absolute">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:.layout article div{
    height: 100px;
}
.layout.absolute .left,.layout.absolute .right,.layout.absolute .center{
    position: absolute;
}
.layout.absolute .left{
    left: 0px;
    width: 300px;
    background: red;
}
.layout.absolute .right{
    right: 0px;
    width: 300px;
    background: blue;
}
.layout.absolute .center{
    left: 300px;
    right: 300px;
    background: yellow;
}
复制代码

  • 弹性盒flex  [完美解决上面2个布局的缺点]

HTML部分:
<section class="layout flexbox">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.flexbox .left-right-center{
    display: flex;
}
.layout.flexbox .left{
    width: 300px;
    background: red;
}
.layout.flexbox .center{
    flex: 1;
    background: yellow;
}
.layout.flexbox .right{
    width: 300px;
    background: blue;
}
复制代码

  • 表格table  [优势:兼容性好;缺点:其中某个单元格高度超出,其它两栏也超出]

HTML部分:
<section class="layout table">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.table .left-right-center{
    width: 100%;
    display: table;
    height: 100px;
}
.layout.table .left-right-center>div{
    display: table-cell;
}
.layout.table .left{
    width: 300px;
    background: red;
}
.layout.table .center{
    background: yellow;
}
.layout.table .right{
    width: 300px;
    background: blue;
}
复制代码

  • 网格grid  [不用栅格来模拟网格布局 / 代码量简化不少]

HTML部分:
<section class="layout grid">
    <article class='left-right-center'>
        <div class="left"></div>
        <div class="center"></div>
        <div class="right"></div>
    </article>
</section>

CSS部分:
.layout article div{
    height: 100px;
}
.layout.grid .left-right-center{
    display: grid;
    width: 100%;
    grid-template-rows: 100px;
    grid-template-columns: 300px auto 300px;
}
.layout.grid .left{
    background: red;
}
.layout.grid .center{
    background: yellow;
}
.layout.grid .right{
    background: blue;
}复制代码

补充:未知高度时,弹性盒flex / 表格table很适用,不修改代码前端

思考:上下高度固定,中间自适应(ps:混子前端都会的,你也必须会!)html5

CSS盒模型

你必须知道的盒子模型:
java

标准盒模型 + IE盒模型

标准模型:不包含padding和border值,content即盒子width / heightweb

IE盒模型:盒子width / height包含padding和border值面试


CSS如何设置这两种模型:

box-sizing: content-box;    // 标准盒模型(默认)
box-sizing: border-box;    // IE盒模型复制代码

JS如何设置盒模型对应宽和高

一、dom.style.width/height;    // 获取内联样式宽/高[局限性]
二、dom.currentStyle.width/height;    // 获取是渲染后的宽/高 [只有ie支持]三、window.getComputedStyle(dom).width/height;    // 支持chrome、firefox
四、dom.getBoundingClientRect().width/height[left/right/top/bottom];    // 不多人知道,获取渲染后width/height,计算一个元素绝对位置,根据视窗(viewport),经常使用获取left/right/top/bottom复制代码

边距重叠

常见问题:子元素高度100px,margin-top: 10px,此时父元素高度 ?ajax

解:父元素是100px;若是父元素设置overflow: hidden,就是110px;算法

[就是给父元素建立BFC(块级格式化)] 

BFC:边距重叠解决方案

基本概念:块级格式化上下文

原理(BFC渲染规则):

  1. BFC垂直方向边距不发生重叠
  2. BFC区域不会与浮动元素重叠(清除浮动)
  3. BFC在页面是独立容器,外面元素不会影响里面元素,里面元素不会影响外面元素
  4. 计算BFC高度时候,浮动元素也会参与计算

怎样建立BFC,除了给父元素添加overflow: hidden建立BFC,还有那些?

  1. float值除none之外
  2. position值不是static或relative就建立了BFC
  3. display: inline-block,table-cell和table相关的几个属性均可以建立
  4. overflow不是visiable也能够建立

DOM事件

你必须知道几个关于DOM的知识点

DOM事件级别(标准级别)

一代:DOM 0级

element.onClick = function(){
    // TODO
}复制代码

为何从DOM 0级直接到DOM 2级了?DOM 1级标准没有设定与事件相关的东西

二代:DOM 2级

element.addEventListener('click', function(){
    // TODO
}, false);复制代码

三代:DOM 3级 

新增了鼠标,键盘等事件

element.addEventListener('keyup', function(){
    // TODO
}, false);复制代码

事件模型

DOM事件流

浏览器在为当前页面与用户交互过程当中,如:点击鼠标是怎样传到页面上(如何响应的)?

混子前端认为分为三个阶段:

  1. 捕获[事件经过捕获到达目标元素]
  2. 目标阶段[点击按钮]
  3. 冒泡到window对象[目标阶段上传到window对象]

DOM事件捕获的具体流程

捕获:window > document > HTML > body  > HTML结构 > 一层层传递到目标元素

冒泡:捕获反过来就是冒泡,从目标元素传递到window

Event对象

  1. event.perventDefault()阻止默认事件,如:<a>标签绑定click事件,点击会阻止跳转默认行为
  2. event.stopPropagation()阻止冒泡行为
  3. event.stopImmediatePropagation(),如:button绑定两个click事件(a,b),如今想a执行后不执行b[在a响应函数添加event.stopImmediatePropagation(),会阻止b的执行],用来阻断
  4. event.currentTarget当前绑定事件元素
  5. event.target当前被点击元素
注意:4和5在没冒泡是同样的值,但在用了事件委托的状况就不同了。

如:ul > li 点击触发,给ul绑定onclick方法,此时:
target:<li>

currentTarget:<ul>

自定义事件

var eve = new Event('custome');    // 声明自定义事件
ev.addEventListener('custome', function(){
    console.log('custome');
});
ev.dispatchEvent(eve);    // dispatchEvent api触发复制代码

HTTP协议

协议主要特色

  1. 简单快速
  2. 灵活
  3. 无链接
  4. 无状态
ps:你觉得这样就完了?如下是你必须知道的扩展说明:
  1. uri是固定的,图片等(简单快速)
  2. http头部分有类型,经过http协议完成不一样数据类型传输(灵活)
  3. 连接一次会断掉,不会保持连接(无链接)
  4. 客户端和服务端请求图片,http协议帮助创建连接和传输,完成后断开,下次客户端在过来,服务端是没办法区分上次和此次是否是同一我的,即服务端没记录(无链接)

补充:网站如今记住的是身份,不是http协议作到的,而是经过服务端加session作到的

HTTP报文

客户端输入url,服务端响应;

报文分为2部分,即:请求报文 响应报文,如图所示:

HTTP方法

GET:获取资源

POST:传输资源

PUT:更新资源
DELETE:删除资源

HEAD:得到报文首部

POST和GET的区别

  1. GET在浏览器回退时是无害的,而POST会再次提交请求
  2. GET产生的URL地址能够被收藏,而POST不能够
  3. GET请求会被浏览器主动缓存,而POST不会,除非手动设置
  4. GET请求只能进行URL编码,而POST支持多种编码方式
  5. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留 
  6. GET请求在URL中传递的参数是有长度限制的(2kb),而POST没有限制
  7. 对参数的数据类型,GET只接受ASCII字符,而POST没有限制
  8. GET比POST更不安全,由于参数直接暴露在URL上,因此不能用来传递敏感信息
  9. GET参数经过URL传递,而POST放在Request body中

HTTP状态码

大概要知道的:
  1. 1xx:指示信息
  2. 2xx:成功
  3. 3xx:重定向
  4. 4xx:客户端错误
  5. 5xx:服务器错误

若是你对本身有追求,那如下你也要知道:

200:客户端请求成功

206:客户发送了一个带有Range头的GET请求,服务器完成;如:客户端发起0-10000字节请求,服务器返回206,服务端会根据Range头部截一部分,响应回客户端(客户端要求的Range范围)

301:所请求页面已经转移至新的url(永久重定向)

302:所请求页面已经临时转移至新的url(临时重定向)

304:客户端口有缓冲的文档并发出了一个条件性的请求,服务器告诉客户端,原来缓冲的文档还能够继续使用 [ 服务器告诉客户端有缓存,能够取缓存用,不用在服务器取 ]

400:客户端有语法错误,不能被服务器所理解

401:请求未经受权

403:对被请求页面访问禁止

404:资源不存在

500:服务器错误

503:服务器临时过载或宕机

HTTP持久链接

HTTP协议采用“请求 -- 应答”模式,当使用普通模式,即非keep-alive(持久链接)时,每一个请求/应答客户端和服务器都要新建一个链接,完成以后当即断开链接(HTTP无链接协议)

当使用keep-alive模式(持久链接)时,keep-alive功能使客户端到服务端的链接持久有效,当出现对服务器的后续请求时,keep-alive功能避免了创建或者从新创建链接

注意:持久链接1.1版本才支持,1.0版本不支持

管线化

当使用持久链接的状况下,某个链接上消息的传递相似于:

请求1 > 响应1 > 请求2 > 响应2 > 请求3 > 响应3

某个链接上的消息变成了相似如下这样(管线化):

[请求1 > 请求2 > 请求3 ] > [响应1 > 响应2 > 响应3]

注意:

  1. 只有GET和HEAD请求能够管线化,POST有所限制
  2. 服务端支持才能够,chrome和firefox默认并未开启管线化

原型链

建立对象的几种方法

  1. 字面量

    var o1 = {name: 'o1'};
    var o11 = new Object({name: 'o11'});复制代码

  2. 显示构造函数

    var M = function(){this.name='o2'};
    var o2 = new M();复制代码

  3. 借用Object.create()

    var P = {name: 'o3'};
    var o3 = Object.create(P)复制代码

Object.create()建立对象是用原型链链接的;o3的__proto__指向P对象,下文会说o3.__proto__指向构造函数的原型对象,也就是说原型就是P

o3.__proto__ === P;    // true复制代码

ps:Object.create()方法是把参数中对象做为一个新对象原型对象赋给o3的,o3自己不具有name属性,是经过原型链链接它的原型对象;

原型 / 构造函数 / 实例 / 原型链


ps:经过prototype 和 __proto__完成原型链向上找的过程,Object.prototype是整个原型链的顶端,到这里未知

问题来了:原型对象和原型链到底起什么做用?

解答:构造函数中增长不少属性和方法,实例就能够共用,当有不少个实例想去公用方法时,不可能每一个都拷贝一份,如今考虑应该有个东西存,这个东西就是原型对象

注意:

  1. 构造函数 > 函数才会有prototype,对象是没有的
  2. 只有实例对象有__proto__ [ 为何函数也有,函数也是对象 ]
  3. 构造函数prototype属性使实例和原型对象产生关联,修改了prototype就是修改实例上一级原型对象

instanceof原理


ps:主要在这条原型链上的构造函数都是实例对象构造函数

instanceof原理是 实例对象__proto__属性 和 构造函数 prototype判断是否是同一个引用,返回true / false

JS code:
function M (){ this.name = name };
var o = new M()
o instanceof M;    // true
o instanceof Object;    // true复制代码

注意为何 o instanceof Object 也返回true ?

JS code:
o.__proto__ === M.prototype;    // true
M.prototype.__proto__ === Object.prototype;    // true复制代码

为了区分此现象,建议你们平常使用constructor

JS code:
o.__proto__.constructor === M;    // true
o.__proto__.constructor === Object;    // false复制代码

因此说使用constructor判断比instanceof更严谨

new运算符(原理)

  1. 一个对象被建立(理解成字面量),它继承构造函数prototype [ 构造函数被执行,执行时候,相同的传参被传入 ]
  2. 同时上下文this会被指定为这个新实例 
  3. 构造函数若是返回了一个新对象,那这个对象会取代new出来结果 [ 若是构造函数没有返回对象,那new出来的结果为步骤1建立的对象 ]

JS code:

var newF = function(func){
    var o = Object.create(func.prototype);    // 1.建立对象关联构造函数原型对象
    var k = func.call(o);    // 2.执行构造函数
    if(typeof k === 'object'){    // 3.判断构造函数运行结果,是否为对象类型
        return k;
    }else{
        return o;
    }
}复制代码

通讯类

同源策略及限制

源:协议 / 域名 / 端口 80(默认),这三个有一项不一样就跨域了

限制:不是同一个源文档没有权利修改另外一个源文档

  1. cookie、localStorage 和 indexDB没法读取
  2. DOM没法得到
  3. Ajax请求没法得到(只适合同源策略)

先后端如何通讯

  1. Ajax [ 同源策略 ]
  2. WebSocket [ 不限制同源策略 ]
  3. CORS [ 支持同源和跨域 ]
  4. ..

如何建立Ajax

var xhr = XMLHttpRequest ? new XMLHttpRequest() : new Window.ActiveXObject("Microsoft.XMLHTTP");    // 兼容写法
xhr.setRequestHeader();    // header头部信息
xhr.open("GET","/try/ajax/ajax_info.txt", true);    
xhr.send();    // 发送
// xhr.onload判断xhr.status = 200 || 206 || 304 复制代码

跨域通讯几种方式

Ajax是不能发送跨域通讯的,浏览器在识别用Ajax发送一个跨域请求时,它会在http头部加origin来容许跨域通讯,若是不加就被拦截,这也是前端混子所理解CORS原理

JSONP

原理:在出现postMessage和CORS前,用script标签的异步加载实现;

JS code:
// 发送
<script src="http://www.abc.com/?data=name&callback=jsonp"></script>

// 返回
<script>
    jsonp({
        data:{}
    })   </script>

// 发送
document.getElementByTagName('head')[0].appendChild('script');复制代码

       实现:

      1.告诉服务端callback名称,未来是做为函数名返回的

      2.本地必须有jsonp全局函数,后面才能把数据执行出来

Hash

应用场景:页面A经过iframe潜入跨域页面B并发送消息

var B = document.getElementByTagName('iframe');
B.src = B.src + '#' + 'data';

window.onhashchange = function(){    // 写在B页面中, 监听hash有没有改变
    var data = window.location.hash;
}复制代码

postMessage

应用场景同上

// A页面发送到B
B.window.postMessage('data', 'http://B.com');

// B页面接收A
window.addEventListener('message',function(event){
    console.log(event.origin);    // 拿到A的源
    console.log(event.source);    // Awindow的对象
    console.log(event.data);    // 发送的消息拿到了
}, false);复制代码

Websocket

var ws = new Websocket('wss: echo.websocket.org');    // 指向服务器地址, ws不加密, wss加密
ws.onopen = function(){ ws.send(); };    // 发送出去
ws.onmessage = function(ev){ 
    ev.data;
    ws.close;    // close断开
}复制代码

CORS

新型的Ajax通讯api:fetch 实现CORS通讯

fetch('/some/url',{
    method: 'get'
}).then(function(response){
    
}).catch(function(error){

});复制代码

安全类

CSRF

CSRF:称为跨站请求伪造


ps:引诱点击后访问网站A后,浏览器自动上传cookie,A网站对身份从新确认(合法),就执行了动做,完成CSRF攻击

实现CSRF不可缺乏的条件:

  1. 访问A网站是登录的
  2. 接口存在漏洞

CSRF防护措施:

  1. token验证:刚才浏览器只自动上传cookie,没有自动上传token,token是注册成功后服务器会往本地存储token,在访问接口时携带token,若是是上图场景,只会自动上传cookie,不会上传token
  2. Refer验证(refer是页面来源):服务器判断页面来源是否是站点下页面
  3. 隐藏令牌(相似token):隐藏才http header头中,不会放在链接上,本质与token无区别,只是使用方式

XSS

XSS称为跨域脚本攻击

攻击原理:不须要作登录验证,核心原理是向页面注入脚本 [ 评论区是最好最入css攻击的,在提交区里面写script标签,img标签等 ],xss是利用合法渠道注入JS

防护措施:宗旨是不让页面执行插入的JS脚本

XSS与CSRF对比区别

  1. XSS是向页面注入JS运行,JS函数体作事情
  2. CSRF是利用自己漏洞帮我执行接口
  3. 方式不同,CSRF依赖用户登录网站

渲染机制类

DOCTYPE的做用

你要知道的:DTD(document type definition)告诉浏览器我是什么类型,浏览器根据类型来判断用什么引擎优化它;如:XML / HTML

这里是做用:DOCTYPE是声明文档类型和DTD规范的,直接告诉浏览器当前文档包含哪些DTD(文档类型)

<!DOCTYPE HTML>    // html5

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//en" "http://www.w3. org/TR/html4/strict.dtd">    // HTML4严格:不包含展现性和弃用元素(front)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//en" "http://www.w3. org/TR/html4/loose.dtd">    // HTML4传统:包含展现性和弃用元素(front)
复制代码

浏览器渲染过程


重排Reflow

浏览器根据各类样式计算放在它该出现的问题,如:JS修改div中内容(即触发Reflow)

常见触发Reflow

  1. 增长/删除/修改 致使Reflow或Repaint
  2. 移动DOM未知(CSS3动画)
  3. 修改CSS样式(宽高/display)
  4. Resize窗口有可能(移动端没有这个问题)或滚动时
  5. 修改字体

重绘Repaint

屏幕上页面的呈现内容

常见触发Repaint

  1. DOM移动
  2. CSS改动
  3. ... ( 太多了,只要判断如今呈现内容有没有改变 )

JS运行机制

JS是单线程的(同一事件只能作一件事);这里混子前端拿道面烂了的面试题来举例:

JS代码:

for (var i = 1;i <= 5;i ++) {
    setTimeout(function timer() {
        console.log(i)
    },i * 1000)
}复制代码

对JS做用域、闭包以及事件循环等概念不了解的伙伴会想固然的回答:

第一次循环,隔一秒输出1;

第二次循环,隔一秒输出2;

第三次循环,隔一秒输出3;

第四次循环,隔一秒输出4;

第五次循环,隔一秒输出5;

或者还有伙伴预期结果是分别输出数字1~5,每秒依次,每次一个。

但实际结果你们粘贴到控制台就都知道,以一秒的频率输出五个6。

下面来过一下上面提到的知识点:

做用域:这里我引用《你不知道的javascript》中的一个比喻,能够把做用域链想象成一座高楼,第一层表明当前执行做用域,楼的顶层表明全局做用域。咱们在查找变量时会先在当前楼层进行查找,若是没有找到,就会坐电梯前往上一层楼,若是仍是没有找到就继续向上找,以此类推。到达顶层后(全局做用域),可能找到了你所需的变量,也可能没找到,但不管如何查找过程都将中止。

闭包:个人理解是在传递函数类型的变量时,该函数会保留定义它的所在函数的做用域。读起来可能比较绕,或者能够简单的这么理解,A函数中定义了B函数而且它返回了B函数,那么无论B函数在哪里被调用或如何被调用,它都会保留A函数的做用域。

事件循环:这个概念深刻起来很复杂,下面新开一个段落详谈:

提及事件循环,不得不提起任务队列。事件循环只有一个,但任务队列可能有多个,任务队列可分为宏任务(macro-task)微任务(micro-task)
XHR回调、事件回调(鼠标键盘事件)、setImmediate、setTimeout、setInterval、indexedDB数据库操做等I/O以及UI rendering都属于宏任务(也有文章说UI render不属于宏任务,目前尚未定论)

process.nextTick、Promise.then、Object.observer(已经被废弃)、MutationObserver(html5新特性)属于微任务。注意进入到任务队列的是具体的执行任务的函数。

好比上述例子setTimeout()中的timer函数。另外不一样类型的任务会分别进入到他们所属类型的任务队列,好比全部setTimeout()的回调都会进入到setTimeout任务队列,全部then()回调都会进入到then队列。当前的总体代码咱们能够认为是宏任务。事件循环从当前总体代码开始第一次事件循环,而后再执行队列中全部的微任务,当微任务执行完毕以后,事件循环再找到其中一个宏任务队列并执行其中的全部任务,而后再找到一个微任务队列并执行里面的全部任务,就这样一直循环下去。这就是我所理解的事件循环。

继续看个栗子:

JS代码:

console.log('global')

setTimeout(function () {
   console.log('timeout1')
   new Promise(function (resolve) {
     console.log('timeout1_promise')
       resolve()
   }).then(function () {
     console.log('timeout1_then')
  })
},2000)

for (var i = 1;i <= 5;i ++) {
  setTimeout(function() {
    console.log(i)
  },i*1000)
  console.log(i)
}

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

setTimeout(function () {
  console.log('timeout2')
  new Promise(function (resolve) {
    console.log('timeout2_promise')
    resolve()
  }).then(function () {
    console.log('timeout2_then')
  })
}, 1000)

new Promise(function (resolve) {
  console.log('promise2')
  resolve()
}).then(function () {
  console.log('then2')
})
复制代码

来一步一步分析以上代码:

  1. 首先执行总体代码,“global”会被第一个打印出来。这是第一个输出
  2. 执行到第一个setTimeout时,发现它是宏任务,此时会新建一个setTimeout类型的宏任务队列并派发当前这个setTimeout的回调函数到刚建好的这个宏任务队列中去,而且轮到它执行时要延迟2秒后再执行。 
  3. 代码继续执行走到for循环,发现是循环5次setTimeout(),那就把这5个setTimeout中的回调函数依次派发到上面新建的setTimeout类型的宏任务队列中去,注意,这5个setTimeout的延迟分别是1到5秒。此时这个setTimeout类型的宏任务队列中应该有6个任务了。再执行for循环里的console.log(i),很简单,直接输出1,2,3,4,5,这是第二个输出
  4. 再执行到new Promise,Promise构造函数中的第一个参数在new的时候会直接执行,所以不会进入任何队列,因此第三个输出是"promise1",上面有说到Promise.then是微任务,那么这里会生成一个Promise.then类型的微任务队列,这里的then回调会被push进这个队列中。
  5. 再继续走,执行到第二个setTimeout,发现是宏任务,派发它的回调到上面setTimeout类型的宏任务队列中去。
  6. 再走到最后一个new Promise,很明显,这里会有第四个输出:"promise2",而后它的then中的回调也会被派发到上面的Promise.then类型的微任务队列中去。
  7. 第一轮事件循环的宏任务执行完成(总体代码能够看作宏任务)。此时微任务队列中只有一个Promise.then类型微任务队列,它里面有两个任务。宏任务队列中也只有一个setTimeout类型的宏任务队列。 
  8. 下面执行第一轮事件循环的微任务,很明显,会分别打印出"then1",和"then2"。分别是第五和第六个输出。此时第一轮事件循环完成。
  9. 开始第二轮事件循环:执行setTimeout类型队列(宏任务队列)中的全部任务。发现都有延时,但延时最短的是for循环中第一次循环push进来的那个setTimeout和上面第5个步骤中的第二个setTimeout,它们都只延时1s。它们会被同时执行,但前者先被push进来,因此先执行它!它的做用就是打印变量i,在当前做用域找变量i,木有!去它上层做用域(这里是全局做用域)找,找到了,但此时的i早已经是6了。(为啥不是5,那你得去补补for循环的执行流程了)因此这里第七个输出是延时1s后打印出6。
  10. 紧接着执行第二个setTimeout,它会前后打印出"timeout2"和"timeout2_promise",这分别是第八和第九个输出。但这里发现了then,又把它push到上面已经被执行完的then队列中去。
  11. 这里要注意,由于出现了微任务then队列,因此这里会执行该队列中的全部任务(此时只有一个任务),即打印出"timeout2_then"。这是第十个输出
  12. 继续回过头来执行宏任务队列,此时是执行延时为2s的第一个setTimeout和for循环中第二次循环的那个setTimeout,跟上面同样,前者是第一个被push进来的,因此它先执行。这里会延时1秒(缘由下面会解释)分别输出“timeout1”和“timeout1_promise”,但发现了里面也有一个then,因而push到then微任务队列并当即执行,输出了"timeout1_then"。紧接着执行for中第二次循环的setTimeout,输出6。注意这三个几乎是同时被打印出来的。他们分别是第十一到十三个输出
  13. 再就很简单了,把省下的for循环中后面三次循环被push进来的setTimeout依次执行,因而每隔1s输出一个6,连续输出3次。
  14. 第二轮事件循环结束,所有代码执行完毕。
这里解释下为何上面第12步不是延迟2秒再输出“timeout1”和“timeout1_promise”,这时须要理解setTimeout()延时参数的意思,这个延迟时间始终是相对主程序执行完毕的那个时间算的 ,而且多个setTimeout执行的前后顺序也是由这个延迟时间决定的。   

再回过头来看上面那个问题,理解了事件循环的机制,问题就很简单了。for循环时setTimeout()不是当即执行的,它们的回调被push到了宏任务队列当中,而在执行任务队列里的回调函数时,变量i早已变成了6。那如何获得想要的结果呢?很简单,原理就是须要给循环中的setTimeout()建立一个闭包做用域,让它执行的时候找到的变量i是正确的。

混子前端给出以下5种解决方案:

  1. 引入IIFE

    JS代码
    
    for(var i = 0;i < 5; i++){
        (function(i){
            setTimeout(function timer(){
                console.log(i);
            }, i * 1000)
        })(i);
    }
    复制代码

  2. 利用ES 6引入的let关键字

    JS代码
    
    for(let i = 0;i<5;i++) {
        setTimeout(function timer(){
            console.log(i);
        }, i * 1000);
    }
    复制代码

  3. 利用ES 5引入的bind函数

    JS代码
    
    for (var i=1; i<=5; i++) {
        setTimeout( function timer(i) {
            console.log(i);
        }.bind(null,i), i*1000 );
    }
    复制代码

  4. 利用setTimeout第三个参数

    JS代码
    
    for (var i=1; i<=5; i++) {
        setTimeout(function timer(i) {
            console.log(i);    
        }, i*1000,i );
    }
    复制代码

  5. 把setTimeout用一个方法单独出来造成闭包

    JS代码
    
    var loop = function (i) {
        setTimeout(function timer() {
            console.log(i);  
        }, i*1000);
    };
    
    for (var i = 1;i <= 5; i++) {
        loop(i);
    }复制代码

页面性能

  • 资源压缩合并,减小HTTP请求(把文件变小)
  • 非核心代码异步加载
    异步加载方式:
    一、动态脚本加载:document.creatElement建立标签/节点标签加到head/body
    二、defer:script上添加
    三、async:script上添加

    对比defer/async区别:
    defer是在HTML解析完才会执行,若是是多个按加载顺序执行
    async是在加载完以后当即执行,多个async的话,执行顺序和加载顺序无关
  • 利用浏览器缓存
    强缓存 / 协商缓存
  • 利用CDN[内容分发]让网络快速到服务端把文件下载下来
  • 预解析DNS
    高级浏览器在页面里a标签默认打开了DNS预解析,不加这句话,a标签也作了DNS预解析,但页面若是是HTTPS协议,浏览器是关闭预解析的,经过meta标签强制打开预解析:

    <meta http-equiv="x-dns-prefetch-control" content="on">
    
    <link rel="dns-prefetch" href="//host_name_to_prefetch.com">复制代码

错误监控

首先咱们要知道错误分类

  1. 即时运行错误:代码错误
  2. 资源加载错误:img / js / css 加载失败

捕获方式:

(一)若是是即时性错误

  1. try...catch
  2. window.onerror 
  3. addEventListener

(二)若是是资源加载性错误

  1. Object.onerror  对 img / js 添加 onerror事件
  2. performance.getEntries() 获取全部已加载的资源,返回一个数组
  3. Error事件捕获window上[冒泡不会阻止捕获]

    JS代码
    
    window.addEventListener('error', function(){
        // ****
    }, true);复制代码

ps:若是JS错误是跨域的,能够在script标签添加crossorigin属性,同时在响应JS资源时,设置JS资源头Access-Control-Allow-Origin就能够拿到错误信息了

这里上报错误的原理:利用ajax通信上报 / 也能够利用image对象上报

JS代码

(new Image()).src = "http://baidu.com/tesjk?r=tksjk";复制代码

利用这种方式比ajax简单,也能实现资源上报

算法类

因为本人也是混子前端,不一一列举,但做为一名合格的前端,算法是必不可少的。

ps:请随意吐槽本混子,会虚心接受


好了,混子前端就总结到这里,还请留意混子前端不定时的更新....

相关文章
相关标签/搜索