Giter VIP home page Giter VIP logo

blogs's Introduction

前端学习笔记

1、JS基础篇

1.1 原型链、对象的创建和继承
1.2 对象的浅拷贝和深拷贝
1.3 new的过程——手写new
1.4 对闭包的理解(垃圾回收机制)与应用——防抖、节流、柯里化
1.5 js的异步回调、Promise及其手写
1.6 this的指向及其4种绑定规则
1.7 js设计模式之——观察者模式与发布订阅模式

2、数据结构与算法篇

2.1 栈和队列——2个栈实现队列
2.2 链表——js实现链表类及其方法
2.3 排序算法——冒泡排序 | 归并排序 | 快速排序
2.4 二叉树、堆的一些概念
2.5 二叉树类的创建及前、中、后续遍历 | 深度优先、广度优先
2.6 集合(Set)、字典(Map)、哈希表(hash)简介

3、计算机网络篇

3.1 计算机网络的体系结构及其各层的作用
3.2 网络层要点
3.3 运输层要点
3.4 应用层要点
3.5 应用层协议——HTTP/1.0、1.1及2.0
3.6 密码体制及应用——HTTPS

4、HTML(5)、CSS(3)篇

4.1 HTML(5)标签
4.2 CSS选择器、单位、文本
4.3 盒模型、元素分类
4.4 浮动与定位
4.5 CSS3新特性
4.6 应用
******换位子 DOM事件

5、浏览器篇

5.1 浏览器的组成
5.2 渲染引擎——重排、重绘
5.3 JS引擎——事件循环Event Loop
5.4 浏览器缓存——Cookie、sessionStorage和localStorage
5.5 浏览器内存——内存泄漏

6、网页通信篇

6.1 Ajax原理
6.2 同源策略及跨域
6.3 服务端推送Comet——长轮询、短轮询和http流
6.4 SSE和Web Sockets
6.5 用户身份识别——session和token
6.6 Web安全防范——CSRF与XSS

7、网站性能优化篇

7.1 减少HTTP请求
  7.1.1 合并图片、内联图片、合并脚本和样式表
  7.1.2 缓存组件——Last-Modified,Expires,Cache-Control和ETag
  7.1.3 使用外部JS和CSS——为了能缓存
  7.1.4 避免重定向
  7.1.5 不使用@import、空的href、空的form表单method
7.2 优化网络连接
  7.2.1 使用内容发布网络CDN——缩短物理传输距离
  7.2.2 减少DNS查找——缩短IP查找时间
7.3 减小响应的大小
  压缩组件、精简代码、删除重复
7.4 优化加载顺序
  样式表放在顶部、脚本放在底部、懒加载和预加载
7.5 减少重排和重绘
  避免CSS表达式、使用事件代理、防抖节流等
7.6 运算量大的代码分片执行
  避免浏览器卡死 —— js代码分片执行

8、前端框架篇--Vue

8.1 前端框架设计模式——MVC、MVP、MVVM
8.2 Vue生命周期、常用API、组件通信
8.3 Vue-router——手写原生hash、history路由
8.4 Vuex
8.5 Vue原理——手写Vue核心
  8.5.1 Vue工作机制——总体流程
  8.5.2 Vue首次渲染——手写响应式、html编译、依赖收集
  8.5.3 Vue更新渲染——手写createElement,render,diff算法

9、webpack和Node.js篇

9.1 js的模块系统——ES6与ES5的模块系统
9.2 webpack原理——loader、plugin、bundle

参考文献

  1. 《JavaScript高级程序设计》 第三版 Nicholas C.Zakas
  2. 《你不知道的JavaScript》 KYLE SIMPSON
  3. 《学习JavaScript数据结构与算法》 第三版 Loiane Groner
  4. 《计算机网络》 第七版 谢希仁
  5. 《CSS权威指南》 第三版 Eric A.Meyer
  6. 《高性能网站建设指南》 Steve Souders
  7. 《How Browsers Work: Behind the scenes of modern web browsers》、w3school、MDN、Vue等在线文档
  8. SegmentFault、CSDN、简书、博客园、牛客等在线博客

blogs's People

Contributors

hwadn avatar

Stargazers

 avatar

Watchers

 avatar

blogs's Issues

应用层要点

一、应用层概述

  应用层用于定义进程之间的通信规则,包括定义进程交换的报文类型、语法、字段的语义等。
  应用层的许多协议都是基于客户服务器方式(通信中所涉及的2个应用进程)。前端里常见的协议有域名系统DNS、 http协议、https协议。
  作为前端的基础知识,本节主要介绍域名系统DNS和URL。

二、域名系统DNS(Domain Name System)

从右向左解析,先解析“.”最外层公网域名,再解析低级域名

1、域名

      image
  任何一个连接在互联网上的主机或路由器,都有一个唯一的层次结构的名字,叫做域名。域名和主机的IP地址一一对应。
  和32位的IP地址相比,域名便于记忆,而且还具有显著的、强烈的标志特征。

2、应用进程域名解析过程

  当一个应用进程需要把主机名(域名)解析为IP地址时,它就调用解析程序,并将域名放在DNS请求报文中,DNS以UDP用户数据报方式发送给本地域名服务器,本地服务器若是查到对应的IP地址就用回答报文返回IP地址,否则就向其他域名服务器查找,直到找到为止(找不到则解析失败)。

3、域名服务器

  所有顶级域名有一个共同的根,从根域名服务器开始,往下有顶级域名服务器、权限域名服务器(负责一个区),本地域名服务器(默认)。
域名服务器查找过程:(迭代查询)
(1)主机向本地域名服务器查找IP地址,若找不到就向根域名服务器查找,转(2);
(2)根域名服务器找到了就返回给本地域名服务器,若找不到就将下一个需要查询的域名服务器的IP地址返回给本地服务器。转(3)
(3)本地域名服务器就向下一个域名服务器发送查找报文。类似于(2),直到查到IP地址为止。
附:递归查询的方式是根服务器帮忙转发DNS报文,然后下一个域名服务器也帮忙转发,实现递归查找。
  为了提高DNS查询效率,域名服务器中广泛使用着高速缓存,用来存放最近查询过的域名,因此下次再查询时就不用再想其他域名服务器查找了。

三、统一资源定位符URL

  URL相当于一个文件名在网路上的扩展,是与互联网相连的机器上任何可访问对象的一个指针。由于访问不同对象使用的协议也不同,因此URL也指定了协议类型。URL组成形式如下:
      <协议>://<主机>:<端口>/<路径>;?#
其中协议和主机不区分大小写。
例:
url示例

  • params——参数,以;分隔,参数为名/值对,可以包含多个参数字段。(最新http移除)
  • query——查询,以?分隔,查询组件的内容没有通用格式。(?和#直接是查询传参)
  • frag——hash值,以#隔开。

浏览器缓存——Cookie、sessionStorage和localStorage

一、HTTP Cookie

1、Cookie的产生原因

  浏览器和服务器用HTTP协议进行通信,但是HTTP协议是无状态的,即每次http请求和之前的请求都无关联。但在很多实际应用中,服务器需要记住浏览器用户的信息,例如购物网站需要记住用户的购物车。因此这时Cookie就产生了。在HTTP中可以通过cookie记录用户身份,给服务器识别。

2、Cookie的使用和限制

(1)Cookie的使用

  当用户浏览某个网站时,网站服务器通过在HTTP的响应报文中添加一个“Set-cookie:识别码”的首部行。当用户收到响应时,浏览器就在它的Cookie文件里添加一行(包括服务器的主机名和Set-cookie后的识别码,即:IP,识别码)。当用户再浏览这个网站,就提取cookie文件里的识别码,在HTTP请求报文中添加首部行“Cookie:识别码”。因此服务器通过查询对这个识别码的记录就能知道用户的一些信息。

(2)Cookie的限制

  • cookie是绑定在特定域名下的——浏览器会根据请求的域名在http请求报文中发送对应域名的cookie值。
  • 每个域名可设置的cookie数量有限,超过规定的数量就会删除之前的cookie。
  • cookie尺寸也有限制,

(3)cookie的属性构成

属性 属性介绍
name 用于指定Cookie的键
value 用于指定Cookie的值
expires 过期时间,GMT格式。默认会话关闭过期。
domain 域名。默认为设定该Cookie的域名。
path 指定路径,必须是绝对路径。默认为请求该Cookie的网页路径。
secure 用来指定Cookie只能在加密协议HTTPS下发送到服务器。
max-age 用来指定Cookie有效期,秒。优先级高于expires。
HttpOnly 用于设置该Cookie不能被JavaScript读取。

例如:
Set-Cookie: cookiename=httponlyTest; Path=/; Domain=domainvalue; Max-Age=seconds; HTTPOnly

二、sessonStorage和localStorage

1、产生原因

  cookie限制多,存储量小。在一些应用中需要在浏览器存储大量数据,因此用Web Storage可以满足需求。

2、sessionStorage

  只存储特定于某个会话的数据。,因此在浏览器关闭后sessionStorage数据将删除。

(1)特点

  • 页面刷新(浏览器崩溃重启)依然存在
  • 绑定于某个服务器的会话。在本地无法读取。

(2)js操作

  • 写入——sessionStorage.setItem(name,value)/sessionStorage.name=value
  • 读取——sessionStorage.getItem(name)/sessionStorage.name
  • 删除——delete sessionStorage.name/sessionStorage.removeItem(name)

3、localStorage

  绑定URL,数据永久保存(除非手动删除)。

(1)特点

  • 同源策略。页面必须来自同一个域(子域无效),使用同一种协议和端口,才能操作localStorage。

(2)js操作

  • 写入——localStorage.setItem(name,value)/localStorage.name=value
  • 读取——localStorage.getItem(name)/localStorage.name
  • 删除——delete localStorage.name/localStorage.removeItem(name)/或用户清除浏览器缓存

对象的浅拷贝和深拷贝

一、浅拷贝--Object.assign实现

定义:只是复制对象的第一层,更深层次的对象只是引用。

方法一 assign

var o={a:1,b:{w:[1,2,{q:4}]}};
var ocopy=Object.assign({},o);

方法二 concat(数组)

 var a2 = a1.concat()

方法三 es6扩展运算符(数组)

 var a2 = [...a1]

缺点: 拷贝的只是首层的属性,深层的对象值会和原对象共享。

二、深拷贝--2种方法

定义:拷贝了对象的值,拷贝结果是一个新对象,与原对象拥有完全相同的属性,包括属性值中的属性。

1、JSON的方法

原理: JSON.stringify()可把js对象序列号为JSON字符串;JSON.parse()可把JSON字符串解析为JS对象。

    var o={a:1,b:{w:[1,2,{q:4}]}};
    var c=JSON.parse(JSON.stringify(o));

优点: 简单方便。
缺点: 当对象的属性值为undefined或function类型时,会忽略该属性,即新对象里没有其属性。无法解决循环引用。

2、递归遍历

原理: 使用递归遍历对象的每层属性,直到属性值不是数组或对象时。

    function deepCopy(obj){
      var res=Array.isArray(obj)?[]:{};//拷贝的新对象
      //遍历对象或 数组的每一项
      for(key in obj){
        if(obj.hasOwnProperty(key)){
          //若此项是数组或对象,则递归。null是一个特殊的对象,不考虑。
          if(obj[key] instanceof Object && obj[key]!=null && !(obj[key] instanceof Function)){
            res[key]=deepCopy(obj[key]);
          }else{
            //此项是值,直接赋给结果对象
            res[key]=obj[key];
          }
        }
      }
      return res;
    }

优点: 不会忽略对象的任何属性。
缺点: 递归占用内存。无法解决循环引用。

浏览器的组成

一、浏览器组成

layers
  浏览器主要包括用户界面,浏览器引擎、渲染引擎、网络、用户界面后端、JS解释器数据存储。下面将介绍主要部分。

二、渲染引擎——浏览器内核

  负责对网页语法的解释(如HTML、JavaScript)并渲染网页。主流的浏览器内核有:

  • trident引擎——IE
  • webkit引擎——chrome、safari、opera
  • blink引擎——chrome、opera

三、JS解释器——JS引擎

  用于解析和执行 JavaScript 代码。主流JS引擎如下:

  • JagerMonkey引擎——Mozilla(Firefox浏览器公司)
  • V8引擎——Google(chrome)
  • JScript引擎——微软(IE)

四、数据存储

  浏览器需要在硬盘上保存数据,如Cookie。

4、浮动与定位

一、浮动——float

              float:left | right | none | inherit

1、特性

  • 任何元素都可浮动,浮动后生成块级框BFC)。
  • 浮动元素会从正常流中删除
  • 其他元素会环绕浮动元素
  • margin不会合并
  • 其包含块是最近的块级祖先元素
  • 浮动元素之间不会重叠
  • 空间不够,浮动元素会被挤到新行上

2、行为

  • 浮动元素高度超出父元素高度时,父元素无法包含它,会产生高度塌陷
    解决方法:
    (1) clearfix:添加块级子元素,设置clear:both。
    (2)触发父元素BFC:
    • float: left、right
    • overflow:auto、scroll、hidden
    • display:table-cell、table-caption、inline-block
    • position:fixed、absolute
  • 浮动元素与一般元素重叠情况(负margin设置):行内框会在浮动元素之上显示;块级框内容显示在上,其他在下。

3、清除浮动

clear:left——元素左边不会出现浮动元素
clear:right——元素右边不会出现浮动元素
clear:both——元素两边不会出现浮动元素

二、定位——position

1、定位类型

static——正常文档流内
relative——相对定位。相对自身定位,原本占位空间保留。建立新的包含块**(BFC)
absolute——绝对定位。相对于最近的非static定位的祖先元素定位,从
文档流完全删除,原本占位消失,生成块级框
fixed——固定定位,相对于视图框定位,从
文档流**完全删除。

2、属性

relative、absolute、fixed具有偏移属性top/bottom/right/left,百分比值相对于包含块的尺寸。相对于外边距边界偏移。

3、层叠顺序

static元素层叠顺序:行内元素 > 浮动元素 > 块级元素 > 背景(层叠上下文)
对于所有元素层叠顺序规则:后来居上,后面的元素覆盖前面的元素(默认)。不论其定位类型。
body——0

三、Block Formatting Context——格式化块级上下文(BFC)

1、作用

  • BFC的子元素和外部相互不影响。(BFC元素不会发生margin重叠)
  • 更适合自适应布局

2、触发情况

  • html根元素
  • 浮动元素
  • overflow不是visible的元素。(auto、scroll、hidden)
  • display:table-cell、table-caption、inline-block
  • position是absolute或fixed

密码体制及应用——HTTPS

一、密码体制

1、对称密钥密码体制——一对一

image
(1)相关概念

  • 加密密钥E——对明文加密需要的字符串
  • 加密算法K——加密运算算法
  • 解密密钥K(也可以不是K,与K有相关性就行)——对密文解密需要的字符串
  • 解密算法D——解密运算算法

(2)对明文X加解密过程(密钥由密钥分配中心分配给通信双方)
生成密文Y:Y=EK(X)
对密文解密:X=DK(Y)
(3)应用——DES
  DES对密文保密,加密和解密算法公开。

2、公钥密码体制——可以多对一

image
(1)相关概念(同1)
  但加密密钥和解密密钥是不同的,加密密钥称为公钥(认证中心CA分配给浏览器),解密密钥称为私钥(存在于服务器)只有私钥需要保密,公钥和加、解密算法都公开。
(2)加密过程(同1)
(3)应用——SSL

二、HTTPS协议——基于SSL/TLS(公钥密码体制)

image
HTTPS是在http协议上增加了一个SSL公钥密码体制,用于对明文加密后传输,过程如下:
image

  • (1)浏览器A保存着CA的认证证书,A向服务器B发送SSL加解密算法
  • (2)服务器B选择算法发给A
  • (3)B发送包含公钥的数字证书
  • (4)浏览器A生成秘密数
  • (5)A用公钥加密对生成的秘密数加密,发给B
  • (6)服务器B用私钥解密,获得秘密数。用秘密数生成会话密钥
  • (7)用会话密钥(对称密钥)进行后续数据加密通信

CA证书

证书 = 公钥(服务端) + 签名(CA私钥签名) + 申请者和颁布者的信息

SSL层3次握手,4步——生成对称密钥

  • (1)客户端请求SSL连接,发送支持的加密方式和一个随机数client random
  • (2)服务端选择加密方式,响应另一个随机数server random、数字证书(包含公钥)
  • (3)客户端校验证书合法性,生成新的随机数premaster random,并用公钥和加密算法加密,发送给服务器。同时对1前3个随机数应用算法K,产生会话密钥F。
  • (4)服务端对接收的数据用私钥解密,对client random、premastre random、server random进行同样的K运算,生成对称密钥F。之后开始用对称密钥通信。

优点

  • 数据加密、更安全,使攻击和伪造数据的成本增高

缺点

  • 响应时间更长,安全套接字层SSL需要先握手
  • 需要费用
  • 需要额外的流量

总体流程如下

image

三、http3

  • 基于UDP

合并图片,内联图片、合并脚本和样式表

一、合并图片——将多个图片合称为1个大图片

  • map标签可对合成的图片进行分区域链接
  • background-position属性可对合成的图片分区域显示

二、内联图片——无需额外下载

  通过将img标签的src属性用data:URL模式。格式如下:
      image
其中image是对图片的编码。这样只要文档中包含了内联图片,就无需额外下载,减少了http请求。
缺点

  • 浏览器不会缓存图片(可将内联图片放在外部样式表中,通过缓存样式表来间接缓存图片)
  • 兼容性较差,ie8以下都不支持
  • 对图片大小有限制
  • Base64编码会增加图片大小,增加文档的下载流量

三、合并脚本和样式表

  将多个脚本合并为1个脚本;
  将多个样式表合并为1个样式表;

2、选择器、单位、文本

一、HTML关联样式方式

(1)link标签引入
(2)style标签里编写
(3)在style里用@import引入
(4)内联样式——在标签的style属性里编写

二、选择器

1、类型

(1)元素选择器(可分组)

如:div,img,h1{color:red;}

(2)类选择器(.)

按照class属性选择,如.btn{...}

(3)ID选择器(#)

按照id属性选择,如#first{...}

(4)属性选择器([ ])

  • 按照元素的某个属性选择,如h1[class]{...};
  • 或具体属性值,如h1[class="aside"];
  • 或属性中的子字符串,如h1[class="asi"];

(5)后代选择器

按元素的后代元素选择,如h1 em{...}

(6)选择子元素(>)

h1>strong{...}

(7)选择相邻兄弟元素(+)

选择紧接着某个元素的后一个元素,如h1+p{...}

(8)伪类选择器(:)

  • 链接访问后a:visited{...};
  • 第一个子元素p:first-child{...}(p集合的第一个)

(9)伪元素选择器

  h2:before{...}

2、优先级

特殊性

  • 1,0,0,0——内联样式
  • 0,1,0,0——ID选择器
  • 0,0,1,0——类、属性或伪类选择器
  • 0,0,0,1——元素、伪元素选择器
  • 0,0,0,0(无特殊性)——结合符(,)、通配符(*)选择器

(1)特殊性值可累加,越靠左特殊性越高,越优先。
(2)若特殊性相同,则写在后面的优先。
(3)样式中带有!important标志的优先性最高。
优先级
!important > 内联样式 > ID选择器 > 类、属性、伪类选择器 > 结合符、通配符
选择器是从右向左查询的 ,所以尽量减少嵌套。

三、值和单位

1、颜色表示方法

  • 命名颜色
  • rgb颜色
  • 16进制颜色:#FF0000
  • rgba

2、单位

(1)绝对长度

  • cm——厘米
  • mm——毫米
  • pt——点。标准印刷单位
  • pc——12点

(2)相对长度

  • em——相对于父元素字体大小
  • rem——相对于根元素(html)的字体大小
  • px——像素。取决于设备的分辨率。

3、其他单位

  • deg——度
  • grad——梯度
  • rad——幅度
  • s——秒
  • ms——毫米

四、文本属性

  • font-size——就是字体高度
  • line-height——行框高
  • vertical-align——垂直对齐方式
  • word-spacing——字间隔
  • letter-spacing——字母间隔

以上属性参考下图:

文本属性

运输层要点

一、运输层概述

  运输层向应用层提高通信服务,主要使用TCPUDP协议。通信中需要提供软件端口号(应用层的各种协议进程与运输实体进行层间交互的一种地址)。

二、端口号

image
  前端主要用到的是http(默认80端口)和https(默认443端口)协议。国内必须备案才能使用默认端口号。默认端口号(熟知端口号)数值范围是0-1023,因此个人搭建服务器时,自定义的端口号不能在此范围内。

三、用户数据报协议UDP(User Datagram Protocol)

1、特点

(1)无连接
(2)尽最大努力交付(不保证可靠交互)
(3)面向报文
(4)没有拥塞控制
(5)支持一对一、一对多、多对一、对对多
(6)首部开销少

2、UDP数据报

image

3、应用

image
多用于实时应用,但无拥塞控制会引起严重的网络拥塞。

四、传输控制协议TCP(Transmission Control Protocol)

1、特点

(1)面向连接(先连接后通信)
(2)一对一(2个端点)
(3)可靠交付
(4)全双工通信(两端具有发送、接收缓存。将数据放入发送或接收缓存就暂时不用管了)
(5)面向字节流(发送方不会识别发送的数据,根据网络情况设置数据块大小;接收方识别数据。)

2、套接字(socket)

      套接字socket=(IP地址:端口号)

3、停止等待协议

image
即发送完一个分组就等待对方确认回复,然后再发送下一个。
(1)无差错情况:发送过去一个分组,收到确认再发送下一个(类似于Promise的链式调用概念)
(2)出现差错:超时重传——在规定时间里还未收到回复就重新发送上次出错的那个分组。
image
流水线传输:停止等待虽然简单,但是信道利用率太低。因此发送方使用流水线传输的方式(发送方连续发送多个分组,不必每发完一个就等待)

4、TCP报文段

image
(1)序号——报文所携带的字节的首序号
(2)确认号——希望收到的下一个报文的序号值
(3)ACK——ACK=1时(2)确认号才有效(因此常和确认号同时使用)
(4)SYN——同步序号,SYN=1表示这是一个连接请求或连接接收报文。在连接建立时使用。
(5)FIN——终止连接。FIN=1表示数据数据已经发完,要求断开连接。

5、TCP三次握手

image
seq——当前报文(本报文)的序号
ack——对发送方确认报文的序号(即希望对方下一个报文的序号)
为什么是3次?2次不行?
  ——防止已失效的A的连接请求报文突然又发送给B
  考虑1种异常情况:A的第1个连接请求在某个网络节点滞留了很长时间,然后A超时重传了连接请求,发完数据断开了连接。之后B又收到了A的连接请求。(假如是2次握手,那么B就会打开连接,然后给A发送确认等待接收数据。但是A已经发送完成,不会再理会B的这个确认。因此B就一直等待,造成了很多资源的浪费)。因此用3次握手,若A没有对B的确认报文做回复,那么B就不用打开连接,就不会浪费资源了。

6、TCP四次挥手

image
为什么是4次挥手?
第1次——A的断开请求;
第2次——B的确认,但B的数据未发完,还要等会才能断开
第3次——B的数据发送完成,发送断开请求
第4次——A发送确认报文,B可以断开
为什么B先断开,A后断开?
在B断开后A还要等待一段时间再断开,原因是若第4次的报文丢失,那么B就会超时重传。如果这时A已经断开,那么B就会一直重传,浪费了B的资源。因此A等待一段时间,若B没有重传说明B已经断开连接,A也就可以断开了。若B重传了,那么A就再发送一次确认,直到B不再重传。

7、应用

image

3、盒模型、元素分类

一、整体布局规则

  元素按照从左向右,从上向下的规则自动排列。称为正常流布局。

二、基本元素框(盒模型)

  每个元素都会生成一个矩形框。包括内容区、内边距、边框、外边距,其中外边距可以是负值。

1、包含块

  每个元素都有包含块,所有元素都是相对于包含块摆放的。
  元素的包含块是指最近的块级祖先元素、表单元格或行内块祖先框的内容区构成。

2、标准盒模型:

  实际宽度 = width + border(左右) + padding(左右) + margin(左右)
  实际高度类似。
margin、padding、border默认值是0。
###注意:width、padding、margin的百分比值都是相对于包含块的width。height相对于包含块的height,若是包含块未指定height,会重置为auto。

3、怪异盒模型

  实际宽度 = width(包含padding和border) + margin(左右)

  **CSS的box-sizing属性可设置盒模型计算方式: content-box对应标准盒模型,border-box对应怪异盒模型。**width和margin可设置为auto,width优先级最高。

三、元素类型划分

替换元素:占位符,其内容就是由外部内容确定的。例如img,input。也生成框
**非替换元素:**内容由其本身内容确定,绝大多数元素都是这种类型。如p、h1~6、span。

块级元素 vs 行内元素

元素类型 块级元素 行内元素
盒模型属性 支持所有 非替换元素不支持width、height。左右内、外边距可见,上下内外边距不会改变行高。边框重叠。
是否换行
水平格式化 默认宽度由包含块决定:width + 2 * padding + 2 * border + 2 * margin = 父元素width。替换元素的宽度默认是固有宽度。 宽度有内容决定
垂直格式化 高度由内容决定:height + 2 * padding + 2 * border + 2 * margin = 父元素height。margin-top/bottom无法auto。垂直相邻外边距合并。 高度由内容最高高度决定
常见元素 div、h1-6、table、li、ul、ol、form、header、footer等 span、a、em、b、strong、input(行内替换元素)、img(行内替换元素)
注意 body高度为0,子元素高度百分比相对于body宽度 换行会出现空格

四、margin重叠情况

1、上下margin重叠
2、父元素无padding-top、border-top,则子元素的margin-top会和父元素的margin-top重叠
3、空的块元素,无padding-top、border-top,其自身的margin-top和margin-bottom会重叠

二叉树、堆的一些概念

一、二叉树的基本属性

image

1、根节点:位于树顶部的节点
2、内部节点:有父、子节点的节点
3、外部节点(叶节点):没有子叶的节点
4、深度:从根节点到该节点的最长简单路径边的条数(当前节点->根节点)。树中所有结点中的最大层次是这棵树的深度。
5、高度:从该节点到叶子节点的最长简单路径边的条数(当前节点->叶节点)

二、二叉数的类型

1、二叉搜索树:左节点比父节点小,右节点比父节点大。
2、自平衡树:是一种二叉搜索树,但其任意一个节点的左右子树的高度之差最多为1.
3、完全二叉树:树的每一层都有左、右2个子节点(除了叶节点可以只有左)
4、二叉堆:是一种完全二叉树。其是最小堆或最大堆。
(1)最小堆:所有的节点都小于等于它的每个子节点
(2)最大堆:所有的节点都大于等于它的每个子节点

三、二叉树的遍历方法

1、前序遍历:父节点在前(->左->右)
2、中序遍历:父节点在中间(左->->右)
3、后序遍历:父节点在后(左->右->

原型链、对象的创建和继承

面向对象的三大特点:

  • 封装——私有变量、方法
  • 继承——本节内容
  • 多态——通过arguments属性判断

一、原型链——实例、原型、构造函数的关系

image
原型: 每个函数都有一个prototype属性,指向一个对象,这个对象就是函数的原型。原型上的属性和方法被所有实例共享。
原型链: 原型链是实现继承的主要方法。让一个函数的原型等于另一个类型的实例实现继承,多个类通过原型层层继承,就形成了原型链。

二、对象的创建--3种方法

1、构造函数模式

原理: 函数也是对象,在函数里直接添加属性和方法。

    function Father(name,age){
      //属性
      this.name=name;
      this.age=age;
      //方法
      this.sayName=function(){
        console.log(this.name);
      }
    }

优点: 属性私有。
缺点: 一样的方法在不同实例中重新创建了一遍,无法复用。

2、原型模式

原理: 函数拥有原型(prototype)属性,其指向一个对象,包含共享的属性和方法。

    function Father(){
    }
    Father.prototype={
      contructor:Father,//字面量重写了constructor,实例的contructor与此同步
      name:'Tom',
      age:24,
      sayName:function(){
        console.log(this.name);
      }
    };

优点: 方法共享。
缺点: (1)无法传递参数;(2)属性被所有实例共享。

3、构造函数+原型模式

原理: 结合以上二者的优点,去除二者的缺点。

    function Father(name,age){
      this.name=name;
      this.age=age;
    }
    Father.prototype.sayName=function(){
        console.log(this.name);
    }

优点: 属性私有,可传参,方法共享。

注意:原型的设置不会提升,在设置对象原型之前,其实例是无法访问到原型上的属性的。

但console.log会打印对象设置后的原型,可能是原型是引用的关系。

归并排序

一、归并**——分而治之

1、数列合并: 将2个递增数列合并为1个递增数列,只需对比2数列中值的大小再即可操作;
2、什么情况下只划分1次就可以将一个数组分成2个递增数列?——当数组长度<=2时。

因此只要将一个数组划分为n个长度为1的小数组,再两两合并,即可组成n/2个长度为2的递增数组;再合并,合并...直到合并成一个大数组,就是我们想要的递增数组。

二、代码

function mergeSort(arr){
	if(arr.length<=1)return arr;
	//一分为二
	var mid=Math.floor(arr.length/2);
	var left=arr.slice(0,mid);
	var right=arr.slice(mid);
	return merge(mergeSort(left),mergeSort(right));
}
//2数组合并
function merge(left,right){
	//排序
	var i=0,j=0;
	var result=[];
	while(i<left.length && j<right.length){
		//对比,小的排左边
		if(left[i]<right[j]){
			result.push(left[i]);
			i++;
		}else{
			result.push(right[j]);
			j++;
		}
	}
	//剩余的都加进去
	while(i<left.length){
		result.push(left[i]);
		i++;
	}
	while(j<right.length){
		result.push(right[j]);
		j++;
	}
	return result;
}

三、时间复杂度

把1个数组分成长度为1的n个数组,每次划分可一分为二,则需要k次:2^k=n,k=logn。
每次都把所有小数组遍历一遍进行排序,一次遍历需要操作n次。
因此总操作次数是nk=nlogn。即时间复杂度=O(nlogn)。

样式表放在顶部、脚本放在底部、懒加载和预加载

一、样式表放在顶部

1、原因

  • 浏览器渲染是逐步呈现——下载完HTML和CSS后,浏览器才会边解析边呈现。若样式表没下载,浏览器不会呈现页面,这样就阻塞了浏览器。

2、解决方法

  • 将CSS样式表用link标签放在头部——link标签会并行下载CSS,这样就不会阻塞页面了。另外,样式表中的@import会在页面加载完毕后加载,会阻塞页面呈现,造成白屏,要避免使用。

二、脚本放在底部

1、原因:

  • 脚本无法并行下载——因为(1)脚本可能修改页面,浏览器需要等待,JS修改完页面后浏览器再呈现;(2)脚本必须在所有HTML解析完生成完整DOM后才能执行
  • 执行脚本会阻塞页面后续动作

2、解决方法

  • 脚本放在底部

三、懒加载和预加载

  懒加载和预加载都是错峰加载,是在浏览器空闲时加载资源,优化网页性能。

1、懒加载

(1)原理

  当访问一个页面的时候,先把img元素或是其他元素的背景图片路径替换成一张大小为1*1px图片的路径(这样就只需请求一次,俗称占位图),只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。这就是图片懒加载。常见应用在淘宝等购物网站中。

(2)注意点

  • 占位图片的大小要设置与待加载图片的大小一致,避免重排(回流)

(3)实现方法

方法一(不好)

image
scrollTop(scrollY或pageYOffset也行,但兼容性不好)获取元素滚动距离。
offsetTop获取元素距离offsetParent内边距距离。并不一定是其直接父元素。
offsetTop-scrollTop-父元素高度 = 元素距离视口底部距离。
缺点: 设置offsetParent的定位比较麻烦,很难用

方法二 (好用)

image
使用element.getBoundingClientRect()获取元素的尺寸和位置对象。获取的位置是相对于浏览器视口顶部的距离,好用简洁。

2、预加载

(1)原理

  在浏览器空闲时提前加载图片,将图片缓存,当用户需要查看时可直接从本地缓存中渲染。

(2)方法

  • html中img标签最初设置为display:none;
<img src="images/3.jpg" alt="" width="50%" height="300px" style="display:none">
  • js脚本中使用image对象动态创建好图片;
var img = new Image(); 
img.src = "images/5.jpg"; 
  • 使用XMLHttpRequest对象可以更加精细的控制预加载过程,缺点是无法跨域

js的异步回调、promise及其手写

一、js的异步回调

1、什么是异步回调?

回调是把一个函数当作参数传递给另一个函数,让其在一定条件发生时执行。
异步是指不阻塞js对当前程序的执行,在将来才发生,例如DOM事件和Ajax请求。
因此异步回调就是指将一个函数作为参数传递给一个异步事件函数,让其在将来这个异步事件发生时执行。

2、代码示例

function callback(){
	console.log('有人点击了按钮');
}
var btn=document.querySelector('#btn');
btn.addEventListener('click',callback);//点击事件

3、异步回调的问题

回调地狱:上面例子只是调用了一层回调函数,如果里面再调用很多次回调函数,那么代码的阅读性将十分差,需要我们一层一层的去找要执行的回调函数。这对以后代码的维护将是十分头疼的事。

二、Promise介绍

1、概述

Promise是异步编程的一种方式,以回调函数的方式将要执行的程序传入Promise。然后Promise有3种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。当Promise中的状态变为成功或失败时,可以通过.then()方法执行后续的操作。

2、优点

将回调函数写成链式,阅读性好,解决回调地狱的问题,维护性强

3、代码示例

var btn=document.querySelector('#btn');
//点击事件
var p=new Promise(function(resolve,reject){
	btn.addEventListener('click',function(){
		console.log('已经点击');
		resolve('ok');
		// reject('点错了');
	});
});
p.then(function(val){
	console.log(val);
},function(err){
	console.log(err);
});

三、手写Promise、Promise.all()、Promise.race()

个人理解的Promise本质上就是将回调函数用对象进行管理,简易的Promise实现如下:

1、手写简易Promise(没有链式调用)

function _Promise(fn){
	// 状态
	this.state = 'pending';
	// 传参
	this.value = undefined;
	this.reason = undefined;
	// 异步待执行函数
	this.fulfilledCallbacks = [];
	this.rejectedCallbacks = [];
	// 执行
	fn(resolve, reject);
	var that = this;
	// resolve函数
	function resolve(value){
		if(that.state == 'pending'){
			that.state = 'fulfilled';
			that.value = value;
			// 执行异步
			that.fulfilledCallbacks.forEach(cb=>{
				cb(that.value);
			});
		}
	}
	// reject函数
	function reject(reason){
		if(that.state == 'pending'){
			that.state = 'rejected';
			that.reason = reason;
			// 执行异步
			that.rejectedCallbacks.forEach(cb=>{
				cb(that.reason);
			});
		}
	}
}
// then异步
_Promise.prototype.then = function(onFulfilled, onRejected){
	// 加入异步待执行队列
	if(this.state == 'pending'){
		if(typeof onFulfilled == 'function'){
			this.fulfilledCallbacks.push(onFulfilled);
		}
		if(typeof onRejected == 'function'){
			this.rejectedCallbacks.push(onRejected);
		}
	}
}

2、手写Promise.all()

(1)状态变化

只有p1、p2、p3...的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
只要p1、p2、p3...之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

(2)代码如下:

_Promise.all = function(ps){
	return new _Promise(function(resolve, reject){
		var values = [];
		for(var i=0, len=ps.length; i<len; i++){
			ps[i].then(value=>{
				values.push(value);
				if(values.length == len) resolve(values);
			}, reason=>{
				reject(reason);
			});
		}
	});
}

3、手写Promise.race()

(1)状态变化

只要p1、p2、p3...之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

(2)代码如下:

_Promise.race = function(ps){
	return new Promise(function(resolve, reject){
		for(var i=0, len=ps.length; i<len; i++){
			ps[i].then(v=>{
				resolve(v);
			},r=>{
				reject(r);
			});
		}
	});
}

链表————js实现链表类及其方法

1、单向链表

image
如图,链表是由一串节点连接而成,节点存储了值和指向下一个节点的指针,所以js实现链表需要以下步骤:

(1)建立一个节点

function Node(value){
	this.value=value;
	this.next=null;
}

(2)新建链表类,初始带有head属性

function LinkedList(){
	this.head=null;
	this.length=0;//长度
}

(3)为链表添加方法:打印、增、查、插入、翻转

a、打印

LinkedList.prototype.print=function(){
	var current=this.head;
	var result=[];
	while(current){
		result.push(current.value);
		result.push('->');
		current=current.next;
	}
	result.pop();
	console.log(...result);
}

效果:
print

b、增

LinkedList.prototype.push=function(value){
	var addNode=new Node(value);
	if(this.head==null){
		this.head=addNode;
	}else{
		//找到最后一个节点
		var current=this.head;
		while(current.next){
			current=current.next;
		}
		current.next=addNode;
	}
	//节点长度更新
	this.length++;
}

c、查

//查,返回指定位置的节点
LinkedList.prototype.getNodeAt=function(pos){
	if(this.head==null || pos>this.length-1)return null;
	var current=this.head;
	//记录当前节点的位置
	var count=0;
	while(current){
		current=current.next;
		count++;
		if(count==pos)return current;
	}
}

d、插入

//插入。在当前位置之前插入节点
LinkedList.prototype.insert=function(pos,value){
	if(pos>this.length-1)return new Error('out of length');
	var insertNode=new Node(value);
        //若是表头
	if(pos==0){
		var mid=this.head;
		this.head=insertNode;
		insertNode.next=mid;
	}else{
		//找到前一个的节点,操作其指针做插入
		var currentNode=this.getNodeAt(pos-1);
		var mid=currentNode.next;
		currentNode.next=insertNode;
		insertNode.next=mid;
	}
	this.length++;
}

e、反转

LinkedList.prototype.reverse=function(){
	var old=null;//老节点
	var current=this.head;//当前节点
	var newOne=current.next;
	while(current){
		current.next=old;
		old=current;
		current=newOne;
		if(current){
			newOne=current.next;
		}
	}
	this.head=old;
}

或用数组先提取再反转实现。
效果:
image

快速排序

快速排序

1、原理

快排原理(从小到大):在数组中找到一个对照值,用2个指针分别从左向右和从右向左遍历数组,小的数放数组左边,大的数放数组右边,用递归遍历数遍直到排序完成为止。

2、代码

function quick(arr,left,right){
	/*
		arr:要排序的数组
		left:数组左起指针
		right:数组右起指针
	*/
	if(arr.length==1)return;
	var contrast=arr[Math.floor((left+right)/2)];//对照值
	var i=left,j=right;
	while(i<=j){
		//左指针找到比contrast大的值
		while(arr[i]<contrast){
			i++;
		}
		//右指针找到比contrast小的值
		while(arr[j]>contrast){
			j--;
		}
		//找到之后进行交互左右2个值的位置
		if(i<=j){
			var swap=arr[i];
			arr[i]=arr[j];
			arr[j]=swap;
			//从下一个位置接着查找
			i++;
			j--;
		}
	}
	//次轮遍历结束,找到中间值的位置i,一分为二递归排序
	if(i-1>left){
		quick(arr,left,i-1);
	}
	if(i<right){
		quick(arr,i,right);
	}
}

3、时间复杂度

(1)最好情况:每次都从中间一分为二

数组需要分k次才能分为n个长度为1的小数组,即2^k=n,k=logn。每分一次,左右指针需要遍历数组一遍,即读取n下。
因此时间复杂度=O(n*k)=O(nlogn)。

(2)最坏情况:每次都在数组的头部或尾部一分为二

数组需要分n次才能分为n个长度为1的小数组。每次读取操作n次。
因此时间复杂度=O(n^2)。

Web安全防范——CSRF与XSS

一、CSRF(Cross—Site Request Forgery)跨站点请求伪造

1、原理

恶意网站通过携带用户在某个网站的cookie来发出恶意请求。
image
  CSRF攻击成功需要一定的条件的,如上图所示。
  首先,用户必须首先登录网址A,然后网址A基于cookie验证,将cookie发送到用户浏览器保存。
  然后在用户未登出(cookie未清除)网址A的情况下,用户访问了黑客网址B。此时网站B的网页通过用户浏览器发送了一条攻击网址A的请求,如利用img或script的src进行跨域技术。由于用户浏览器保存着网站A的cookie,所以黑客网页B发送的攻击请求会携带用户浏览器中网站A的cookie。
  最后,如果服务器不对用户请求进行验证,那么就会认为攻击请求是用户发来的合法请求,就会执行攻击请求。

2、防御

(1)验证HTTP Referer字段,同源检测

  Referer记录发送请求的 页面的URL,因此CSRF攻击请求的Referer指向黑客的网址,只要服务器发现请求页面的网站不是本站的网站,就不予以响应。
缺点

  • 不是万无一失的。一些浏览器可以更改Referer的值,如IE6
  • 涉及用户隐私问题

(2)不用cookie,使用token

  • CSRFToken。第一,将token存储在服务器的session中。第二,将token添加到自定义请求头中
  • 双重token。第一,在cookie存储token;第二,提取cookie的token,以url参数形式携带。

(3)使用图形验证码

  服务器产生验证码和其对应的图形验证码,然后把图像发送给用户浏览器,浏览器进行人眼识别后将请求携带验证码发送给服务器,服务器对比用户识别的验证码和服务器的验证码,若一致则验证成功,允许响应。
缺点:影响用户体验

(4)在cookie里存放token + 发送前修改cookie

  • 基于csrf只能携带,不能读取操作的原理。
    在每次请求前,将cookie的token取出,在后面添加一个字符串,再重新添加到cookie里。比如'abcd4'。发送出去后再将cookie修改为原来的token。后端验证token时,若token未变长,则匹配失败;否则将token的添加内容去除,再解密。如'abcd4'的末位是4,说明之前添加了4个字符,就将这些字符剔除。

二、XSS(Cross Site Scripting)跨站脚本攻击

1、原理

  通过对网页注入可执行代码且成功地被浏览器执行来攻击。根据XSS攻击的效果可以分为2种类型。

(1)反射型(非持久型)

  利用浏览器实现。攻击者发送给浏览器一个恶意网站链接,一旦用户点击浏览器就会加载恶意网站,执行js脚本等恶意代码。

(2)存储型

  指攻击者把恶意代码存储到服务器。
例子:在留言板上、评论处、个人资料填写、等地方,攻击者利用script标签写入一段窃取cookie的恶意JavaScript代码,这段恶意代码会保存到数据库中。当其他用户浏览写入代码的页面时,网站从数据库中读取恶意代码显示到网页中被浏览器执行,导致用户的cookie被窃取攻击者无需受害者密码既可以登陆账户。所以也被成为持久性xss,持久性xss比反射性xss危害性大得多。

2、预防

(1)Set-Cookie中设置HttpOnly

Set-Cookie:Http-Only;
  让cookie只能通过http请求读取,防止被页面脚本读取。

(2)过滤页面输入字符

  前端过滤输入字符串中的<iframe>, <script>,a标签等,因为在前端执行XSS代码可能将cookie发送出去。

(3)对显示到页面的字符串转义

  createTextNode不就解析标签,可实现转义

(4)输入长度限制

  对预期的输入长度做限制。

(5)内容安全策略CSP

服务端设置html的http头:Content-Security-Policy: default-src ;设置允许加载的资源。参考mdn
或在meta标签里写http-equiv=“Content-Security-Policy”和content=“default-src

减少DNS查找——缩短IP查找时间

  • 使用Keep-Alive持久连接——用一个持久连接避免多次请求进行DNS域名解析(其实归功于于http1.1);
  • 使用较少的主机放置组件,从而可以减少域名的数量,减少DNS查找时间。

5、CSS3新特性

一、新增选择器

结构伪类选择器

  • :root——根选择器
  • :nth-child(an+b)——子元素选择器。下标是a的倍数,偏移b个的子元素,n从0计数。odd奇数,even偶数。
  • :nth-last-child(n)——倒数第n个
  • :last-child——最后一个
  • :only-child——属于其父元素的唯一子元素的那个
  • ...

二、新增属性

边框

  • border-image——边框图片
  • border-radius——边框圆角
  • border-shadow——阴影

背景

  • background-image——背景图片
  • background-size——尺寸
  • background-origin——位置区域
  • background-clip——裁剪属性

渐变色

  • linear-gradient(direction, color-start1, color-stop1)——线性渐变
  • radial-gradient(shape size at position, start-color, ..., last-color)——由中心向周围渐变

文本效果

  • text-shadow
  • box-shadow
  • text-overflow: clip/ellipsis/string——文本溢出显示形式

字体

@font-face{...}

三、重要属性

1、2D、3D转换——transform

  • 位移——translate(x,y)、translateX(n)、translateY(n)、translateZ(z)、translate3d(x,y,z)
  • 缩放——scale(x,y)、scaleX(n)、scaleY(n)、scaleZ(z)、scale3d(x,y,z)
  • 旋转——rotate(angle)、rotateX(angle)、rotateY(angle)、rotateZ(angle)、rotate3d(x,y,z,angle)
  • 倾斜——skew(x-angle,y-angle)、skewX(angle)、skewY(angle)

2、过渡动作——transition

切换样式/或伪类时触发。

  • transition-property ——规定设置过渡效果的 CSS 属性的名称。
  • transition-duration——规定完成过渡效果需要多少秒或毫秒。
  • transition-timing-function——规定速度效果的速度曲线。
  • transition-delay——定义过渡效果何时开始。

transform不会触发回流。height、margin、padding、offsetTop、scrollTop、clientTop、getComputedStyle()、getBoundingClientRect等会触发回流

贝塞尔曲线:cubic-bezier(n,n,n,n)

运动速度就是曲线的斜率大小
曲线网站

3、动画——@Keyframes和animation

animation是以下属性的缩写:

  • animation-name——规定需要绑定到选择器的 keyframe 名称。。
  • animation-duration——规定完成动画所花费的时间,以秒或毫秒计。
  • animation-timing-function——规定动画的速度曲线。
  • animation-delay——规定在动画开始之前的延迟。
  • animation-iteration-count——规定动画应该播放的次数。
  • animation-direction——规定是否应该轮流反向播放动画

@ keyframes语法如下:

@ keyframes animationname {
	keyframes-selector {
		property: style;
	}
}
  • animationname——自定义的动画名
  • keyframes-selector——"from" 和 "to",等价于 0% 和 100%。或百分比
  • property——元素属性
  • style——元素样式

能触发GPU加深的属性有:transform、opacity、filter

四、弹性盒子——flex

display:flex;——采用 Flex 布局的元素,称为 flex 容器。它的所有子元素自动成为容器成员,称为 flex 项目。

1、父元素属性

flex-flow: flex-direction || flex-wrap;

  • flex-direction: row | row-reverse | column | column-reverse;——主轴方向(项目排列方向)

  • flex-wrap: nowrap(不换行) | wrap(换行) | wrap-reverse(换行,倒序);——超出换行

  • justify-content: flex-start | flex-end | center | space-between | space-around;——主轴对齐方式(横轴)

  • align-items: flex-start | flex-end | center | baseline | stretch;——纵轴对齐方式

  • align-content——堆叠伸缩行的对齐方式

2、子元素属性

  • order——在父元素里的排序,小的在前
  • flex: none | flex-grow |flex-shrink|flex-basis
  • flex-grow——根据弹性盒子元素所设置的扩展因子作为比率来分配剩余空间。
    若盒子的basis总和小于父盒子宽度,则剩余的宽度按grow比例分配。
  • flex-shrink——根据弹性盒子元素所设置的收缩因子作为比率来收缩空间
    若盒子的basis总和大于父盒子宽度,则按子盒子shrink比例压缩,例如1:1则子盒子等比例压缩。
  • flex-basis——设置或检索弹性盒伸缩基准值

五、多媒体查询

1、语法

@media mediatype and | not | only (media feature){
	CSS-Code;
}

2、属性值

(1)mediatype(媒体类型)

  • all——所有设备
  • print——打印机
  • screen——电脑、平板、手机屏幕

(2)媒体特征

  • height——设备可见区域高度
  • width——设备可见区域宽度
  • max-height——可见区域最大高度
  • max-width——最大宽度
  • min-height——最小高度
  • min-width——最小宽度

六、响应式设计——viewport

在meta中用法如下:
<meta name="viewport" content="width=device-width, initial-scale=1.0">

属性有以下几种:

  • width——viewport(视口)宽度
  • initial-scale——初始缩放比例。页面第一次加载的缩放比例
  • maximum-scale——允许用户缩放到的最大比例
  • minimum-scale——允许用户缩放到的最小比例
  • user-scalable——用户是否可以手动缩放

七、新单位

  • vm——视口宽度的百分比,1vm是宽度的百分之一
  • vh——视口高度的百分比
  • vmin——vw和vh当中最小的
  • vmax——vw和vh当中最大的

栈和队列——2个栈实现队列

1、栈--先进后出

入栈--push();
出栈--pop();

例子——十进制转二进制

compute(n){
	var res;
	var stack = [];
	while(n){
		stack.push(n % 2)
		n = Math.floor(n/2)
	}
	return stack.reverse().join('')
}

2、队列--先进先出

单端队列
入队列--push();
出队列--shift();
双端队列 可以从头或尾加入,从头或尾移除
(1)入队列
头--unshift();
尾--push();
(2)出队列
头--shift();
尾--pop();

例子

1、2个栈实现队列

image

var stack1=[];//入队列
var stack2=[];//出队列
function push(node){
    stack1.push(node);
}
function pop(){
    //都是空队列则返回
    if(stack1.length==0 && stack2.length==0)return null;
    //若入队列不为空,出队列为空。将入队列值加入出队列
    if(stack2.length==0){
        //从尾向头,将入队列的数移到出队列
        for(var i=0;i<stack1.length;i++){
            stack2.push(stack1.pop());
            i--;//stack1大小花生了变化
        }
    }
    return stack2.pop();
}

2、击鼓传花

// num——击鼓次数
compute(persons, num){
	var queue = [];
	var out = [];
	for(var i=0,len=persons.length;i<len;i++){
		queue.push(persons[i])
	}
	// 队头作为传花
	while(queue.length>1){
		for(var i=0;i<num;i++){
			queue.push(queue.shift());
		}
		out.push(queue.shift())
	}
	for(var i=0;i<out.length;i++){
		console.log(out[i],'被淘汰')
	}
	return queue[0]
}

3、优先队列

(1)概念

  队列中的每项都有优先级,优先级高的会先出队列

(2)js实现——节点类 + 数组(数组出队列比较慢,用链表则会快点,不过链表需要额外变量记录其前一个节点)

function PriorityQueue(){
	this.items = [];
}
PriorityQueue.prototype.dequeue = function(){
	var highest = 0;
	for(var i=0;i<this.items.length;i++){
		if(this.items[i].order > this.items[highest].order){
			highest = i;
		}
	}
	var resArr = this.items.splice(highest, 1);
	return resArr[0];
}

缓存组件——Last-Modified,Expires,Cache-Control和ETag

一、缓存

  缓存包括协商缓存和强缓存,介绍如下:

1、协商缓存——Last-Modified和ETag

  协商缓存是浏览器每次请求组件都要询问服务器组件是否更新,服务器判断后做不同的响应,流程如下:
image
下面对协商缓存使用的头做详细介绍。

(1)Last-Modified

  • Last-Modified——记录服务器最后更新时间
  • If-Modified-Since——记录浏览器缓存的更新时间

  浏览器通过If-Modified-Since与服务器的Last-Modified进行对比,检测是否更新,服务器做不同的响应,流程如下:

  • step1. 浏览器第一次请求时,服务器的响应状态码是200。若在响应头加入Last-Modified,表示资源的最后修改时间,其格式如下:
    image
  • step2. 浏览器收到A响应后,会将这个资源缓存在浏览器中。当浏览器下次再请求这个资源时,会自动在请求头部添加If-Modified-Since,格式同上,表示浏览器缓存中的资源的Last-Modified后的时间。
  • step3. 服务器收到B浏览器的请求时,通过If-Modified-Since的值对比资源的Last-Modified:
    若是一样,说明资源未变化,服务器返回304 Not Modified状态码。这时浏览器从缓存中获取资源,缩短了从服务器下载资源的响应时间。
    若是不一样,说明服务器资源更改了,服务器返回200状态码,并把更新后的资源发送给浏览器。

(2)ETag(Entity Tag)——实体标签

  • ETag——服务器中组件的标识符
  • If-None-Match——浏览器中缓存的组件的ETag标识符

  类似于Last-Modified,不同的是服务器用字符串作为ETag头让浏览器缓存,浏览器请求时用If-Nono-Match来携带记录的ETag标识符。服务器收到请求时若验证相同,就响应304,否则响应200和更新的组件。

缺点

  • 每台服务器产生的ETag都不一样,服务器集群时大部分ETag将失效,使性能下降

2、强缓存——Expires和Cache-Control

  强缓存是浏览器每次请求组件都要查询缓存日期是否过期,然后做不同的处理,流程如下:
image
下面对强缓存使用的头做详细介绍。

(1)Expires

  • Expires——服务器响应头,格式与Last-Modified一样,不过这里指的是具体的过期时间,这个时间之后就失效

使用流程

  • step1. 服务器响应组件时携带Expires头,浏览器将组件缓存并记录Expires的有效日期
  • step2. 浏览器再次请求相同组件时会先验证Expires的有效期是否过期,若没过期则使用缓存,否则向服务器发送新的http请求

缺点

  • 要求浏览器端和服务器的时钟严格同步,若是不同步,那么误差将会很大。(课使用mod_expires Apache模块解决)

(2)Cache-Control

  • Cache-Control:max-age=缓存时长——服务器设置组件在浏览器的缓存时长,通过max-age=xxx设置。是相对于当前时间之后多久。
  • Cache-Control:max-age=剩余时长——过期时,请求携带

使用流程
  类似于Expires,不同的是使用Cache-Control来记录缓存时长,是相对值。

缺点

  • HTTP1.1引入的,HTTP1.0无法使用

2、合理使用缓存

image
ETag和Last-Modified同时存在的话,都会发送,但大多数浏览器认为ETag的优先级更高。

二、使Ajax可缓存

  使Ajax请求具有长久的Expires头。

6、应用

一、面经问题

问题1、CSS3动画,GPU加速,导致独立图层生成的属性?

  CSS3的transform不会触发回流,会产生独立图层使用GPU加速。还有opacity,filter(滤镜)会触发GPU加速

问题2、首页白屏优化

  样式放头部,异步下载,脚本放底部。

问题3、求body高度

html

<body>
	<div>1</div>
	<div>2</div>
	<div>3</div>
	<div>4</div>
	<div>5</div>
</body>

style

	html{
		padding: 0;
		margin: 0;
	}
	div{
		height: 10px;
		border: 10px;
		padding: 10px;
		margin: 10px;
		border: 10px;
		background-color: green;
	}

  border无效;body没有padding和border,导致上下2margin变为body的margin;div上下margin重叠了4个。所以margin总共减少了6个。因此:
    body高度 = 1055 - 10*6 = 190

如果是border:10px solid red

    body高度 = 1057 - 10*6 = 290

问题4、base64压缩图片时,如果图片大小1k,没有优化效果

  base64增大css体积,css会阻塞页面渲染。

问题5、CSS布局介绍?

布局 实现方式 优点 缺点
静态布局 尺寸一律用px写死 设计简单、无兼容性问题。最适合PC端 无法根据屏幕尺寸做调整
自适应布局 分别为不同屏幕尺寸设计不同静态布局,通过media查询加载不同代码 适配范围内的设备体验最好 一个网页需要设计多种布局,开发工作量大
流式布局(传统) 宽度用百分比做自适应,高度、文字大小用px写死 整体布局不变 视口很小会让宽度很窄但是高度不变,视觉体验差
弹性布局 用rem、em相对单位设置尺寸,保持宽高比例;用media查询控制根元素的字体大小 保持尺寸比例。最适合移动端 rem相对字体尺寸计算麻烦。chrome最小字体大小是12px。
响应式布局 流式布局+弹性布局+media查询 适合pc和移动端兼容 media查询有限。工作量大。chrome浏览器的最小字体问题

问题6、display:inline-block元素之间存在间隙,原因及解决方案?

原因:换行符和空格在文本行内都会形成间隙
解决:

  • 父元素font-size:0,子元素再重新设置
  • margin负值
  • 父:table-cell; word-spacing:-em
  • 写在同一行

二、代码实现

1、流式布局系列

###(1)一侧固定,另一侧自适应
方法:

  • 左侧浮动脱离文档流,右侧形成BFC

    左侧浮动,右侧overflow:hidden/scroll/auto

  • flex

  • 父子嵌套。父元素margin-left或padding-left,子元素margin-left负值。

  • absolute绝对定位,脱离文档流。

(2)两侧固定,中间自适应

方法:

  • flex
  • absolute绝对定位,脱离文档流,形成BFC。
  • 先左右浮动,再让中间形成BFC,或用margin/padding
  • 双飞翼或圣杯布局,也是使用float

(3)瀑布流布局——等宽不等高

flex实现,父元素flex,设置flex-wrap: wrap;换行。

2、垂直居中系列(父元素宽高未知)

(1)居中元素不定宽高

  • 父relative,子absolute + transform
  • 父flex,justify-content:center, align-items:center
    方法3、

(2)居中元素定宽高

以上方法都可用。额外方法:

  • 可用margin替换transform
  • left/right/top/bottom全是0,再用margin:auto设置

(3)文字和图片并排垂直居中

父元素:line-height = 高度;text-align:center;
img:vertical-align: text-bottom;

3、CSS绘图系列

(1)CSS实现宽高等比例自适应矩形

padding实现

(2)手写css画三角

	<style>
	.f{
		width: 0;
		border: 20px solid transparent;
		border-left-color: red;
	}
</style>

(3)用css写个气泡

伪元素 + absolute

html

<div class="f"></div>

css

<style>
	.f{
		width: 100px;
		height: 40px;
		border-radius: 20px;
		background-color: pink;
		position: relative;
	}
	.f::after{
		content: '';
		display: inline-block;
		border: 6px solid rgba(0,0,0,0);
		border-left:14px solid pink;
		position: absolute;
		right: 0;
		transform: translateX(12px) rotate(-20deg);
	}
</style>

4、过渡、动画效果系列

(1)实现一个渐入渐出效果

html

<div id="dd">
	<div class="ss"></div>
</div>

css

<style>
	.ss{
		width: 10px;
		height: 10px;
		border-radius: 50%;
		background-color: red;
		display: inline-block;
	}
	#dd{
		background-color: yellow;
	}
	#dd:hover .ss{
		transform: translateX(400px);
		transition: transform 1s ease-in-out;
	}
</style>

(2)css动画实现一个点加速中间再减速到右边

<style>
	.ss{
		width: 10px;
		height: 10px;
		border-radius: 50%;
		background-color: red;
		display: inline-block;
		animation: move 1s ease-in-out infinite alternate;
	}
	@keyframes move{
		0{
			transform: translate(0,0);
		}
		50%{
			transform: translate(200px,0);
		}
		100%{
			transform: translate(400px,0);
		}
	}
</style>

(3)css动画有哪些实现方式

svg 和 animination 、过渡transition

5、超出文字转省略号

white-space: nowrap; // 不换行
overflow: hidden;  // 不显示
text-overflow: ellipsis;  // 溢出部分用省略号

注意: word-wrap: wrap | nowrap。是否允许长单词换行

二叉树类的创建及前、中、后续遍历

一、创建二叉树类(二叉搜索树)

//二叉搜索树
function BinarySearchTree(){
	this.root = null; //根节点
}
//插入节点
BinarySearchTree.prototype.insert = function(key){
	//插入根节点
	if(this.root == null){
		this.root = new Node(key);
	}else{
		insertNode(this.root,key);
	}
	//节点类
	function Node(key){
		this.key = key;
		this.left = null;
		this.right = null;
	}
	//在某个节点插入值。小的插在左边,大的插在右边
	function insertNode(node,key){
		//小的插在左边
		if(key < node.key){
			if(node.left == null){
				node.left = new Node(key);
			}else{
				insertNode(node.left,key);
			}
		}else{
			//插在右边
			if(node.right == null){
				node.right = new Node(key);
			}else{
				insertNode(node.right,key);
			}
		}
	}
}

二、前序遍历

//前序遍历
function preOrder(tree){
	var node;
	if('root' in tree){
		node = tree.root;
	}else{
		node = tree;
	}
	//父节点
	console.log(node.key);
	//左节点
	if(node.left!=null){
		preOrder(node.left);
	}
	//右节点
	if(node.right!=null){
		preOrder(node.right);
	}
}

三、中序遍历

//中序遍历
function midOrder(tree){
	var node;
	if('root' in tree){
		node = tree.root;
	}else{
		node = tree;
	}
	//左节点
	if(node.left!=null){
		midOrder(node.left);
	}
	//父节点
	console.log(node.key);
	//右节点
	if(node.right!=null){
		midOrder(node.right);
	}
}

四、后续遍历

//后序遍历
function backOrder(tree){
	var node;
	if('root' in tree){
		node = tree.root;
	}else{
		node = tree;
	}
	//左节点
	if(node.left!=null){
		backOrder(node.left);
	}
	//右节点
	if(node.right!=null){
		backOrder(node.right);
	}
	//父节点
	console.log(node.key);
}

同源策略及跨域

一、同源策略

概念:如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。出于安全考虑,同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。

二、跨域方法

1、CORS(跨域资源共享)

  • 请求头——Origin:请求页面URL
  • 响应头——Access-Control-Allow-Origin:请求页面URL(或*)
    注意:请求和响应都不包含cookie,因为cookie是同源策略。

2、图片Ping

(1)方法

  动态创建图片,用img的src属性跨域请求资源。

ar img = new Image();
img.onload=img.onerror = function(){
	console.log("done");
}
img.src="http://statics.scnu.edu.cn/statics/images/yjsy/ban1.jpg";

(2)缺点

  只能发送get请求;单向通行;无法获得响应文本,一般是图片。

3、JSONP

(1)方法

  把回调函数作为URL的查询字符串向跨域网站发送请求。跨域网站将数据做为回调函数的参数响应。
//浏览器

function responseFn(response){
	// var jsonObj = ISON.parse(response);
	console.log(response);
}
var script = document.createElement("script");
script.src = "http://localhost:3000/data.json?callback=responseFn";
document.body.insertBefore(script,document.body.firstChild);

//跨域网站后端,node

app.get('/data.json',function(req,res){
	var data = {'name': 'jifeng', 'company': 'taobao'};
	if(req.query.callback){
		var str = req.query.callback + '('+JSON.stringify(data)+')';
		res.send(str);
	}
});

(2)缺点

  • 从其他域加载代码,其他域若发送恶意代码将会不安全
  • 很难确定是否请求失败。onerror事件很多浏览器不支持。

4、服务器代理

  浏览器有同源策略的限制,服务器并没有。可以由服务器请求跨域资源,再发送给浏览器。

5、iframe跨域

(1)方法

  • 方法一——父窗口通过iframe.contentWindow.document访问iframe窗口的dom文档
    index.html
<body>
	原始
	<iframe src="iframe.html" frameborder="0" id="myIFrame"></iframe>
	<script>
		document.domain='localhost';
		var iframe = document.getElementById("myIFrame");
		iframe.onload = function(){
			var doc = iframe.contentWindow.document;
			console.log(doc.getElementById('test').innerHTML);//'xiaohuochai'
			console.log(document.cookie);
		}
	</script>
</body>

iframe.html

<body>
	<div id="test">kuayu</div>
	跨域跨域跨域
	<script>
		document.domain='localhost';
		document.cookie = 'name=match';
	</script>
</body>
  • 方法二——window.postMessage() 发送消息,message事件监听消息
      对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议、端口号、以及主机时,这两个脚本才能用window.postMessage()相互通信。

index.html

<body>
原始
<iframe src="iframe.html" frameborder="0" id="myIFrame"></iframe>
<script>
	document.domain='localhost';
	var iframe = document.getElementById("myIFrame");
	iframe.onload = function(){
		var win = iframe.contentWindow;
		win.postMessage('我是跨域发送者','http://localhost:3000/iframe.html');
	}
</script>
</body>

iframe.html

<script>
	 window.onmessage = function(e) {
		e = e || event; // 获取事件对象
		console.log('收到消息:',e.data); // 通过 data 属性得到发送来的消息
	}
</script>

(2)缺点

  只能在主域相同的情况下跨域。

6、Web Sockets(见6.4节 SSE与Web Sockets)

Ajax原理

一、Ajax底层原理(概述)

  Ajax使用XMLHTTPRequest对象来实现http请求。XMLHTTPRequest对象是浏览器提供的一个接口,内部包装了HTTP等应用层协议,在逻辑最上层增加同源策略限制。
  Ajax主要内容是异步、回调和线程。Ajax会让浏览器新开一个线程去执行HTTP请求,得到请求结果前可以继续做其他事情(异步),得到结果后可以调用回调函数做后续操作。
  由于Ajax是基于HTTP协议的,所以浏览器会限制HTTP请求的数量(同个域名下)。

二、Ajax的使用

1、XMLHTTPRequest对象的属性

image
注意点:

2、XMLHTTPRequest对象的方法

(1)open(method, url, Boolean)

  • method——get(查询信息)、post(向服务器发送被保存的数据)
  • url——请求的资源位置
  • Boolean——true(异步)、false(同步)

(2)send()——发送特定请求,一般用send(null)

3、浏览器缓存问题——避免每次都读取一样的缓存数据

  • (1)发送请求前加上 anyAjaxObj.setRequestHeader("If-Modified-Since","0")
  • (2)发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache")
  • (3)在URL后面加上一个随机数: "fresh=" + Math.random()
  • (4)在URL后面加上时间搓:"nowtime=" + new Date().getTime()

4、代码示例

function send(){
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function(){
		if(xhr.readyState == 4){
			if(xhr.status == 200){
				console.log(xhr.responseText);
			}
		}
	}
	xhr.open('get','data.json',true);
	//避免请求缓存
	// xhr.setRequestHeader("If-Modified-Since","0");
	xhr.send(null);
}

三、并发控制

// 实现带并发限制的异步调度器
// 异步任务函数
const fetchUser = (name, delay) => {
  return () => new Promise((resolve) => {
    setTimeout(() => {
        console.log(name);
        resolve();
    }, delay);
  })
}

class Scheduler {
	constructor(limit) {
		this.limit = limit
		this.count = 0
		this.queue = []
	}
	
	add(callback) {
		if (this.count === this.limit) {
			this.queue.push(callback)
			return
		}
		this.count++
		callback().finally(() => {
			this.count--
			if (this.queue.length === 0) return
			const firstcb = this.queue.shift()
			this.add(firstcb)
		})
	}
}

const scheduler = new Scheduler(2); // 控制并发数 2
scheduler.add(fetchUser('A', 2000));
scheduler.add(fetchUser('B', 1000));
scheduler.add(fetchUser('C', 800));
scheduler.add(fetchUser('D', 500));

// 预期:B C A D

计算机网络的体系结构及其各层的作用

一、计算机网络的体系结构

image
OSI七层模型

  • 应用层——协议
  • 表示层——语法、语义、加密、解密
  • 会话层——会话管理
  • 运输层——tcp/udp
  • 网络层——ip
  • 数据链路层——IP数据报组成帧
  • 物理层——物理线路

tcp/ip四层模型

  • 应用层——http协议等
  • 运输层——TCP、UDP
  • 网际层IP
  • 网络接口层

1、上图(a)——OSI(国标组织)理论上的7层协议

2、上图(b)——实际应用广泛的TCP/IP的4层协议

3、上图(c)——结合理论和实际,便于学习的5层协议

(1)将TCP/IP四层协议的网络接口层分为:数据链路层+物理层
(2)将网际层改为网络层
对前端来说,其中最重要的是网络层、传输层和应用层。

二、5层协议的各层作用

计算机网络

1、物理层

(1)数据传输单位:比特
(2)作用:将上层的数据以bit的形式(如1010...0110)通过光纤、电缆、无线电等物理媒体进行传递(或接收下层数据,进行反过程)

2、数据链路层

(1)数据传输单位:帧
(2)作用:将上层的数据封装成帧(添加控制信息),用于通信控制(或接收下层数据,进行反过程)

3、网络层

(1)数据传输单位:IP数据报
(2)作用:将上层的数据封装成IP数据报(添加IP地址等信息),用于主机地址查找(或接收下层数据,进行反过程)

4、运输层

(1)数据传输单位:数据段
(2)作用:将上层的数据封装成数据段(添加端口号等信息),用于应用进程的端口查找(或接收下层数据,进行反过程)

5、应用层

(1)数据传输单位:报文
(2)作用:将应用进程的数据封装成报文(添加通信协议等信息),用于传输协议的规定(或接收下层数据,进行反过程)

二、换个顺序更容易记

用户身份识别——session和token

一、用户身份识别

  由于http连接是无状态的,所以只能用于简单的网站浏览。但是存在一些情况需要用户与网站后端进行会话,如网站购物。因此无状态的http连接就无法满足需求了,网站就需要识别用户身份。进行用户身份识别前,需要用户先进行注册(写入数据库)或密码登录(查询数据库),流程大致如下
image

二、方法

  当用户登录或注册后与网站交互,若通过账户和密码进行前后端交互很不方便,每次都需要用户填写,而且还存在安全问题。因此可用session和token方法解决。
注意:用户登录时可以给session或token设置超时时间,时间过后自动登出。或在登出时清除cookie里的sessionId或token。

2、session身份识别

(1)原理

  session方法就是在用户登录时,生成一个会话,服务器把用户身份信息存储在session里,生成一个sessionId,然后把sessionId发送给浏览器,浏览器把sessionId存在cookie或其他地方,以后每次发送请求都带上这个sessionId,然后服务器通过sessionId查到对应的用户信息,对数据库的用户数据进行操作。
image

(2)缺点

  • 用户离开网站或者服务器重启session会被销毁
  • 用多个服务器做负载均衡时,服务器的session只存在于一台服务器上,需要进行拷贝才行

3、token身份识别

(1)原理

  与session类似,但是token是将用户设备号/硬件地址作为用户id,再通过加密算法和密钥生成签名,最后将用户id和签名作为token发送给浏览器(http自定义头部)。浏览器将token保存在storage等其他地方。token的签名生成方式如下:
sign
  浏览器之后与服务器进行会话时都会将携带用户id和签名的token发给服务器,服务器通过加密算法和密钥再对用户id生成一个签名,若是生成的签名与请求携带的签名一致,则说明请求方的身份是正确的。签名验证方法如下:
yz
  token验证成功之后,服务器就从token获取了用户id,然后就可以去操作数据库的用户数据。
image

(2)特点

  • 无状态、可扩展——不用在session里记录,负载均衡没有顾虑
  • 支持移动设备
  • 跨程序调用
  • 安全——预防CSRF

二叉树的深度优先、广度优先遍历

一、深度优先(前序)

image

function deepFirstSearch(node){
	var stack = [];
	var cur = node;
	while(cur){
		console.log(cur.value);
		stack.push(cur);
		cur = cur.left;
	}
	while(stack.length){
		cur = stack.pop();
		if(cur.right){
			cur = cur.right;
			while(cur){
				console.log(cur.value);
				stack.push(cur);
				cur = cur.left;
			}
		}
	}
	
}

二、广度优先

image

function breadthFirstSearch(node){
	var stack = [];
	stack.push(node);
	while(stack.length){
		var cur = stack.shift();
		console.log(cur.value);
		if(cur.left){
			stack.push(cur.left);
		}
		if(cur.right){
			stack.push(cur.right);
		}
	}
}

避免重定向

一、重定向介绍

1、概念

  重定向用于将用户从一个URL重新路由到另一个URL,浏览器收到一个包含新URL的Location头,然后自动重新请求。因此重定向会增加请求,使网页变慢。

2、状态码

  • 300 Multiple Choices——服务器可执行多种操作
  • 301 Moved Permanently——所请求的URL资源永久转移
  • 302 Moved Temporarily(Found)——临时转移,以后会可能改变
  • 303 See Other——几乎不用
  • 304 Not Modified——协商缓存,请求的资源未修改
  • 305 UseProxy——使用代理才能请求

3、用途

  • 网站URL转移
  • 跟踪流量
  • 记录广告点击
  • 美化URL

二、减少重定向

1、请求的URL添加斜线“/”(域名除外)

  请求的URL若没有“/”,会产生一个重定向,使其增加“/”。域名和浏览器除外,浏览器地址栏会自动添加。

2、网站URL变化时,让域名指向新的主机

3、’跟踪内部网站流量时,用Referer代替重定向

使用外部JS和CSS

  虽然内联JS和CSS会减少首次访问时http请求的数量,但后面再访问时都要重新请求(因为html需要经常更新的),这样就会造成每次请求的文本都比较大。使用外联JS和CSS则可以进行缓存,不用以后每次都重新请求。
  对于搜索网站等则可以结合内联和外联。让主页内联JS和CSS,等主页加载完,通过onlad事件下载后续页面的外联JS和CSS。

JS引擎——事件循环Event Loop

一、概念

  浏览器内核分成两部分:渲染引擎JS引擎。由于JS引擎越来越独立,浏览器内核就倾向于单指渲染引擎。
  渲染引擎采用了单线程。几乎所有操作(除了网络操作)都是在单线程中进行的。浏览器的主线程是事件循环。它是一个无限循环,永远处于接受处理状态,并等待事件(如布局和绘制事件)发生,并进行处理。
  作为浏览器内核的一部分,JS引擎也是单线程,执行原理也是事件循环。

二、JS事件循环——Event Loop

  javascript是单线程的语言,也就是说,同一个时间只能做一件事。

1、相关概念

  了解事件循环需要先了解以下概念:

(1)调用栈

  调用栈是解释器追踪函数执行流的一种机制。每调用一个函数,解释器就会把该函数添加进调用栈并开始执行。如果正在调用栈中执行的函数还调用了其它函数,那么新函数也将会被添加进调用栈,一旦这个函数被调用,便会立即执行。当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。例:

function bar(num){
	console.log(num);
}
function foo(){
	var num = 2;
	console.log(1);
	bar(num);
}
foo();

  在执行foo()时,解释器将foo()加入调用栈,在foo()中又执行了bar(),这时又把bar()加入了调用栈。此时bar()的上下文环境就是foo,bar可以访问foo里定义的变量。输出1,2。当bar()执行完,调用栈会将其弹出,继续执行foo例的剩下部分。当foo()执行完,调用栈就将其弹出,继续执行全局环境下的代码。
  堆栈溢出——当递归的层次很深时,调用栈会压入大量递归函数。由于调用栈分配的空间有限,所以此时堆栈就会溢出,程序报错。(堆是存放对象的,如函数对象等)

(2)同步任务、异步任务

  • 同步任务——同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
  • 异步任务——异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
  • 任务队列——存放异步任务回调函数的队列。队列中的回调函数按先进先出的顺序进入"执行栈"执行。

(3)异步任务中的宏任务、微任务

异步任务分为宏任务和微任务两部分。

  • 宏任务——setTimeout、setInterval等,js引擎会把其调用函数放入宏任务队列
  • 微任务——Promise.then,process.nextTick(node独有)等,js引擎会把其回调函数放入微任务队列

2、事件循环流程

任务

  • (1)JS引擎一直循环检测是否有可执行任务
  • (2)检测到可执行的任务,同步任务会按顺序加入调用栈优先执行
  • (3)同步任务执行完,清空调用栈,将任务队列里的异步任务按先进先执行的顺序加入调用栈执行
  • (4)在执行异步任务时,会优先调用微任务队列里的回调函数进调用栈执行。执行完清空调用栈,执行宏任务队列的回调函数。
  • (5)异步任务都执行完,调用栈清空。js引擎循环检测,若再有任务加入同步任务或异步任务队列中,就按上述优先级执行。

3、例子

function foo(){
	console.log(2);
}
//同步任务
var p = new Promise((resolve,reject)=>{
	console.log(1);
	resolve(3);
});
//异步任务——微任务
p.then((val)=>{
	console.log(val);
});
//异步任务——宏任务
setTimeout(function(){
	console.log(4);
},0);
foo();//同步任务

输出:1,2,3,4
JS引擎会先执行同步任务,按同步任务顺序执行。因此先执行promise,输出1,再执行foo(),输出2;
再执行异步任务中的微任务,p.then(),输出3;
最后执行异步任务中的宏任务,setTimeout,输出4.

压缩组件

一、压缩组件

  压缩可以减小响应组件的大小,缩短响应时间。实际上,值得压缩的内容是文本,脚本和样式表。图片和PDF不能压缩,因为它们已经被压缩过。压缩头如下:

  • Accept-Encoding: gzip,deflate——浏览器请求携带,http1.1版本开始,表示支持压缩
  • Content-Encoding: gzip——服务器选择浏览器支持的1种压缩方法,进行压缩响应

问题:代理服务器转发请求时无法确认浏览器是否支持压缩
解决:为服务器添加Vary: Accept-Ecoding头

二、精简代码

精简:移除不必要的注释、空白字符
混淆:在精简的基础上还会改写代码,使代码更简练,但是可能引起错误。(避免使用)

三、删除重复脚本

重复的脚本若未缓存会造成额外的HTTP请求,还浪费JS的时间。

使用内容发布网络CDN——缩短响应时间

一、原因

  用户请求的响应时间受到距离Web服务器的远近的影响。

二、方法

  使用CDN。CDN是一组分布在多个地理位置的Web服务器。CDN用于将网站的静态内容,如图片、脚本、样式表和Flash放在多个服务器上。
  当用户请求时,CDN可能选择网络阶跃数最少或响应时间最短的服务器。

三、应用场景

前端框架Vue、jQuery库等在线引用。

四、缺点

  • 响应时间可能受到其他网站的影响。因为CDN服务提供商让所有用户共享Web服务器组。
  • 无法直接控制CDN服务器。
  • 隐私安全问题

应用层协议——HTTP/1.0、1.1及2.0

一、(超文本传送)HTTP协议概述

1、作用

  HTTP协议基于运输层的TCP协议,用于浏览器向服务器请求文档,以及服务器把文档传递给浏览器。

2、特点:

  (1)无连接:每次HTTP请求被处理后就断开连接(针对HTTP/1.0)
  (2)无状态:服务器不会记录HTTP请求的客户,同一个客户可以重复请求多次

3、HTTP报文结构

image
(1)请求报文——浏览器向服务器发送请求
(2)响应报文——服务器向浏览器回复
  响应报文的状态行有状态码,表示不同的含义:

  • 1xx——表示通知信息,如请求收到货正在处理
  • 2xx——表示成功。例:200(正常)、202(接收但未处理)
  • 3xx——表示重定向,还需要下一步操作。例:301(请求的资源被永久移动到其他URL上)、304(可使用缓存中的资源)
  • 4xx——表示客户端错误。例:400(请求的语法错误或其他异常)、404(请求的资源不存在)、403(无权限访问、IP被加入黑名单)
  • 5xx——表示服务器错误,如服务器失效无法完成请求。例:500(服务器无法解析代码)、503(服务器停机维护或超载)

4、原理——基于TCP3次握手的HTTP请求过程

    image
(1)HTTP通过TCP第1次握手,请求建立连接
(2)服务器第2次握手,确认连接
(3)TCP第3次握手,浏览器将HTTP请求报文作为此次TCP握手报文的数据发送过去
(4)服务器收到HTTP请求报文,把请求的文档作为响应报文返回。

二、HTTP/1.0、1.1及2.0

1、HTTP/1.0

(1)方式——非持续连接

  请求一次就断开连接,每次连接服务器只处理一个请求。

(2)缺点——连接无法复用

  每次下载文档都要3次握手,就要等待2xRTT的时间(RTT是tcp请求一次到接收响应的等待时间)。服务器要处理大量的客户请求,负担很重。

(3)缓解方法

  浏览器可以打开5-10个并行TCP连接

2、HTTP/1.1

(1)方式——持续连接

  浏览器发送响应后仍保持连接一段时间。只有当前浏览器请求的文档在同一个服务器上,就可以用这条连接传送后续的HTTP请求和响应。
  持续连接有2种方式:

  • 非流水线方式——在同一个连接上,客户收到前一个请求的响应后再发送下一个请求。(若无后续请求则浪费资源)
  • 流水线方式(较好)——连续发送多个请求,服务器连续响应。

(2)缺点——队列头阻塞(实际应用少,浏览器一般是打开多个并行TCP连接解决)

  虽然使用了持续连接,但是发送的请求仍是按顺序到达的,若服务器对其中的一个请求处理时间过长,那么后续的请求都会被阻塞。

3、HTTP/2.0

image
image

(1)方式

  • 二进制传输——在应用层和传输层之间增加一个二进制分帧层,将http请求报文按头部和数据分为头帧和数据帧,并按照二进制编码。帧头部有流标识符,表示它属于哪个流,也就是哪个http请求。帧也可以设置优先级,将重要的请求优先发送。
  • 头部压缩——通讯双方各自缓存一份头部字段表,若报文头部都是一样,则不发送头帧;若头部变化,则将变化的部分以头帧的形式发送,通信双方将改变的头部加入头部字段表。
  • 多路复用——在1个TCP连接上传送多个不同的流(以帧的形式),由于每个帧都具有流标识符,因此不必按序传送和按序处理。接收方会按照流标识符把头帧和数据帧重新组装成报文。

(2)优点

  • 易于解析(二进制传输)
  • 减小传输数据量(头部压缩)
  • 没有队列头阻塞情况(用流传输,按流标识符重新组合,不必顺序发送和处理)
  • 可服务器推送

浏览器内存——内存泄漏

一、概念

  内存泄漏指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。浏览器垃圾回收方法有bug,会产生内存泄露。
  内存泄漏的主要原因是循环引用。对象结束使用后 ,令obj = null,利于解除循环引用。

二、内存泄漏的几种情况

1、全局变量

  全局变量不会被垃圾回收,因此要减少使用全局变量,使用严格模式可避免。

2、闭包中对DOM的引用

  将事件处理函数定义在外部,解除闭包。或者在定义事件处理函数的外部函数中,删除对dom的引用。

3、被遗忘的定时器或者回调

  定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。因此要手动删除定时器和dom。

DOM事件

一、事件流

1、事件冒泡

image
  事件冒泡指事件从触发事件的那个元素接收,然后逐级向上传播到祖先节点,直至Document文档。

2、事件捕获

image
  事件捕获指事件从Document文档节点开始传播,逐级向下传播到触发事件的那个元素。

3、DOM事件流

image
  DOM结合以上2种事件方式,规定了事件流的三个阶段:事件捕获、处于目标阶段和事件冒泡。
1、首先发生的是事件捕获(为截获事件提供了机会);
2、然后是目标接收阶段;
3、最后是冒泡阶段。

二、事件对象

  事件触发时DOM会将event对象传入事件处理程序,event有常用以下属性/方法:

  • currentTarget——事件(捕获或冒泡)传播到的元素
  • target——事件的目标,事件源
  • stopPropagation()——取消事件的进一步捕获或冒泡

三、事件委托(事件代理)

  事件委托利用了事件冒泡原理,就是把某个节点的所有子元素的触发事件都放在当前节点的事件处理程序里处理。避免了为多个子元素都添加事件监听,优化了性能。例:

<ul id='ul'>
	<li id='li1'>点我</li>
	<li id='li2'>点我</li>
	<li id='li3'>点我</li>
	<li id='li4'>点我</li>
	<li id='li5'>点我</li>
</ul>
var ul = document.getElementById('ul');
ul.addEventListener('click',function(event){
	var tarId = event.target.id;
	var order = tarId[tarId.length-1];
	switch(order){
		case '1':
			alert('点了'+order);
			break;
		case '2':
			alert('点了'+order);
			break;
		case '3':
			alert('点了'+order);
			break;
		case '4':
			alert('点了'+order);
			break;
		case '5':
			alert('点了'+order);
			break;
		default:
			alert('点了ul');
	}
});

四、DOM事件模拟

createEvent()方法允许程序员设计一个自定义事件,类似于click事件,由js程序触发。同样此事件支持冒泡,可以添加到节点的事件监听程序中。例:

<div id="div">
	<button id='btn'>点击</button>
</div>
var btn = document.getElementById('btn');
var event = document.createEvent("MouseEvents");
event.initMouseEvent("myEvent",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null);
//冒泡到父元素触发
var div = document.getElementById('div');
div.addEventListener('myEvent',function(){
	console.log('ok');
});
btn.dispatchEvent(event);//触发

对闭包的理解(垃圾回收机制)与应用——防抖、节流、柯里化与bind()

一、闭包及其原理

1、相关概念

  • 执行环境: 定义了变量或函数有权访问的其他数据。有全局执行环境、函数执行环境。
  • 变量对象: 每个执行环境都有一个与之关联的变量对象,保存着环境中定义的所有变量和方法。
  • 作用域链: 环境中的代码执行时,会创建变量对象的一个作用域链。保证对 执行环境有权访问的所有变量和函数 的有序访问。作用域链如下:
    活动对象(当前执行环境的变量对象:arguments,变量) -> 包含环境的变量对象 -> 下一个包含环境的变量对象 ->...-> 全局环境的变量对象

函数执行机制

当执行流进入一个函数时,该函数的环境被推入环境栈中,函数执行完,会将其弹出。其环境被销毁,保存在其中的所有变量和函数也会被销毁。(全局环境除外)

2、闭包

(1)概念

  • 闭包: 是指有权访问另一个函数作用域中的变量的函数。创建闭包的方法是在一个函数内部创建另一个函数。

(2) 原理

  闭包中引用着另一个函数的变量,由于垃圾回收器不会清除被引用变量,虽然那个函数执行后,其作用域链会被清除,但是它的变量对象不会被清除。因此闭包保存的是那个函数的整个变量对象

二、闭包的应用——防抖、节流、柯里化与bind()

(1)防抖——频繁点击多次,执行1次

  顾名思义,就是防止按键抖动(物理按键按下时,其电路接通,会有电压的抖动),但在js中,防抖主要是针对html的按钮。当用户在提交表单时可能会多次点击按键,重复提交多次,因此通过防抖函数可以限制这种情况在1次内。代码如下:

setTimeout的返回值timeoutID是一个正整数,表示定时器的编号。这个值可以传递给clearTimeout()来取消该定时器。需要注意的是setTimeout()和setInterval()共用一个编号池,技术上,clearTimeout()和 clearInterval() 可以互换。但是,为了避免混淆,不要混用取消定时函数。在同一个对象上(一个window或者worker),setTimeout()或者setInterval()在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。

function preventShake(fn,wait){
	var flag=null; 
	return function(){
		clearTimeout(flag);
		flag=setTimeout(fn,wait);
	}
}

(2)节流——频繁点击多次,周期执行一定次数

  在一些页面射击游戏中会用的节流。比如游戏中枪的射击速度限制在10发/s内,但是玩家高频点击射击键可能就会使射速很快。因此通过节流函数可解决这个问题。代码如下:

function throttle(fn,wait){
	var flag=null;
	return function(){
		if(flag)return false;
		flag=setTimeout(function(){
			fn();
			flag=null;
		},wait);
	}
}

(3)柯里化——使代码模块化,减少耦合增强其可维护性

  用于函数解耦,将参数分开输入。

function curry(fn){
	var pre = Array.prototype.slice.call(arguments,1);
	return function(){
		var rest = Array.prototype.slice.call(arguments);
		var args = pre.concat(rest);
		return fn.apply(null,args);
	};
}

bind()绑定函数

function bind(fn,context){
	var pre = Array.prototype.slice.call(arguments,2);
	return function(){
		var rest = Array.prototype.slice.call(arguments);
		var args = pre.concat(rest);
		fn.apply(context,args);
	};
}

集合(Set)、字典(Map)、哈希表(hash)简介

一、集合(Set)——[值,值]

  用对象构造,把值同时作为key和值存储。由于对象的key指向唯一,确保了集合里的值无重复

二、字典(Map)——[键,值]

  以键值对存储。键必须是字符串。键不能重复

三、哈希表/散列表(hash)——字典的一种实现

  哈希表通过哈希函数将key值映射为表地址,再将值存储在映射的地址中。每次检索值时,只要通过哈希函数计算出key对应的地址,即可通过表地址进行访问。(JS中用对象作为表)。

哈希算法(哈希函数)——将key转换为地址数值

  • lose lose哈希算法——将key对应的ASCII码求和
    例:key = 'John',映射后哈希地址=74+111+104+110=399。那么就将值value存在table[399]。
    当访问时,再通过lose lose算法将key映射为399,直接从table[399]访问。时间复杂度为O(1)。

SSE和Web Sockets

一、SSE——EventSource(单向)

  服务端发送事件SSE主要通过EventSource接口实现。EventSource是服务器和页面的单向连接,服务器通过这个连接可以发送任意数量的数据。但服务器响应的MIME类型必须是text/event-stream。它支持短轮询、长轮询和http流,可断开重连。

1、EventSource对象介绍

(1)readyState属性——0:正在连接服务器;1:打开了连接;2:关闭了连接。

(2)事件

  • open——连接建立触发
  • message——从服务器获得响应时触发
  • error——无法建立连接触发

(3)用于http流时,响应格式是纯文本,与下一个数据必须换行。如:

data:foo

data:bar

data:over

2、代码示例

前端:

var source = new EventSource("date");
source.onmessage = function(event){
	var data = event.data;
	console.log(data);
}

后端(nodejs):

const express = require('express');
const app=express();
// app.use(express.static('test'));
app.get('/', (req, res) => res.sendFile('F:/node-project/test/index.html'));

app.get('/date', (req, res) => {
	// while(true){
		res.writeHead(200, {
			"Content-Type":"text/event-stream", 
			// "Cache-Control":"no-cache", 
			// "Connection":"keep-alive"
		});
		setInterval(function(){
			res.write("data: " + (new Date()) + "\n\n");
		},2000);
	// }
});

app.listen(3000,()=>{
	console.log('Server is runing on 3000');
});

二、Web Sockets(双向)

1、简介

  WebSocket是在一个单独的持久的TCP连接上进行通信。在页面创建WebSocket之后,会有1个http请求发送到服务器,与服务器经过TCP3次握手之后,就升级为WebSocket协议。所以使用中需要服务器和浏览器都支持WebSocket协议。

2、特点

  • 可加密成wss
  • 数据量小,可以发送文本或二进制数据
  • 可跨域

3、WebSocket对象

  • 实例:new WebSocket(url)——绝对URL
  • 方法:close()——关闭连接
  • 事件:
    (1)open——成功建立连接触发
    (2)error——发送连接错误触发
    (3)close——连接关闭触发

4、代码示例

前端:

var socket = new WebSocket("ws://localhost:8001/");
socket.onopen = function(){
	socket.send('我是浏览器数据');
}

socket.onmessage = function(event){
	console.log(event.data);
}

后端(nodejs+nodejs-websocket):

const express = require('express');
const app=express();
var ws = require("nodejs-websocket");
// app.use(express.static('test'));
app.get('/', (req, res) => res.sendFile('F:/node-project/test/index.html'));


var server = ws.createServer(function (conn) {
	conn.on("text", function (str) {
		console.log("Received:"+str);
		conn.sendText("我是服务器");
	});
	setInterval(function(){
		conn.sendText((Date.now()).toString());
	},2000);
}).listen(8001);


app.listen(3000,()=>{
	console.log('Server is runing on 3000');
});

三、组合Ajax和SSE实现双向通信(略)

HTML(5)标签

一、HTML文档构成

<!DOCTYPE html>
<html>
  <head>
    <title>标题</title>
  </head>
  <body>
    <---元素标签--->
  </body>
</html>

<!DOCTYPE>——声明文档类型。
  常用的有:html,以支持html5的方式渲染;HTML 4.01等。
  设置为html会让浏览器以标准模式渲染。否则浏览器默认用自己的怪模式渲染,造成页面渲染混乱。
<html></html>——根元素。包含了整个页面的内容。
<head></head>——包含例如面向搜索引擎的搜索关键字(keywords)、页面描述、CSS 样式表和字符编码声明等。
body></body>——包含元素标签,页面呈现。

二、head中的标签

  • <title>——必须。定义文档的标题
  • <base>——通常情况下,浏览器将当前文档的URL作为相对地址。使用base标签让相对地址以base里的URL为基础。
    属性
    href:当前文档共用的相对地址。
    target:页面所有链接都默认以此属性方式打开。
  • <link>——链接外部样式资源,异步下载。
    属性
    href:被链接文档的地址。
    midea:媒体查询。
    rel:规定当前文档与被链接文档之间的关系。stylesheet表示被链接文档是样式表。 dns-prefetch可以预解析dns。
    type:规定被链接文档的类型。
  • <meta>——元信息。
    属性
    charset:声明当前文档所使用的字符编码。
    name::application-name;author;description;generator;keywords。关联到content。
    content:关联到name。
    http-equiv:把content关联到http头部,类似name。
    例如:
    <meta name="keywords" content="HTML, CSS, XML, XHTML, JavaScript">定义网站的关键字,用于搜索引擎。
    <meta name="viewport" content="width=device-width, initial-scale=1.0">针对移动网页优化.
  • <script>——脚本下载。
    属性
    async:立即下载,异步。
    defer:延迟到页面加载。

三、body中的元素标签

基础

  • <h1>-<h6>——标题
  • p——段落(不能嵌套块级元素)
  • <br>——换行
  • <hr>——水平线

格式

  • <abbr>——定义缩写
  • <address>——文档作者信息
  • <b>——粗体
  • <bdo>——文字方向
  • <big>——大号字体
  • <cite>——引用
  • <code>——计算机代码
  • <em>——强调文本
  • <small>——小号字体
  • <strong>——语气强调
  • <sup>——上标
  • <sub>——下标

表单

  • <form>——表单
  • <input>——输入
  • <textarea>——文本输入
  • <button>——按钮
  • <select>——下拉列表
  • <optgroup>——选项区
  • <option>——选项
  • <label>——input的元素标注

列表

  • <ul>——无序列表
  • <ol>——有序列表
  • <li>——列表项
  • <dl>——列表区
  • <dt>——列表标题
  • <dd>——列表描述

表格

  • <table>——表格
  • <caption>——表格标题
  • <th>——行头
  • <tr>——行
  • <td>——列
  • <thead>——表头内容
  • <tbody>——主体内容
  • <tfoot>——脚注

四、最新的HTML标准——HTML5

4.1 新特性

(1)新的语义元素

  • <header>——头
  • <footer>——底部
  • <nav>——导航栏
  • <aside>——侧边栏
  • <section>——节
  • <article>——独立包含的内容
  • <figure>——图片区
  • <figcaption>——图片描述
  • <time>——日期/时间
  • <summary>——details元素的可见标题
  • <detail>——隐藏的细节
  • ...

(2)新的表单控件

  • <datalist>——输入控件的预定义选项
  • <output>——计算结果

(3)强大的图像支持

  • <canvas>——画布,标量图。适合图表、游戏。不支持事件处理。
  • <svg>——矢量图。适合地图。支持事件处理。

(4)多媒体支持

<video><audio>

(5)本地存储

sessionStorage,localStorage

冒泡排序

sort

冒泡排序

1、 原理

  顾名思义,冒泡是气泡从水中向上冒的过程。类似的,冒泡排序是将大的数逐个往后排。在排序数组时(比如从小到大排序),通过遍历数组,对比当前索引和下一个索引的值,将大的值往后排。迭代数次即可完成排序。

2、代码

//冒泡,从小到大
function bubble(arr){
	for(var i = 0; i < arr.length-1; i++){
		var flag = false;
		for(var j = 0; j < arr.length-1-i; j++){
			if(arr[j] > arr[j+1]){
				// 交换
				var swap = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = swap;
				flag = true;
			}
		}
		if(flag == false)return;
	}
}

3、时间复杂度

(1)最好情况
  若数组的初始状态是正序的,一趟扫描即可完成排序。所需的比较次数是n-1,记录移动次数是0。
  所以时间复杂度=O(n);
(2)最坏情况
  若初始数组是反序的,需要进行n-1趟排序。每趟排序要进行n-i次比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置 。所以比较次数=(1+2+3+...+(n-1))*3=3n(n-1)/2。
  所以时间复杂度=O(n^2)

服务端推送Comet——长轮询、短轮询和http流

一、概念

  Comet是服务器向页面推送数据的技术,能够让信息实时地推送到页面,非常适合体育比赛的分数和股票报价。

二、实现方式——2种

1、长轮询

  长轮询是短轮询的一个翻版,介绍如下。

(1) 短轮询

  浏览器定时向服务器发送数据,服务器收到请求就响应,然后断开。如下图:
image
代码:

function sortPoll(){
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function(){
		if(xhr.readyState == 4){
			if(xhr.status == 200){
				console.log(xhr.responseText);
			}
		}
	}
	xhr.open('get','date',true);
	xhr.send(null);
}
setInterval(sortPoll,500);

缺点:服务器要持续处理请求。

(2)长轮询

  页面发起一个到服务器的请求,服务器数据有更新就响应,然后断开,页面发送新的连接请求。若数据没更新,服务器就保持连接一段时间然后再断开(一般情况是2小时内客户端没响应就断开连接)。重复此过程。如下图:
image
代码:

function longPoll(){
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function(){
		if(xhr.readyState == 4){
			if(xhr.status == 200){
				console.log(xhr.responseText);
				longPoll();
			}
		}
	}
	xhr.open('get','date',true);
	xhr.send(null);
}
longPoll();

缺点:服务器要挂起连接,不断查询数据是否更新。

2、http流

概念:不同于轮询要发送多次请求,http流只发送一次请求,然后服务器就一直保持连接,然后周期性的向页面发送数据。当然,若是http流断开还可以重新连接。前端代码如下:

function httpStream(){
	var xhr = new XMLHttpRequest();
	var received;
	xhr.onreadystatechange = function(){
		//接收到部分数据。因为服务器一直发送数据
		if(xhr.readyState == 3){
			//截取最新接收的数据
			var result = xhr.responseText.substring(received);
			received += result.length;
			console.log(result);
		}else if(xhr.readyState == 4){
			console.log('流断开');
		}
	}
	xhr.open('get','date',true);
	xhr.send(null);
}
httpStream();

网络层要点

一、网络层概述

  网络层提供的是数据报服务,因此网络层协议是针对如何传送IP数据报。
  网络层将数据报分组转发,每个分组头都有IP地址,便于分组交换机寻址。网络层主要使用IP协议(IPv4),还有3个配套的协议:
  (1)地址解析协议ARP——将IP地址解析为硬件地址
  硬件地址(物理地址、MAC地址):全球唯一、不可变,固化在网卡上的ROM中
  (2)网际控制报文协议ICMP——允许主机或路由器报告差错或异常(ICMP报文装在IP数据中),重要应用是PING。
  (3)网际组管理协议IGMP——主要用于IP多播

二、IP地址

         IP地址 ::={<网络号>,<主机号>}
(1)IP地址:全球范围内唯一的32位标识符,一分为二为网络号和主机号。采用点分十进制记法,如:128.11.3.31
(2)网络号:在互联网中唯一,标志主机所连接的网络
(3)主机号:在网络号指定的网络里唯一,标志主机
(4)保留IP

  • 127.0.0.1——环回测试(本地服务器localhost),用于本主机的进程之间的通信
  • 主机号全0(如:5.0.0.0)——该主机所在的网络地址,即网络号对应的网络
  • 主机号全1(如:5.255.255.255)——该主机所在网络上的所有主机

三、ip地址分类,及子网划分

1、ip地址分类——网络号分别是1,2,3个字节

image

2、子网划分

image

3、子网掩码——为了获取网络号

image

4、分类地址的默认子网掩码

image

this的指向及其4种绑定规则

一、this是什么

this指调用当前函数的那个对象,包括原始对象,全局对象,函数对象。
注意:this 是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
因此要想知道this指向谁,就要找到函数被调用的位置。

二、this的4种绑定规则

js中有多种方法可以改变this的指向,但总的绑定规则大致有以下4种:

1、默认绑定

默认绑定指独立函数调用,就是普通的函数调用。如下:

function A(){
	console.log(this);
}
A();

默认绑定的this指向全局对象(Window),严格模式则是undefined。

2、隐式绑定

当函数引用有上下文对象时,隐式绑定会把函数调用中的this 绑定到这个上下文对象。
例如:

function fn(){
	console.log(this.a);
}
var obj={
	a:3,
	fn:fn
};
obj.fn();

此例中this指向obj。
注意:回调函数中的this指向全局对象(Window),但箭头函数的this是继承外部函数的this,因为它没有this。

隐式丢失

var obj = {
	a:1,
	fn: function(){
		console.log(this.a)
	}
}
obj.fn(); // 1
var out = obj.fn;
var a=2; 
out(); // 2

引用了对象的函数,再执行,应用默认绑定。

3、显示绑定——call或apply绑定

显示绑定指由call和apply绑定的this。
注意:若call或apply绑定到null或undefined,如fn.call(null),则this是默认绑定(Window)。

bind绑定

bind也是显示绑定,bind的polyfill是指兼容没有bind的浏览器。如下:

if (!Function.prototype.bind) {
	Function.prototype.bind = function(context){
		if(typeof this != "function"){
			throw new TypeError('this is not a function');
		}
		var args = Array.prototype.slice.call(arguments,1);
		var that = this;
		return function(){
			var rest = Array.prototype.slice.call(arguments);
			var all = args.concat(rest);
			return that.apply(context,all);
		}
	}
}

4、new绑定

new会把this绑定到对象的实例上。(参考之前的new的过程,其实也是用apply或call实现的)

new的过程--手写new

new的过程--手写new

一、new的过程

使用new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

  1. 创建(或者说构造)一个全新的对象。
  2. 这个新对象会被执行[[ 原型]] 连接。
  3. 这个新对象会绑定到函数调用的this。
  4. 如果构造函数没有返回其他对象,那么new 表达式中的函数调用会自动返回这个新对象。
    (注意:new始终返回一个对象)

二、手写new

function myNew(){
    //参数提取
	var currentArguments=Array.prototype.slice.call(arguments);
	var objConstructor=currentArguments.shift();
	var newArguments=currentArguments;
        //1. 
	var newObj={};
        //2. 注意新建的对象没有prototype属性,原型是__proto__表示
	newObj.__proto__=objConstructor.prototype;
        //3.
	var result = objConstructor.apply(newObj,newArguments);
       // 4. 
	if(result instanceof Object){
		return result;
	}
	return newObj;
}
//使用示例
var p=myNew(Person,'Mark',23)

渲染引擎——重排、重绘

一、渲染引擎工作流程图(以webkit内核为例)

webkitflow

二、渲染过程

  • 1、HTML解析器先识别HTML元素标记,然后在Document对象下生成DOM树(DOM树会不断加入元素更新,期间若JS修改了DOM对象也会更新。);同时,CSS解析器会生成CSS规则对象
  • 2、在DOM树创建的同时,渲染引擎结合DOM树和CSS规则对象创建渲染树
  • 3、渲染树创建完成,渲染引擎计算元素的位置和大小信息,进行布局、排列。
  • 4、布局之后,渲染引擎遍历渲染树进行绘制,绘制顺序如下:背景颜色>背景图片>边框>子代>轮廓
  • 5、绘制过程中页面会显示

三、重排和重绘

  • 重排——元素位置和大小改变,渲染引擎需要重新布局、排列,就是重排
  • 重绘——元素的颜色、背景等发生变化,渲染引擎需要重新绘制,就是重绘
    因此,重排会引起重绘,但重绘不会重排。避免重排,或保持很少的重排、重绘次数可以优化页面性能。

四、如何避免

  • 避免一条一条的修改DOM样式,而是修改className或者style.classText
  • 对元素进行一个复杂的操作,可以先隐藏它,操作完成后在显示
  • 在需要经常获取那些引起浏览器回流的属性值时(如offsetTop,offsetLeft),要缓存到变量中
  • 不使用table布局,一个小的改动可能就会引起整个table重新布局
  • 通过文档片段修改

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.