meta标签
- 必须属性: content(定义与 http-equiv 或 name 属性相关的元信息)
- 可选属性: http-equiv(把 content属性中的值关联到 HTTP 头部),http-equiv属性的值可为content-type,expires,refresh,set-cookie
name(把 content属性的值关联到name的值上) ,name属性的值可为author,description,keywords,revised,generator,others
bfc 块级格式化上下文
定义
BFC是一个完全独立的布局环境,让空间里的子元素不会影响到外面的布局.
触发条件
- overflow: hidden
- 固定定位,相对定位
- flex布局
- 页面根元素
bfc的规则
-
BFC就是一个块级元素,块级元素会在垂直方向一个接一个的排列 -
BFC就是页面中的一个隔离的独立容器,容器里的标签不会影响到外部标签 -
垂直方向的距离由margin决定, 会出现垂直外边距塌陷
-
计算
BFC的高度时,浮动元素也参与计算
解决的问题
- 使用float时,脱离文档流,父元素没有高度的问题
- 两个元素,第一个元素使用了浮动布局会压在第二个元素上.设置第二个元素为bfc可以解决这个问题
null 与 undefined
null
指示变量未指向任何对象,也可理解为null 作为尚未创建的对象.是一个字面量.typeof为object
undefined
undefined 是 全局对象 的一个属性,代表着未定义(变量声明未赋值).
作用域和作用域链
作用域
当前上下文中,值和表达式在其中能被访问到的上下文.
作用域就是变量的使用范围.
作用域链
当在Javascript中使用一个变量的时候,首先Javascript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到找到该变量或是已经到了全局作用域,如果在全局作用域里仍然找不到该变量,它就会直接报错
http协议
超文本传输协议
http0.9 :只支持get,返回值只支持html格式.请求和返回值都是ASCII码.
http1.0:多了post和head.支持任意返回的格式.增加头部信息.返回值增加状态码
http1.1:支持长连接(TCP连接默认不关闭,可以被多个请求复用), 管道机制(一个TCP连接可以同时发起多个请求),支持了更多的请求方式.
http2:全部采用二进制(以前头部是ASCII码,数据体可以是ASCII可以是二进制.统称为帧), 多路复用(客户端和服务端都可以同同时发送多个请求/回应,解决了队头阻塞),头部压缩,服务器推送(服务器无法感觉,做缓存)
路由
hash路由
缺点
hash本来用来做页面定位的,用了hash路由,锚点功能就没了.
hash传参是基于url的,传复杂参数有体积限制
原理
实现原理用window.onhashchange来监听hash的改变
history路由
缺点
由于请求时会带上路径,所以后台需要对路径做相应的处理,不然会404.(hash路由中#后面的请求时不会携带)
原理
利用history API.
popstate事件能够监听到:
1.点击浏览器的前进和后退操作
2.调用 history 的 back、forward 和 go 方法
但是监听不到pushState和replaceState方法.
所以需要,给history增加一个属性,这个属性的方法和popstate事件的方法相同,在调用pushState和replaceState方法时时再调用一下自己定义的这个属性
script标签
浏览器在解析 HTML 的时候,如果遇到一个没有任何属性的 script 标签,就会暂停解析,先发送网络请求获取该 JS 脚本的代码内容,然后让 JS 引擎执行该代码,当代码执行完毕后恢复解析
- async(仅适用于外联式):当浏览器遇到带有 async 属性的 script 时,请求该脚本的网络请求是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器会暂停解析,先让 JS 引擎执行代码,执行完毕后再进行解析.
async 是不可控的,因为执行时间不确定(可能网络请求回来以后html已经解析完成,也有可能没有解析完成),异步JS 脚本中获取某个 DOM 元素,有可能获取到也有可能获取不到。而且如果存在多个 async 的时候,它们之间的执行顺序也不确定,完全依赖于网络传输结果,谁先到执行谁
- defer:浏览器遇到带有 defer 属性的 script 时,获取该脚本的网络请求也是异步的,不会阻塞浏览器解析 HTML,一旦网络请求回来之后,如果此时 HTML 还没有解析完,浏览器不会暂停解析并执行 JS 代码,而是等待 HTML 解析完毕再执行 JS 代码 存在多个 defer script 标签,浏览器(IE9及以下除外会保证它们按照在 HTML 中出现的顺序执行,不会破坏 JS 脚本之间的依赖关系
Promise
Promise有Pending,Fulfilled,Rejected三种状态.状态只能由 Pending --> Fulfilled 或者 Pending --> Rejected,且一但发生改变便不可二次修改.Promise 中使用 resolve 和 reject 两个函数来更改状态
- .then的参数可以接受两个回调函数,第一个是成功的回调,第二个是失败的回调.
- .catch的参数是一个回调,作用和.then的第二个回调一样的;还可以在执行resolve回调时被抛出错误也会进入到.catch中.
- Promise.all():接受一个数组,可以并行执行多个异步操作.全部执行成功则进入.then得到的结果也是一个数组的形式;有一个失败就进入.catch,抛出第一失败的错误信息
- Promise.race():接受一个数组,只要有一个resolve或reject就会进入.then或.catch.(看谁跑得快)
promise实现原理
- 维护一个state状态
- 维护一个存储值的变量
- 执行new MyPromise 传入的回调
- resolve 方法
state 由Pending --> Fulfilled
把resolve方法调用里面的值赋值给变量存储
- reject方法,
state 由 Pending --> Rejected
把resolve方法调用里面的值赋值给变量存储
- then方法 执行传入回调,把结果包装成promise对象返回
class MyPromise {
// 构造方法
constructor(executor) {
this.PromiseState = 'pending'
this.onFulfilledCallbacks = [] // 保存成功回调
this.onRejectedCallbacks = [] // 保存失败回调
this.PromiseResult = null // 终值
// 初始化值
// 初始化this指向
// 执行传进来的函数
this.initBind()
executor(this.resolve, this.reject)
}
initBind() {
// 初始化this
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
}
resolve(value) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行resolve,状态变为fulfilled
this.PromiseState = 'fulfilled'
// 终值为传进来的值
this.PromiseResult = value
}
reject(reason) {
// state是不可变的
if (this.PromiseState !== 'pending') return
// 如果执行reject,状态变为rejected
this.PromiseState = 'rejected'
// 终值为传进来的reason
this.PromiseResult = reason
}
then(onFulfilled, onRejected) {
// 接收两个回调 onFulfilled, onRejected
// 参数校验,确保一定是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
var thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = cb => {
try {
const x = cb(this.PromiseResult)
if (x === thenPromise) {
// 不能返回自身哦
throw new Error('不能返回自身。。。')
}
if (x instanceof MyPromise) {
// 如果返回值是Promise
// 如果返回值是promise对象,返回值为成功,新promise就是成功
// 如果返回值是promise对象,返回值为失败,新promise就是失败
// 谁知道返回的promise是失败成功?只有then知道
x.then(resolve, reject)
} else {
// 非Promise就直接成功
resolve(x)
}
} catch (err) {
// 处理报错
reject(err)
throw new Error(err)
}
}
if (this.PromiseState === 'fulfilled') {
// 如果当前为成功状态,执行第一个回调
resolvePromise(onFulfilled)
} else if (this.PromiseState === 'rejected') {
// 如果当前为失败状态,执行第二个回调
resolvePromise(onRejected)
}
})
// 返回这个包装的Promise
return thenPromise
}
}
const test3 = new MyPromise((resolve, reject) => {
resolve(100) // 输出 状态:成功 值: 200
}).then(res => 2 * res, err => 3 * err)
.then(res => console.log('成功', res), err => console.log('失败', err))
async await
解决多层回调嵌套的问题,用同步的方式操作异步.
async函数返回的是一个Promise对象.
async/await是一种语法糖,可以通过generate函数去实现
generate函数
generator函数 跟普通函数在写法上的区别就是,多了一个星号*
function* gen() { yield 1 yield 2 yield 3 }
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: undefined, done: true }
只有在generator函数中才能使用yield,它相当于generator函数执行的中途暂停点.使用到next方法才能继续往后走.next方法执行后会返回一个对象,对象中有value 和 done两个属性.
- value:暂停点后面接的值,也就是yield后面接的值.可以看到最后一个是undefined,这取决于你generator函数有无返回值
- done:是否generator函数已走完,没走完为false,走完为true
function* gen() { yield 1 yield 2 yield 3 return 4 }
const g = gen()
console.log(g.next()) // { value: 1, done: false }
console.log(g.next()) // { value: 2, done: false }
console.log(g.next()) // { value: 3, done: false }
console.log(g.next()) // { value: 4, done: true }
yield后面接函数,到了对应暂停点yield,会马上执行此函数,并且该函数的执行返回值,会被当做此暂停点对象的value.
function fn(num) { return new Promise(resolve => { setTimeout(() => { resolve(num) }, 1000) }) }
function* gen() { yield fn(1) yield fn(2) return 3 }
const g = gen()
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: Promise { <pending> }, done: false }
console.log(g.next()) // { value: 3, done: true }
generator函数可以用next方法来传参,并且可以通过yield来接收这个参数
第一次next传参是没用的,只有从第二次开始next传参才有用
next方法的参数表示上一个yield表达式的返回值
function* gen() {
const num1 = yield 1
console.log(num1)
const num2 = yield 2
console.log(num2)
return 3
}
const g = gen() console.log(g.next()) // { value: 1, done: false }
console.log(g.next(11111)) // 此时是把111111复制给了num1
// 11111 // { value: 2, done: false }
console.log(g.next(22222))
// 22222 // { value: 3, done: true }
EventLoop
浏览器事件循环
过程
js的任务被分为了同步任务和异步任务. 同步任务在调用栈中立即执行,异步任务会被放进任务列表中(异步任务有了结果就把回调放入对应的任务队列中(分宏任务队列和微任务队列)).等到同步任务全部完成后,再依次执行任务队列中的任务.
异步任务又分为宏任务和微任务,微任务先于宏任务执行(执行完所有微任务执行下一个宏任务,再去清空所有微任务,再执行一个宏任务.如此循环).
其实是宏任务先于微任务的,因为整个script就是一个宏任务.
宏任务和微任务
微任务是线程之间的切换,速度快。不用进行上下文切换,可以快速的一次性做完所有的微任务。
宏任务是进程之间的切换,速度慢,且每次执行需要切换上下文。因此一个Eventloop中只执行一个宏任务。
区分宏任务微任务的原因:是为了插队。由于微任务执行快,一次性可以执行很多个
反观如果不区分微任务和宏任务,那么新注册的任务不得不等到下一个宏任务结束后,才能执行。
- 宏任务:setTimeout,setInterval,script(整体代码)
- 微任务:promise,MutationObserver(监听dom的变化) 注意:定时器时间是不准的(因为他只是到时间了才开始加入任务队列,而不是到时间了执行.)
缓存(强缓存与协商缓存)
强缓存
通过http请求头中的Cache-Control和Expire两个字段控制.Cache-Control优先于Expire. 命中强缓存时客户端不会再求,直接从缓存中读取内容,并返回HTTP状态码200
缺点:时间过期就会请求,可能过期的时候我的文件并没有发生变动
Expires:
代表该资源的过期时间,是一个GMT 格式的标准时间.
Expires的缺点:
- 缓存过期以后,服务器不管文件有没有变化会再次请求服务器。
- 缓存过期时间是一个具体的时间,这个时间依赖于客户端的时间,如果时间不准确或者被改动则缓存也会受到影响
Cache-Control:
客户端可以在HTTP请求中使用的标准 Cache-Control 指令:
- max-age:在多少秒内有效
- no-cache:不使用强缓存,使用缓存协商
- no-store:禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源
- max-stale: 5 客户端愿意接收一个已经过期的资源。可以设置一个可选的秒数,表示响应不能已经过时超过该给定的时间。
- min-fresh: 5 表示客户端希望获取一个能在5秒内保持其最新状态的响应。
- only-if-cached 这个字段加上后表示客户端只会接受代理缓存,而不会接受源服务器的响应。如果代理缓存无效,则直接返回 504(Gateway Timeout)。
服务器可以在响应中使用的标准 Cache-Control 指令
- max-age
- s-maxage: 就是用于表示 cache 服务器上(比如 cache CDN,缓存代理服务器)的缓存的有效时间的,并只对 public 缓存有效
- no-cache
- no-store
- public:可以被所有的用户缓存,包括终端用户和中间代理服务器
- private:只能被终端用户的浏览器缓存,不允许中间缓存代理进行缓存(这个是默认值)
协商缓存
商缓存主要有四个头字段,它们两两组合配合使用.If-Modified-Since 和 Last-Modified一组,Etag 和 If-None-Match一组.当同时存在的时候会以Etag 和 If-None-Match为主.当命中协商缓存时,服务器会返回HTTP状态码304,让客户端直接从本地缓存里面读取文件.
If-Modified-Since 和 Last-Modified
- If-Modified-Since:请求头,资源最近修改时间,由浏览器告诉服务器。其实就是第一次访问服务端返回的Last-Modified的值 当客户端第一次请求服务器的时候,服务端会返回一个Last-Modified响应头,该字段是一个标准时间。客户端请求服务器的时候会带上If-Modified-Since请求头字段,该字段的值就是服务器返回的Last-Modified的值。服务器接收到请求后会比较这两个值是否一样,一样就返回304,让客户端从缓存中读取,不一样就会返回新文件给客户端并更新Last-Modified响应头字段的值
If-Modified-Since 和 Last-Modified的缺点:时间只能精确到秒
Etag 和 If-None-Match
- Etag:是由文件修改时间与文件大小计算而成,只有当文件文件内容或修改时间变了Etag的值才会发生变化. 服务端会返回一个Etag响应头。客户端请求服务器的时候会带上If-None-Match请求头字段,该字段的值就是服务器返回的Etag的值。服务器接收到请求后会比较这两个值是否一样,一样就返回304,让客户端从缓存中读取,不一样就会返回新文件给客户端并更新Etag响应头字段的值。
缓存失效问题
比如我们明明更新了系统版本,为什么客户端看到的还是老文件.
解决方法:html文件不使用缓存或者使用协商缓存.每次更改js文件名加上版本号或者时间戳(老方法)/webpack打包用hash值命名(新方法),这样客户端请求文件会当新文件去请求.
垃圾回收
原理:JavaScript 垃圾回收机制的原理说白了也就是定期找出那些不再用到的内存(变量),然后释放其内存
标记清除算法:
为 标记 和 清除 两个阶段,记阶段即为所有活动对象做上标记,清除阶段则把没有标记(也就是非活动对象)销毁
标记阶段: 在此阶段,垃圾回收器会从mutator(应用程序)根对象开始遍历。 每一个可以从根对象访问到的对象都会被添加一个标识,于是这个对象就被标识为可到达对象。
清除阶段: 在此阶段中,垃圾回收器会对堆内存从头到尾进行线性遍历,如果发现有对象没有被标识为可到达对象,那么就将此对象占用的内存回收,并且将原来标记为可到达对象的标识清除,以便进行下一次垃圾回收操作
栈
当前上下文执行完后,移动记录当前执行状态的指针向下移动.旧的上下文就被销毁了,新的上下文被压入.
堆
新生代
新生代区域不大,若太大,回收就需要隔很久.
生存时间短的放入新生代区域.
新生代区域分为对象区域和空闲区域.
新加入的都会放入对象区域,当对象区域快被写满时就执行一次垃圾清理操作.
垃圾回收过程中,对对象区域中的垃圾做标记,标记完成之后,就进入垃圾清理阶段.
副垃圾回收器会把这些存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来(相当于内存整理)
完成复制以后,对象区域与空闲区域进行交换.这样就完成了垃圾的回收操作
经过两次垃圾回收依然还存活的对象,会被移动到老生代区域中
老生代
除了新生代区域中晋升的对象,一些大的对象会直接被分配到老生代区域.老生代区域中的对象有两个特点,一个是对象占用空间大,另一个是对象存活时间长
由于老生代区域的对象比较大,复制操作就会比较耗时.还会浪费一半的空间 所以主垃圾回收器是采用标记-清除
然后还会进行标记-整理,让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
全停顿
执行垃圾回收算法,要将正在执行的 JavaScript 脚本暂停,待垃圾回收完毕后再恢复脚本执行.我们把这种行为叫做全停顿
使用增量标记算法,可以把一个完整的垃圾回收任务拆分为很多小的任务,这些小的任务执行时间比较短,可以穿插在其他的 JavaScript 任务中间执行.最后统一清理整理
渲染
html解析为dom树,css解析为css规则树,通过dom树和css规则树来构造渲染树.最后就是绘制.
构建dom:字节数据(网络中都是0,1这种字节数据)=>字符串=>标签=>node=>dom
渲染过程遇到js,会停止渲染,执行js.
css和dom
css不会阻塞dom解析,会阻塞dom渲染.
css和dom是并行构建的,render树依赖css树和dom树,所以必须两个都加载完成才能开始render树渲染.所以css会影响dom渲染
css 和js
css会阻塞js执行,不会阻塞js下载. js可以操作样式所以js依赖css,所以要等css加载执行完毕后才开始js
dom和js
js会阻塞dom解析,也就会影响dom渲染
重绘和回流(重排)
- 重绘:修改样式不改变几何属性 -回流(重排):改变了几何属性
TCP三次握手四次挥手
三次握手
- 发送syn,请求连接
- 回SYN/ACK,收到请求,同意建立连接
- 去确认同意连接(seq(序号),ack(确认码),syn(建立连接的包))
握手为什么不是两次:无法确认客户端的接收能力.发了syn想建立tcp连接,由于网络拥堵没有迟迟不到,客户端便超时重发,然后建立上了.
关闭连接,后来之前拥堵的syn包到了服务器,由于是两次,直接建立了连接,但是客户端已经关闭了连接.服务器会处于等待发送数据状态,这样会造成连接资源浪费
四次挥手:
- 请求关闭连接(seq,fin)
- 收到关闭连接的请求(seq,ACK)
- 服务器传完数据,通知客户端它关闭连接了(ack,seq,fin)
- 客户端收到服务器关闭连接的消息,然后告诉服务器自己处于time_wait状态.
是四次挥手而不是三次挥手的原因:服务器收到fin后会先发个ack,然后等到服务端所有报文发送以后才给客户端发fin
如果是三次,把ack和fin合并为一次,等待发送时间可能会延长,客户端会以为服务器没收到 fin,而一直发fin
最后等待2MSL的意义:
- 如果不等待,客户端直接跑路,当服务端还有很多数据包要给客户端发,且还在路上的时候,若客户端的端口此时刚好被新的应用占用,那么就接收到了无用数据包,造成数据包混乱。
- 1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端
- 第四次挥手的ack如果没有收到,服务器在经历1个MSL时段后将重新发送第三次挥手FIN包,客户端再次接收到FIN包时会再次发送ACK包,直至2MSL时间终结
观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
被观察者:
变量:
声明一个数组存放观察者,
再声明一个状态的变量
方法:
设置状态值的方法:设置状态和调用通知所有观察者
通知观察者的方法:遍历存放观察者的数组,调用观察者的update方法
添加观察者的方法:把观察者加入存放观察者的数组
观察者
实例化的时候接收被观察者,调用被观察者的attach方法,把自己加入存放观察者的数组
本身有一个update方法
过程
发布者发生改变,遍历所有观察者,调用观察者的更新方法.
每次观察者被实例化时,都调用发布者的方法把自己存入观察者数组
class Subject {
let observers = [];
let state;
getState() {
return this.state;
}
setState(state) {
this.state = state;
notifyAllObservers();
}
attach(observer){
observers.push(observer);
}
notifyAllObservers(){
for (observer in observers) {
observer.update();
}
}
}
class Observer {
let subject;
update();
}
class BinaryObserver extends Observer {
constructor(subject) {
super();
subject.attach(this);
}
update() {
console.log("Binary");
}
}
class OctalObserver extends Observer {
constructor(subject) {
super();
subject.attach(this);
}
update() {
console.log("Octal");
}
}
var subject = new Subject();
var binaryObserver = new BinaryObserver(subject);
var octalObserver = new OctalObserver(subject);
subject.setState(15);
发布订阅模式
* handle:存放事件处理函数的对象
* on: 订阅事件
* emit: 发布事件
* off: 删除事件
handle:{},
on事件:判断handle里面是否存有这个事件,没有就加上,然会把处理函数存到对应的事件里面去
emit事件:有这个事件就循环hanlde对象对应的事件存放的数组去执行处理函数
删除事件:删除对应事件中的对应处理函数
/**
* 发布订阅模式
* handles: 事件处理函数集合
* on: 订阅事件
* emit: 发布事件
* off: 删除事件
**/
class PubSub {
constructor() {
this.handles = {};
}
// 订阅事件
on (eventType, handle) {
if (!this.handles.hasOwnProperty(eventType)) {
this.handles[eventType] = [];
}
if (typeof handle == 'function') {
this.handles[eventType].push(handle);
} else {
throw new Error('缺少回调函数');
}
return this;
}
// 发布事件
emit (eventType, ...args) {
if (this.handles.hasOwnProperty(eventType)) {
this.handles[eventType].forEach((item, key, arr) => {
item.apply(null, args);
})
} else {
throw new Error(`"${eventType}"事件未注册`);
}
return this;
}
// 删除事件
off (eventType, handle) {
if (!this.handles.hasOwnProperty(eventType)) {
throw new Error(`"${eventType}"事件未注册`);
} else if (typeof handle != 'function') {
throw new Error('缺少回调函数');
} else {
this.handles[eventType].forEach((item, key, arr) => {
if (item == handle) {
arr.splice(key, 1);
}
})
}
return this; // 实现链式操作
}
}
// 下面做一些骚操作
let callback = function () {
console.log('you are so nice');
}
let pubsub = new PubSub();
pubsub.on('completed', (...args) => {
console.log(args.join(' '));
})
pubsub.on('completed', callback);
pubsub.emit('completed', 'what', 'a', 'fucking day');
pubsub.off('completed', callback);
pubsub.emit('completed', 'what', 'a', 'fucking day');
// 控制台打印
// what a fucking day
// you are so nice
// what a fucking day
模块化
定义:复杂的程序拆成多个文件.
模块化优点
- 更好的分离, 按需加载
- 更高复用性
- 高可维护性
commonJs
用module对象记录模块信息
用moudule.exports或者exports导出模块
用require导入
exports为moduel对象的一个属性
moudule.exports===exports
当moudule.exports与exports混用时,moudule.export会覆盖exports
CommonJS 模块输出的是一个值的拷贝
CommonJS 模块是运行时加载
es6
ES6 模块输出的是值的引用
ES6 模块是编译时输出接口
XSS 攻击
Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击.可分为存储型、反射型和 DOM 型三种
- 存储型:将恶意代码提交到目标网站的数据库中
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
- 这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等
- 反射型:
- 攻击者构造出特殊的 URL,其中包含恶意代码
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等
- DOM 型:
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作 DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞
预防存储型和反射型 XSS 攻击:
改成纯前端渲染,把代码和数据分隔开(前后分离)。 对 HTML 做充分转义(对于 HTML 转义通常只有一个规则,就是把 & < > " ' / 这几个字符转义掉) 对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度
预防 DOM 型 XSS 攻击:
在使用 .innerHTML、.outerHTML、document.write() 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 .textContent、.setAttribute() 等 避免 eval() 对于不受信任的输入,都应该限定一个合理的长度。虽然无法完全防止 XSS 发生,但可以增加 XSS 攻击的难度
webpack
loader
本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。Webpack 只认识 JavaScript,对其他类型的资源进行转译的预处理工作
常用loader
file-loader(处理图片和字体),babel-loader(高版本语法转低版本),sass-loader,css-loader,style-loader(SS 代码注入到 JavaScript 中)
Plugin
在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果.插件可以扩展 Webpack 的功能
常用plugin
html-webpack-plugin(简化 HTML 文件创建),clean-webpack-plugin(清理目录)
Webpack构建流程
初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler,
编译:从入口触发,对模块进行递归编译
输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
打包原理
对文件的依赖关系进行静态分析,生成静态资源.递归地构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle.