[toc]


  • 严格模式( use strict ):要求显示的引用全局作用域

优先级: + > ? ! > == . > =

// 这里举例不准确  这里实际是对引用类型的理解
var a = { n: 1 } 
var b = a
a.x = a = { n:2 }  // 先执行 a.x  给x赋值 undefined,这里先挂起等待右边的表达式返回结果,
// a={n:2} 重新将a指向了新的地址  但是 a.x  还是原来的地址,这里也是b指向的堆地址,所以有如下结果 
console.log(a, b);
// { n: 2 }  { n: 1, x: { n: 2 } }

[] == ![] // true
// 先执行 ![] 返回 false ; [] 转换为空字符串 返回 false

二叉树

前序:跟左右; 中序:左跟右  后序:左右跟   这里的左右表示左边部分和右边部分,注意根据根的位置确定值在哪边,前序第一个值为根,中序第一个值为最左;
前序和中序结合对别 可直接判断出其它值在树的哪边

内核

ie trident || chrome,Safari webkit || firefox gecko || Opera presto
chrome Opera 现在: Blink(基于webkit,Google与Opera Software共同开发)

兼容性

  1. < !--[if IE]> 所有的IE可识别 <![endif]-- >
    < !--[if lt IE 9]> IE9以下版本可识别 <![endif]-->
    ie10及以上无效

清除浮动

  • div style="clear:both;"//左右侧
  • parent:after{ content:"."; height:0; visibility:hidden;display:block; clear:both;}
  • 给包含浮动元素的父标签添加css属性 overflow:auto; zoom:1; zoom:1用于兼容IE6。

undefined || not defined || null

var x; // 声明 x     
console.log(x); //output: undefined  
console.log(z); // 抛出异常: ReferenceError: z is not defined

typeof null //object  
// null是一个表示"无"的对象,转为数值时为0;undefined是一个表示"无"的原始值,转为数值时为NaN。  
var a; Number(a); //NaN  
5 + undefined ;//NaN  
// 基本是同义的,null表示"没有对象",即该处不应该有值。  
// null 表示一个值被定义了,定义为“空值”;  

undefined // 表示根本不存在定义。  
// 所以设置一个值为 null 是合理的,如  
objA.valueA = null;  
// 但设置一个值为 undefined 是不合理的  
  1. CSS 提供的语法规则,只有导入样式表的作用,link是HTML提供的标签,可以定义 RSS、rel 连接属性等;
  2. 加载页面时,link标签同时加载;@import在页面加载完毕后被加载;
  3. @import在 IE5+ 才能识别,link不存在兼容性问题;
  4. link可以用js插入,@import不可

垃圾回收 garbage collection

垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存,当使用了值之后未取消引用,可能导致内存泄漏

  • 当没有变量指向某一内存 a={b:1}; a={c:2}; 存贮{b:1} 会被回收
  • 函数调用完后,内部所有变量将被回收

内存泄漏

  1. 全局变量不会被回收(除了退出浏览器),函数运行完之后,运行函数的内存区被回收。
  2. 闭包。
  3. DOM被删除或清空时,事件未清除导致的内存泄漏。 避免使用全局变量和函数
  4. 定时器没有及时清除
  5. 函数调用完成后,内部运行申请的内容没有办法及时回收,如产生的数据,又被保存到了外部的变量中 var myvar = 0; // 336ms window.myvar = 0; // 2383ms
    var myfunc = function(){} // 3515ms window.myfunc = function(){} // 10151ms
    避免用new创建函数和数组;
    用对象方式比数组方式更快一点
    a[i] = value; // 1270ms obj[property] = value; // 960ms

堆(heap) 栈(stack)

  • 栈:自动分配内存,自动释放
  • 堆:动态分配内存,大小不定也不自动释放空间

学习interview map 笔记

https://yuchengkai.cn/docs/zh/frontend/open in new window

js

  • 判断为 false 的情况只有: 0 false '' null undefined 未定义的值; 其它都为 true

类型

  • 基本类型 string boolean number null undefined symbol

  • 引用类型 object array function

  • 变量,基本数据类型在栈,对象在堆

  • 基本类型(Symbol)和引用类型(Object,array等)

const symbol = Symbol('接受一个字符串参数,以作区分用') // 独一无二的
let s = Symbol('s')
s // Symbol(s)
  • NaN属于number,两个 NaN === NaN 返回false

  • 字面量只有在使用的时候才会变成对应的类型

let a = 1    // 此时只是字面量
a.toString() // 此时变为引用类型

typeof

  • 返回的值都是小写,有number,boolean,string,function,object,undefined symbol
  • 当变量没有定义时可以用 if(typeof variable === 'undefined') 来判断,若直接判断则会报错阻碍程序后续执行
  • Array,null 返回 object
let a = null
Object.prototype.toString.call(a) // "[object Null]"
typeof a // object

类型转换

  • 除 Object 以外的所有类型都是不可变的,值本身无法被改变,称这些类型的值为“原始值” primitive

  • 条件判断时,除了 undefined,null,NaN,'',0,-0,false 其它值都转换为true

  • NaN,{}和任意值比较都是返回false;,对象会先转为数字 NaN

  • 数组转化为Number时(会隐式的调用join方法): 空的[]转为空字符串再转为 0,有两个或以上元素的数组转为NaN,只有一个元素时,根据该元素进行Nunber转换


对象转字符串:

先调 toString() 后调 valueOf()

  • valueOf() 方法

Array返回数组对象本身; Boolean布尔值; Date存储的时间是从1970年1月1日午夜开始计的毫秒数UTC; Function函数本身; Number数字值; Object对象本身; String字符串值; Math和Error对象没有该方法;可以自定义覆盖


对象转数字:

依照 toPrimitive 规则 先调 valueOf() 如果返回原始值( null undefined 布尔值 字符串 数字 ) 将其转换为数字, 就是直接作为返回值

再调 toString() 过程同上

如果都没有返回原始值 则抛出错误

以上两个方法都可以被重写

let a = {
    [Symbol.toPrimitive](){
        return 9 //只能返回Number String,优先级更高
    },
    valueOf(){
        return 'you are'
    },
    toString(){
        return 'SB'
    }
}
console.log(a) // 9 
console.log(a.toString()) // SB
// 没有重写valueOF 返回 SBSB
// 没有重写toString 返回 you are[object Object]
// 返回 you areSB
// 有了toPrimitive方法,即不返回valueOf的值

逗号操作符

对它的每个操作数求值(从左到右),并返回最后一个操作数的值

var a,b; a=(b=1,2); // console.log(a);//2 console.log(b);//1

四则运算

  • 赋值运算符是右结合性的 A = B = C = D 等价于 A = (B = (C = D))

先计算左边的表达式(计算的值为内存中的地址),再计算右边的表达式,然后再将右边返回的值赋值给左边返回的对应的地址

  • 加法中,其中一个是字符串,另一个也会转为字符串; 其它运算,只要其中一方是数字,就会都转为数字

  • 加法运算会触发三种类型转换: 转换为原始值,转换为数字( + 'b' 等于NaN),转换为字符串

比较运算

UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式

JavaScript语言采用Unicode字符集,但是只支持一种编码方法,UCS-2

charCodeAt(): 返回指定位置的字符的 Unicode 编码,返回值是 0 - 65535 之间的整数

Unicode前128个编码单元等于ascii值

英文数字,按照ASCII码比较

中文是按照Unicode编码比较的

  • null == undefined true null === undefined false

  • 1<2<3 返回 true 从左到右计算 与等号不同

原型

__proto__ 和 constructor prototype

  • constructor 和 __proto__ 是对象独有的
  • prototype 是函数独有的,但函数也是一种对象,所以函数都拥有上诉属性
  1. constructor 指向对象的构造函数; Function这个对象比较特殊,它的构造函数就是它自己(因为Function可以看成是一个函数,也可以是一个对象) 所有函数和对象最终都是由Function构造函数得来,所以constructor属性的终点就是Function这个函数

  2. __proto__ 隐式原型 指向创建对象的构造函数的 prototype; 推荐使用 getPrototypeOf() 在其 constructor 属性的 name 属性值,表示原型名; 这样一层一层的构成了原型链 当访问一个对象的属性值时,也会按照该方式一层一层的找,直到 __proto__ 指向 null

  3. prototype 显示原型 函数创建后都有一个(Function.prototype.bind 方法构造出来的函数没有),指向函数的原型对象
    也是当前函数所创建的实例的原型对象 所以构造函数的prototype 和实例的 __proto__ 指向同一个原型对象

作用:实例共享属性和方法

new

发生以下四个步过程:

  • 新生成了一个对象
  • 链接到原型 空对象的原型prototype指向构造函数的原型
  • 绑定 this 将空对象作为构造函数的上下文(改变this指向)
  • 对构造函数的返回值做一些判断: 1、如果返回值是基础数据类型,则忽略返回值; 2、如果返回值是引用数据类型,则使用return 的返回,也就是new操作符无效;

new Fn() 结果等价于 new Fn

优先级:new Foo() 的优先级大于 new Foo,有参数的优先级高 new fn.getValue() 等价于 将 getValue 作为构造函数,因为 . 优先级高于 无参 new ; . 的优先级与有参 new 相同为18


this

指向调用函数前的对象

  • 优先级:有函数调用的会覆盖指向window的,用new创建的优先级最高

  • 箭头函数没有this,它的 this 只取决于他外面的第一个不是箭头函数的函数的 this


闭包

一个函数X返回另一个函数Y,且Y使用了X的变量,Y就被称为闭包,X已经出了调用栈,Y能调用X的变量原因是X的变量此时已存贮在堆上,JS引擎通过逃逸分析哪些变量 需要存贮在堆上,哪些需要存贮在栈上

function fn () {
    var a = 'a'
    return function() {
        console.log(a)
    }
}

闭包
销毁 函数名=null

for ( let i=1; i<=5; i++) {
	setTimeout(() => {
		console.log(i);
	}, i*1000 );
}
// 输出结果: 1-5
// 原因是let有个块级作用域,每一次循环都新声明了一个i;引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算
// 而var是声明被提升到全局
// for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域

深浅拷贝

浅拷贝

不会拷贝对象的子对象

  • Object.assign(target, source)
  • 展开运算符 ...

深拷贝

对对象以及对象的所有子对象进行递归拷贝

  • JSON.parse(JSON.stringify(object)) :会忽略undefined,不能解决循环引用, 不能序列化函数

  • lodash

  • MessageChannel

  • 形如 [{ a:1, b:2 }] 的对象数组,简单的深拷贝实现

const arr = [{ a:1, b:2 }]
const result = arr.map(item => ({ ...item }))

事件

  • 任意事件触发后三个阶段:捕获(window到目标)-> 目标 -> 冒泡。addeventListener(,,false默认(冒泡阶段执行)||true)
<div id="outDiv">
  <button id="btn">按钮</button>
</div>
document.getElementById('btn').addEventListener('click',() => {
  console.log('button 冒泡阶段')
}, false)

document.getElementById('outDiv').addEventListener('click',() => {
  console.log('外层 div 捕获阶段')
}, true)

// 点击按钮返回
// 外层 div 捕获阶段
// button 冒泡阶段

attachEvent('onclick',function(){}) //兼容ie8

addeventListener('click',function(){},false) //w3c

event对象

  1. event.stopPropagation传播():阻止事件传递(相同),不管是冒泡还是捕获;IE8以及以下版本用event.cancelBubble冒泡 =true;
  2. event.target 返回触发此事件的元素(可用于事件委托),IE下是event.srcElement;
  3. 添加的事件函数中用return false来实现stopPropagation()和preventDefault()的功能。
  • 同一节点同时注册冒泡和捕获事件,按注册代码先后顺序执行
  • addEventListener(),第三个参数如果是对象
{
    capture: Boolean  // 和直接输Boolean一样
    once: Boolean // 回调只执行一次
    passive: Boolean // 永远不会调用preventDefault    
}
  • stopPropagation 可以阻止冒泡和捕获
  • stopImmediatePropagation() 可以阻止在同一元素上的后执行的事件,比如挂载了多个click当执行到有该方法的事件回调后,后面的事件即被阻止

事件委托优缺点

  1. 减少事件注册,节省内存。
  2. 子对象动态绑定。
  3. 若把所有事件用代理方式容易出现误判。

event loop

主线程从"任务队列"中读取事件,这个过程是循环不断的

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop

进程 : 运行的程序就是一个进程,比如正在运行的浏览器,它会有一个进程。 线程 : 程序中独立运行的代码段。一个进程 由单个或多个 线程 组成,线程是负责执行代码的。

对象被分配在一个堆中

"任务队列"是一个事件的队列(也可以理解成消息的队列)

  • js执行过程中会产生执行环境,这些环境会被顺序加入到执行栈中。

  • 遇到异步代码,会被挂起并加入到Task(有多种)队列中

  • 执行栈空了后,Event loop 便从Task队列中拿出需要执行的放入执行栈执行

  • setTimeout后面的是异步函数,即便设置了0秒延迟,也是在同步之后执行,HTML5规定延迟不得小于4ms,不足自动增加,和浏览器也有关联,设置的时间代表了消息被实际加入到队列后才计算的延迟时间,而不是从代码执行到 setTimeout 就开始计算延迟时间

  • 不同任务源会被分配到不同Task队列,任务源分为微任务(micro task:es6中叫jobs)和宏任务(macro task:es6中叫task)

    微任务:promise process.nextTick Object.observe MutationObserver ; process.nextTick优先级大于promise.then 如果微任务中又有微任务加入 也会在宏任务前执行

    宏任务:script setTimeout setInterval setImmediate I/O UI rendering requestAnimationFrame

await后面的表达式会先执行一遍,将await后面的代码加入到微任务中

node > 11 : 一旦执行一个阶段里面的任务就会立即执行微任务队列(和浏览器中一致),例如 setTimeout 中有微任务时将其加入当前宏任务中,当执行 setTimeout 的回调函数时,也会将微任务立即执行了,而不是先执行下一个 setTimeout 的回调中的同步代码,再返回来执行微任务

  • 一次EventLoop顺序:
    • 执行同步代码(属于宏任务)
    • 执行栈为空,查询是否有微任务要执行
    • 执行微任务
    • 若有需要,渲染UI
    • 下一轮的EventLoop,执行宏任务的异步代码
setTimeout(() => {
    console.log('set timeout 中的宏任务');
    new Promise((resolve) => {
        console.log(1);
        resolve()
    }).then(() => {
        console.log(2);
    })
}, 0)

setTimeout(() => {
    console.log('set timeout : 2000');
    new Promise((resolve) => {
        console.log(3);
        resolve()
    }).then(() => {
        console.log(4);
    })
}, 2000)

var c = new Promise((res) => {
    console.log('in promise 同步任务');
    res('params')
})

c.then((r) => {
    console.log('微任务 in promise then:' + r);
    var ak = new Promise((re) => {
        console.log('promise in promise');
        re()
    })

    ak.then(() => {
        console.log('promise in promise then');
    })
})

console.log('script 中的宏任务');
// 输出:
// in promise 同步任务
// script 中的宏任务
// 微任务 in promise then:params
// promise in promise
// promise in promise then
// set timeout 中的宏任务
// 1
// 2
// set timeout : 2000
// 3
// 4

同源

协议(http) 主机(域名) 端口 都相同 则同源 域名默认 80 端口

存储

cookie(4K):删除 同名加max-age=0 如果不设置,默认就是一直存在,关闭浏览器后再打开,依然存在,只有手动删除才会被清除 同一个ip下的多个端口下的cookie是共享的!ip相同,端口不同 但是会覆盖 document.cookie='name=hew;path=/;expires=UTCstring;max-age=秒' //设置和获取
UTCstring = new Date(2021, 10, 5, 10, 10, 0, 0).toUTCString()

注意 iframe 中如果有域名重定向,将无法获取到重定向之前的 cookie,替换为使用localstorage; 猜测原因为 iframme 上显示的域名还是重定向之前的域名,所以重定向后,设置的cookie会被保存到重定向前的域名,导致现在域名内无法获取

localStorage

(5M) 除了主动清理,否者永远存在; sessionStorage(页面或浏览器关闭就清理):可保存的数据大小与浏览器有关,同域标签才能共享

indexDB

(无限)


Service worker

https://yuchengkai.cn/docs/zh/frontend/browser.html#service-workeropen in new window

html 渲染解析

  • 处理html并构建DOM(document object model)树

  • 处理css构建CSSOM(cascading style sheets object model)树,与构建DOM是可以同时进行的

  • 将DOM与CSSOM合并成一颗渲染树 render tree

  • 根据渲染树来布局(layout),计算每个节点的位置

  • 调用GPU绘制,合成图层,显示

  • 构建CSSOM树 应尽量保证层级扁平 css选择器层级越多,执行越慢,最好用id,class

  • 必须等CSSOM构建完毕才能进入下一个阶段,即使DOM已经构建完;如果有外部的样式表处于下载中,也会等待下载并解析完毕才会开始构建 render tree,所以CSS 放头部

css的加载和构建cssom并不会阻塞DOM树的生成,前提是 link 放在 head 内

当解析器发现非阻塞资源,例如图片,浏览器会请求这些资源并且继续解析 当遇到一个CSS文件时,解析也可以继续进行

遇到普通 script 标签,会先等CSSOM下载和构建完毕(如果js要修改 cssom,必须拿到一个完整的cssom),再执行js,等JavaScript执行完后,再继续构建DOM,由于js可以修改cssom和dom 导致阻塞,所以一般js放最后

普通脚本的加载顺序:先下载,再执行,再下载下一个,再执行;

<script defer>: 开启一个线程去下载js文件,不会立即执行,会阻塞DOMContentLoaded事件,并在其之前执行,不会阻塞DOM树创建,需要等待DOM树生成完毕后执行

<script async>:开启一个线程去下载js文件,下载完成后立刻执行,多个脚本间下载是无顺序的,load 触发之前执行,不会阻塞DOM树的创建和DOMContentLoaded事件的执行

load与DOMContentLoaded

Load 事件触发代表页面中的 DOM,CSS,JS,图片已经全部加载完毕

DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析

图层

  • 普通文档流看成一个图层
  • 不同的图层渲染互不影响
  • 某些频繁需要渲染的建议单独生成一个新图层,提高性能
  • 不可生成过多图层,会有反效果

以下方法可产生新图层

  • 3D变化,translate3d,translateZ
  • will-change
  • video,iframe标签
  • 动画实现opacity动画转换
  • position:fixed

重绘和回流

重绘:不会影响布局的样式改变

回流(重排):布局或者几何属性需要改变,必定产生重绘

和EventLoop关系

  • EventLoop执行完microtasks后,会判断是否需要document需要更新
  • 判断有无resize和scroll事件需要触发
  • 判断是否有media query
  • 更新动画并发送事件
  • 判断是否有全屏操作事件
  • 执行requestAnimationFrame回调
  • 执行IntersectionObserver回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好
  • 更新界面
  • 以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调

减少重绘和回流

  • 使用translate代替top
  • visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
  • 批量更改后一次修改DOM
  • 不要在循环里反复获取DOM节点属性(如node.style.offsetTop)
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
  • 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
  • css查找避免深度过深(找到符合匹配规则的节点就加入结果集;直到根元素html都没有匹配,则不再遍历这条路径,从下一个最右边的规则开始)。css查询规则是从最右边开始。
  • 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video 标签

性能

首屏优化

  • ssr 服务端渲染
  • 组件按需加载
  • UI组件按需引入
  • css 放 head中 js 放 body 内最后
  • 字体图标和icon雪碧图
  • cdn 依靠部署在各地的边缘服务器 用户就近获取所需内容
  • 静态资源压缩 css、js、图片等资源进行压缩,并且服务器开启 GZIP 压缩
  • chunk.js 优化 分离出 elementUI vuex 等静态库

提升用户体验

  • 界面风格统一
  • 如今大多数浏览器在下载css/js时是并行的,但是单线程运行js的,这也就意味着一个js文件在被解析的同时浏览器不能对任何事件做出响应,直到浏览器弄明白这个js是干什么的才会转向下一个js文件;所以将js文件放尾部,优先下载了html/css等文件,这样会给用户一些错觉,认为网站已经加载。加载js时候会阻塞浏览器的渲染操作。window的 onload 方法里面执行添加script标签。
  • 保证代码的规范性避免404页面,保证每个资源能够正常加载。
  • 一个页面中如果频繁出现向其他域名请求数据,而且使用的是域名,则会出现频繁的DNS解析。
  • 公共库cdn资源,当其他网站也,使得很多访客的缓存中已经存在这些公共库的资源。第二张页面时,如果使用的是同一个外部css/js文件

DOM 操作

  • 多用伪元素实现页面样式,清除浮动等,它不属于html页面 不会增加页面渲染的负担,也不会增加页面js查询的负担
  • 按需加载
  • 缓存节点 即将节点获取到存到变量上 不要在for循环之类的中去重复获取
  • 将修改的元素 存入文档片段 一次性插入

网络相关

DNS预解析:

<link rel="dns-prefetch" href="//delai.me">  
  1. 对静态资源域名需要手动做dns prefetching
  2. 对js里会发起的跳转需要手动做dns prefetching
  3. 不用对超链接做,浏览器会自动做
  4. 对重定向跳转的新域名做手动dns prefetching

最优方法:通过js初始化一个iframe异步加载一个页面,而这个页面里包含本站所有的需要手动dns prefetching的域名

gzip 压缩

  • 客户端浏览器存在兼容问题
Response Headers
Accept-Encoding: gzip
  • tomcat 可配置

  • nginx 可配置

  • koa 用 koa-compress express 用 compression,使用webpack插件compression-webpack-plugin 将资源文件压缩为.gz文件,并且根据客户端的需求按需加载

  • 压缩后可能文件反而变大,所以用其它方式压缩过的可不必再压缩


<link rel="preload" href="http://example.com">

....等https://yuchengkai.cn/docs/zh/frontend/performance.html#cdnopen in new window


安全

点击劫持

防止网站被别的网站用 iframe 嵌入

参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/X-Frame-Options

X-Frame-Options: DENY  同域名页面中同样无法加载
X-Frame-Options: SAMEORIGIN  可以在相同域名页面中展示
<script>
    /* html 加载后执行如下代码,可以优化,加跳转等,但是循环重置必须要保留,否者容易被绕过 */
    (function () {
        if (window.top !== window.self) {
            setInterval(function () {
                document.documentElement.innerHTML =
                    "不能放到 iframe 中";
            }, 1);
        }
    })();
</script>

XSS

参考 xss 文件

CSRF 跨站请求伪造

钓鱼网站中加入如下代码,链接为一个get请求,

<img src="http://localhost:2500/api/names/?name=hew" alt="no">

预防

前端:

  • 避免拼接 html
  • 输入的校验 数字 url 邮箱等
  • 验证码
  • Get 请求不对数据进行修改
  • 不让第三方网站访问到用户 Cookie
  • 阻止第三方网站请求接口
  • 请求时附带验证信息,比如验证码或者 token

密码安全

密码加盐,只能保证用户真实密码不会泄露,对于暴力访问破解,可以使用验证码拖延时间,或是限制访问次数

  • 单项散列函数:又称为消息摘要算法, 不可逆的加密算法,即对明文进行加密后,无法通过得到的密文还原回去得到明文。

采用 crypto-js 包 处理, MD5、SHA1、SHA256、SHA512

  • MD5 信息摘要算法(Message-Digest Algorithm):把一个任意长度的字节串变换成一定长的16进制数字串 做密码的保护,但是现在认为不太安全,更多是利用 MD5 的高度离散性特点,用它来做数字签名、完整性校验,云盘秒传等; 数字签名。我们可以在发布程序时同时发布其 MD5。这样,别人下载程序后自己计算一遍 MD5,一对比就知道程序是否被篡改过,比如植入了木马。 完整性校验。比如前端向后端传输一段非常大的数据,为了防止网络传输过程中丢失数据,可以在前端生成一段数据的 MD5 一同传给后端,这样后端接收完数据后再计算一次 MD5,就知道数据是否完整了

  • SHA 安全哈希算法(Secure Hash Algorithm)

import CryptoJS from 'crypto-js'
CryptoJS.MD5('123').toString()
CryptoJS.SHA1('123').toString() // 注意方法名的大小写
  • 对称加密算法 指加密和解密使用的是相同的秘钥,常用的有 DES、3DES 、RC4、RC5、RC6 和 AES 对称加密算法的优点是速度快,缺点就是不安全

  • 非对称加密 加密和解密用的不是同一个秘钥,安全性是要好于对称加密的,但是性能就比较差了

RSA (三个人名首字母)一种非对称加密算法

采用 jsencrypt 包,170K大小

  • node 可以直接用自带的 crypto 包

框架知识

angluar

  • 脏数据检测 触发事件调用$digest(遍历所有数据观察者,判断值是否变化),有变化调用$watch,然后再调$digest,2<=循环<=10,可批量检测出更新值再统一更新UI。

  • vue 与 react 差异 现代前端框架有两种方式侦听变化, pull 和 push

pull: react为代表 通常用 setStateAPI 显式更新,然后react 通过 virtual Dom Diff 找出差异 再 patch 到Dom上,所有react一开始是不知道哪里变化了,只是知道了有变化,然后通过暴力的diff知道哪里发生了变化 agular的脏值检测也是如此

push: vue就是在初始的时候会对数据行监听收集,一旦数据变化,就会立刻得知,所以vue是一开始就知道哪里有变化;这就导致了一个问题,一个数据就有一个watcher,一旦绑定的细粒度过高,就会产生大量的watcher,就会带来内存以及依赖追踪的开销,但细粒度过低又无法完成精准的追踪,所以vue采用了中等粒度的方案,即在组件级别进行push侦测,然后及时发现发生变化的组件,再在组件内用 Virtual Dom Diff 获得更具体的变化,所以vue是 pull+push的集合方式


M: 数据及数据的获取,页面模板等

V: html css

C: js 及 js操作框架

mvc: c中直接获取元素节点进行渲染

mvp:通过view提供的接口进行渲染

mvvm: 数据双向绑定

页面相关拦截

  1. 拦截 ajax 请求:修改 XMLHttpRequest 原型或重写 XMLHttpRequest

  2. 拦截 a 标签,将 a 标签的 src属性值替换,并加上点击事件

  3. 拦截点击事件,可以在父元素上添加事件,缺点是只能在捕获阶段拦截,而且拦截后,若直接阻止传递,原本挂载的事件也无法执行

websocket

html5 出的一个持久化的协议

基于 http 协议 利用其协议来完成了一部分握手,在握手阶段是一致的

请求头中会携带

Upgrade: websocket
Connection: Upgrade

表示告诉 Nginx等服务器 这发起的是 Websocket 协议请求

webSocket、Ajax轮询(客户端每隔多少秒发起一次请求)、长轮询(客户端发出请求,服务器等待有了结果再返回客户端,客户端收到消息后,又再次发起请求) 区别

单项推送消息 SSE 参考 https://juejin.cn/post/7325730345840066612

Last Updated:
Contributors: Warren, Warren