总结
驾驭技术的能力\
用健壮的代码去解决具体问题->>用抽象的思维应对复杂的系统->>用工程化的思想去规划更大规模的业务
收藏夹
抖音前端 详解js基础类型
js rest 参数实现 重载!
单页面应用与多页面优缺点
单:
优点:
1. 用户体验好,页面局部刷新,页面之间切换块,并且可以加过渡动画
2. 前后端分离更彻底,服务端压力更小
3. 有利于组件与资源的共用
4. 数据传递更加方便
缺点: 不利于seo,首次加载资源较多,开发成本上手难度高
多:
优点:
1. SEO优化
2. 上手简单
ES678910.....
收集关于js的有趣新特性
...
// 作为拓展运算符
console.log('apple', ...['banana', 'car', 'door']); // apple banana car door
// 作为rest运算符
// rest 运算符就是把js传进来的多余的参数,合并为一个数组,便于操作
var arr = ['apple', 'banana', 'car'];
function fn(...args) {
// 作为rest运算符
console.log(args);
// 输出 ['apple', 'banana', 'car']
};
fn(...arr) // == fn(apple,banana,car)
Class
class 其实是一个语法糖,实现了更接近面向对象语言的类机制
class MyClass {
// constructor 每一个类都有的默认构造器 new myclass() 调用此方法
// 不声明默认返回this
constructor(x,y){
this.x = x; //这里的x就是hasOwnProperty ,非私有属性和方法都存在class的prorotype上
this.y = y;
}
test(){
console.log(this.x);
}
}
let _newClass = new MyClass(1,2)
_newClass.text()// 1;
// 因为this. 是私有属性,而如果引用的是class上的属性方法,就会报错,因为this上下文环境变了
var let const 区别
- var 声明的变量会挂在到window上,let,const 不会
- var 会发生变量提升,let const不会
- let 与 const 形成块级作用域,var 不会
- 同一作用域下 var 可以声明重复变量,let const 会报错
- 存在暂时性死区问题
- const 声明时必须赋值,如果赋值为引用类型,可以改变内容
IIFE 立即执行函数
//function js引擎规定如果出现在开始,一律识别为语句
// 所以这样就报错了
function(){}()
// 于是 第一种写法 () 就相当于自己调用自己
(function(){
//....
}())
// 第二种写法 先声明() 后调用()
(function(){ ... })(args)
个人介绍
个人优势
熟练掌握Vue技术栈,掌握Flutter跨端技术,基本掌握Threejs 3D可视化开发,深入了解js,vue底层原理,了解前端工程化及基本配置,自行实践过vue3+vite+typescript项目开发,搭建个人博客网站,提升技术深度,热衷于学习新兴技术,自行学习nodejs与uni-app,拓宽技术广度,了解Serverless,WebAssembly,乾坤前端微服务,低代码平台等新兴技术
手写题之原理
- 实现一个new 操作符 js 里的new主要执行了如下几步
- 首先创建一个指向构造函数原型的对象
- 在对象中添加子对象的自定义属性
function mynew(fn,...args){
let obj = Object.create(fn.prototype);//1
let res = fn.apply(obj,args);//2
if(res && (typeof res === 'object' || typeof res === 'function')){
return res;
}
return obj;
}
- 实现一个 apply call bind
// call apply 区别只有传入的值,call 只能为参数列表 需要..rest,而apply 可以接受数组
function myCall(context,...args){
// 如果没有传入context 默认为window
if( !context || context === null ){
context = window;
}
// 存储 this, 这里this指向调用apply的函数
let fn = Symbol();
context[fn] = this;
// 调用并返回 其实就是相当于把自己传入context并调用了
return context[fn](...args)
}
- typeof 实现原理 与 实现一个instanceof
js 在底层 通过前1-3个低位的机器码 存储类型信息
000 object
010 浮点数
100 字符串
110 布尔
1 整数
但是对于null 与 undefined 的判断比较特殊null判断为全部为0,undefined:用 −2^30 整数来表示 因此 null 就被识别为了 object 因此需要避免问题 还有一种比较好的判断方法 Object.prototype.toString.call(类型变量)
instanceof原理 object.proto == function.prototye 说明object 为function实例
function myinstanceof(left ,right){
// 一直循环, 知道匹配或者找到原型链顶端
while(true){
// 是不是找到 Obeject.prototype._proto_ === null 了
if(left._proto_ === null){
return false
}
// 没有就继续判断
if(left._proto_ === right.prototype){
return true;
}
// 都不是 继续沿着原型链找
left = left._proto_
}
}
Generator 迭代器
js模块化编程规范
原先的js只为了简单的交互,但发展到如今越来越庞大,项目逐渐复杂,需要模块规范. 最原始的一个函数极为一个简单模块,但是造成全局变量过多,命名出现重复,因而加入了nameSpace,也就是一个对象属性,但是这样既能访问也能修改模块内容,并不安全合理,因此加入了IIFE(闭包)来解决私有化问题,并在window对象上暴漏属性,做到及可以访问又不能修改,那么怎么解决模块之间的相互依赖呢? 这就需要在IIFE中引入依赖
- commonJS 一个文件极为一个模块,因为是同步加载的,并不适用于浏览器的场景,常用于nodejs 服务端规范,默认导出语法为 module.exports = value , 或者 exports.value = value
引入语法为 require(),参数为模块名或者模块文件的路径,实现原理就是找到此模块js,然后执行一遍,并返回exports对象的值,这里加载的就是module变量里的exports属性的值,需要关注的一点就是,这里返回的值是拷贝的值,类似于值传递,因此当文件加载完后,返回了值,再次修改文件里的原始属性值并不会修改返回的值,因为外部的值已经对原始属性值进行了缓存,除非用函数返回值来改写 - AMD规范是同步的,但是写起来很复杂
- CMD规范 整合了两者优点
- 常用的Es6模块规范 ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 引用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
// 上述需要知道模块内部的参数名称
// 加一个默认导出
export default function () {
console.log('foo');
}
//模块默认输出, 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
模块化可以 提高复用性,提高维护性,更好的分离代码,便于按需加载,同时避免了命名冲突
柯里化
柯里化就是把函数的参数控制为1个剩下的参数通过内部return出来的函数继续处理,以此类推
设计模式
设计模式是前辈积累的方法论,是类似于游戏攻略的存在
设计模式的核心思想是封装变化,将变与不变隔离,变的部分灵活,不变的部分稳定,帮助我们写出健壮的代码
设计模式分为了三大类封装创建对象过程变化的创建型 封装对象之间组合方式变化的结构型 封装对象里千变万化的行为的行为型
构造器模式
构造器模式就是我们抽象出来的 类
function Person(name,age,sex){
....
}
// 构造器模式与工厂模式的区别就是
// 构造器模式是对对象的共性提取 工厂模式是对类的共性提取
发布订阅模式 与 观察者模式
二者是不一样的,观察者模式 松耦合,发布订阅无耦合,因为加了个中间层
实现发布订阅模式
思路: 一个维护事件与订阅回调的对象 "event1": [cb1, cb2] ,订阅,取消订阅,发布事件.
class EventBus(){
// 初始化方法
constructor(){
this.events = {}
};
// on('',()=>{})
on(type,callback){
if(!this.event[type]){
// 新建
this.events[type] = [callback];
}else{
// 加入
this.events[type].push(callback);
}
}
// emit('',arg1,arg2);
emit(type,...rest){
this.event[type] &&
this.event[type].forEach(fn => fn.apply(this,rest));
}
// off('',fn)
off(type,callback){
if(!this.events[type]) return ;
this.events[type] = this.events[type].filter(
iten =>{
return item != callback
}
)
}
// once('',fn)
once(type,callback){
function fn(){
callback();
this.off(type,fn)
}
this.on(type,fn);
}
}
移动端相关
移动端适配问题
- 像素 像素分为设备独立像素和物理像素,物理像素就是设备的实际像素,但是因为像素越来越高,就会导致之前的页面缩放,于是提出了视网膜屏的概念,将高分辨率设备的多个像素,作为一个设备独立像素,之前用300个像素点渲染,现在就会用300*4个像素渲染,保证页面精致的同时不缩放页面
- 设备像素比
设备像素比
device pixel ratio简称dpr,即物理像素和设备独立像素的比值。 在web中,浏览器为我们提供了window.devicePixelRatio来帮助我们获取dpr。 在css中,可以使用媒体查询min-device-pixel-ratio,区分dpr: - 1px 问题
由于我们常用设备独立像素来适配页面,导致1px看起来很粗
解决方法:利用媒体查询,加伪类,配合transform:scale 来进行缩放
.border_1px:before{
content: '';
position: absolute;
top: 0;
height: 1px;
width: 100%;
background-color: #000;
transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio:2){
.border_1px:before{
transform: scaleY(0.5);
}
}
@media only screen and (-webkit-min-device-pixel-ratio:3){
.border_1px:before{
transform: scaleY(0.33);
}
}
还可以利用 postcss的 postcss-write-svg 配合 border-image 实现
- 前端适配方案
rem(基本不用了) , vw,vh+postcss-px-to-viewport(视口)
后者有两个主要问题,其一是可能不会整除,发生像素偏移,二是如果用margin:px+ 宽高v定位,会导致整体宽高大于100%,这是可以使用padding,拿进去,或者使用calc计算
安全问题
- XSS 跨站脚本攻击 分为三种 反射型xss,通常为诱导用户点击,发送一个包含非法请求的url,get请求,服务端处理后返回给前端,相应的恶意代码也会混杂在其中执行,解决方案是后端对get url 进行encodeURIComponent(),屏蔽掉恶意代码,第二种是dom xss攻击,比如用户在一个输入框中,输入了包含script标签的代码,如果为innerHtml,appendChild ,document.write等可以将代码原样解析的api 就会执行恶意代码,很危险,正确做法需要对输入的内容进行转义,最后一种是存储形xss,也就是通过一些列手法把恶意代码存储到了数据库中,每次有人访问都i执行恶意代码,解决办法也是前后端转义,另外 服务端配置只加载同域资源,禁止外域脚本执行,也可以限制输入框字符长度等手段 2 csrf 跨站请求伪造 是指如果我登陆a网站,并未退出或者保留了登录状态,那么黑客可以诱导我点击某个链接或者图片,进入到其他网站而在这个网站回自动发送一些,藏在图片src中,自动提交的表单中的恶意代码,完成攻击 解析,防御:主要是需要提高服务端的安全性,比如使用token,验证源地址,origin和 refer 但是这不是很安全,因为可以修改隐藏,再就是充分利用好 Cookie 的 SameSite 属性.
WeakMap WeakSet
WeakMap与Map类似,但有几点区别:
1、WeakMap只接受对象作为key,如果设置其他类型的数据作为key,会报错。
2、WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。
3、由于WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。
4、没有clear()方法
5、不能遍历
学习学习算法喽
涉及知识:动态规划,回溯,数组遍历,链表,二叉树,排序的原理
常见的数据结构
-
数组
按照索引查询很快,依次遍历数据也比较方便数组的大小创建后就无法扩容了,(js除外,js里数组是个对象),添加,删除比较复杂,性能消耗高,因为要移动后续值
-
链表
单向链表只能单向查找,而双线链表可以双向查找,链表是一种非连续的存储式结构,但是数组必然式一块连续的内存,因此链表的插入删除可以达到o(1)的复杂度,只需要改变引用即可,因此他的内存也是动态的
优点: 不需要初始化容器大小,插入删除很迅速,可以任意添加元素
缺点: 需要储存引用,占用的内存更大,查找元素时需要遍历,耗时很多 -
栈 先进后出\
-
队列 先进先出,从队尾部插入从队首部读取删除\
-
树 非线性结构 有序树 二叉树 平衡二叉树
-
堆 堆是一颗完全二叉树 分为最大堆最小堆
-
图
-
哈希表 集合了数组与链表的有点 就类似于hashMap
js
隐式类型转换
数组与字符串操作
0.判断数组是否存在某个值
- includs()
- indexof 包含返回下标 否则返回-1
- find
- findIndex
- 替换字符串指定内容
str.replace(/正则语法/匹配规则,'替换为')比如str.replace(/\s/g,'%20)替换所有空格为%20 - 初始化数组
Array(100).fill(0)初始化100个值为0的数组
初始化二维数组Array(10).fill(0).map(() => Array(5).fill(0)); - 数组的累加器
Array.reduce()
arr.reduce(function(prev,cur,index,arr){
...
}, init);
接收一个函数用作递归调用,函数接收一个pre,cur,分别为初始值或者为上次函数返回值,与cur当前值做运算,返 回给下一次调用// 求数组的和 let arr = [1,2,3,4,5]; arr.reduce((prev,cur)=>{ return prev+cur; },0); // 求数组最大值 arr.reduce((prev,cur)=>prev > cur ? prev : cur) // Math Math.max(...arr); // 数组去重 newarr = arr.reduce((p,c){ // 第一次p为[] // 在p中查找数组第一项,肯定找不到,push进去 // 返回p数组给下一次调用 // 在p中找数组第二项,如果找不到,push,找到了,直接返回p数组 p.indexof(c) === -1 && p.push(c); return p; },[]) - 数组元素转化
转化为数字const array = ['12', '1', '3.1415', '-10.01']; array.map(Number); // [12, 1, 3.1415, -10.01] // 为array的每一项执行Number(x); const c = console.log.bind(document) c(996) c("hello world")- 数组去重 利用Set原理
[...new Set(arr)] - 数组扁平化 利用数组reduce累加器 一直解构数组
function flatter(arr){
if(!arr.length) return;
return arr.reduce((pre,cur)=>{
Array.isArray(cur)? [...pre,...flatter(cur)]:[...pre,cur]
},[])
}
利用迭代的方法解决 每次concat() 函数都会把参数里的数组 转为数组的值解构出来,一直循环到arr 中没有数组
function flatter(arr){
if(!arr.length) return;
while(arr.some(item => return Array.isArray(item))){
arr = [].concat(...arr);
}
return arr;
}
Array.from()方法对一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
let map = new Map();
let arr = Array.from(map);
Array.from([1,2,3],x=>x+1),第二个参数是一个回调函数,每一项都会执行此函数返回值
- Map 与 Set
map 的方法: clear delete set get forEach has size
set 的方法: add clear delete has forEach size
array 方法: push pop shift unshift concat join reverse slice splice sort indexof lastIndexOf
HTML 5
< ! doctype html>
<html>
head
title
head
body
body
<html>
- 新特性\
- 语义化 便于维护阅读,便于搜索引擎SEO,方便其他设备根据语义渲染页面
- video audio
- canvas svg
- 智能化表单
- 地理定位API
- web worker
- web Store
- data-
<div id='div1' data-age = '16></div>然后获取id dom 用 div1.dataset.age 获取 也可以添加 - className 用来设置元素类名
- SEO h1 h2 h3,图片Alt,语义化标签,设置不重复的meta
原型链大家族
执行上下文
js 里的执行上下文包含 变量对象,作用域链,和this,全局上下文的变量对象就是全局对象,浏览器端js的全局对象包含直接在js最外层声明的变量和一个window属性,window对象指向自己,还包含一大堆预定义的函数,比如Math\
函数的执行上下文分两个生命周期,首先是进入准备运行阶段,在此阶段会创建变量对象,也就是变量提升阶段创建VO对象,下一步执行阶段就会具体的执行赋值操作,这一部变量就准备好了,可以交给js引擎访问了,因此也叫活动对象AO.
js 里的继承实现
js 实现继承 分两类 一类是函数继承,也可以叫做构造函数继承,第二类是对象继承,也就是非构造函数继承
下面来讲讲
- 构造函数继承 构造函数继承很好实现,毕竟原型链的继承就是围绕构造函数展开的
// 父类
function Animal(){
this.species = '动物'
}
Animal.prototype.run = (){
...
}
// 子类 通过apply 实现继承
function Dog(){
// 可以直接用apply 调用父类构造函数
Animal.apply(this)
this.name = '哈士奇'
}
// 子类 通过原型链实现继承
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.construtor = Dog;
// 上面这两句,内涵很深
// dog的原型直接指向animal的原型,节省了内存,提高了效率,不需要创建animal,而用Object.create()
// 是创建了一个原型为Animal.prototype的空对象 用于给dog继承后,虽然指向了同一个对象地址,但是你在
// 修改了constructor后,并不会把Animal 原来对象的地址修改了
// 封装一个
function extend(child,parent){
// 媒介对象
function F(){};
// 设定原型
F.prototype = parent.prototype;
// 设定继承 并回调构造函数属性 保证原型链正确
child.prototype = new F();
child.prototype.constructor = child;
}
- 非构造函数继承 非构造函数无法直接设置prototype ,但是可以狸猫换太子,给他加一步
var cat = {
name :'加菲猫'
}
var animal = {
base = '地球'
}
// 那么如何实现继承?
// easy
function objectExtend(father){
var f = function(){};
f.prototype = father;
return f();
}
var cat = objectExtend(animal);
// 剩下的方法就是深拷贝了 ,直接把父对象的属性,全量copy 过去
what's THIS?
this是在代码执行上下文创建时确定的一个在代码执行过程不可更改的变量
- this的四种情况分析:直接调用,作为对象的方法调用,构造函数调用,使用apply call bind
首先直接调用,也就是在全局环境直接调用或者在函数中调用,这时是指向window(全局对象的)的,有一个特例就是当为严格模式,会指向undefined,当作为对象的方法调用会指向此对象,当构造函数调用时,this会指向新创建的对象,当用apply call 时,myFun.apply(obj,[arg1,arg2])会将myfun 作为一个属性传给 obj ,然后调用obj.fn(arg1,arg2),此时this就指向obj了.最后时箭头函数,箭头函数的this指向在词法分析阶段就确定了,直接指向箭头函数所在的上下文环境.
深拷贝与浅拷贝
首先明白js 对于对象是引用传递,浅拷贝就是只复制了指针,所以两者同步变化,深拷贝就是单独开辟了内存空间,各自维护数据
- Object.assign 实现浅拷贝
var obj = {a:1,b:{c:0}},
var obj_copy = Object.assign({},obj);
2. for in 遍历属性实现浅拷贝
``` js
var obj_copy ={};
// for in
for(let i in obj){
// 判断是否为本地属性
if(obj.hasOwnProperty(i)){
obj_copy[i] = obj[i]
}
}
- 递归调用实现深拷贝
function cloneDeep(target){
let ctarget = {};
if(typeof target == 'Object'){
// 遍历属性
for(let key in target){
ctarget[key] = cloneDeep(target[key])
}
return ctarget;
}else{
return target
}
}
// 上述代码 可以实现对基本代码的深拷贝,但是没有考虑数组对象
// 稍作修改
function cloneDeep1(target){
let ctarget = Array.isArray(target) ? [] : {};
if(typeof target === 'object'){
for(ley key in target){
ctarget[key] = cloneDeep1(target[key])
}
return ctarget
}else{
return target
}
}
// 解决循环引用问题 并精准判断对象 考虑symbol
function isObject(val) { return typeof val === "object" && val !== null; }
function cloneDeep2(target, map = new WeakMap()){
if(isObject(target)){
// 兼容数组,防止数组变成对象
let cTarget = Array.isArray(target)? []:{};
// 解决循环引用 防止爆栈,解决互相引用问题
if(map.get(target)){
return map.get(target)
}
map.set(target,cTarget);
//可以用 Reflect.ownKeys(target) 来获取包含Symbol的私有属性
for(const key in target){
cTarget[key] = cloneDeep2(target[key]);
}
return cTarget
}else{
// 一直遍历到基础数据类型,所有的数据类型都是在这返回出去的
return target
}
}
JSON.parse(JSON.stringify(obj))
缓存
强缓存的弊端: 当发布更新时,资源虽然更新了,但是依旧从本地缓存读缓存,导致看不到更新 解决方案: 考虑到让url的修改与文件内容相关联即只有文件内容变化时,才会导致相应的url的变更,从而实现文件级别的精确缓存控制。
- 强缓存,两种字段 expire时间节点,盒catch-contro: no-catch...,返回200(use store)
- 协商缓存,顾名思义就是和服务器进行协商,有两种方式,一是last-modify和if-modify-scene,首先服务器返回请求头携带一个last-m,之后发送请求在请求头添加if-m-s 值,就是last-m的值.判断是否一致,一致返回304,不一致返回200,请求头携带新的last-m
第二种通过 Etag 和 If-None-Match . Etag 是根据文件的内容生成签名的,虽然文件名称可能周期性变化,但是只要文件内容不变,对应的Etag就不会改变,可以精准的控制缓存,浏览器会首先判断Etag再判断last-modified
性能优化
渲染过程中,如果遇到<script>就停止渲染,执行 JS 代码。因为浏览器有GUI渲染线程与JS引擎线程,为了防止渲染出现不可预期的结果,这两个线程是互斥的关系。 JavaScript的加载、解析与执行会阻塞DOM的构建,也就是说,在构建DOM时,HTML解析器若遇到了JavaScript,那么它会暂停构建DOM,将控制权移交给JavaScript引擎,等JavaScript引擎运行完毕,浏览器再从中断的地方恢复DOM构建
Js:
- 关于defer与async 其中蓝色线代表JavaScript加载;红色线代表JavaScript执行;绿色线代表 HTML 解析
首屏优化流程图
性能定位与分析
- 前端性能监控-埋点
- window.performance
- 开发者工具的performance 与 lighthouse
- 性能指标
性能指标
- FP 首次渲染触发时间,就是白屏时间,一直到检测到任何渲染时
- FCP 首次内容渲染时间,页面出现有效信息的时间
- FMP 首次关键有效内容出现时间
- LCP(largest content paint) 视窗可见最大图片或文本等渲染时间
- long task 是指执行时间超过50ms的任务
- TTL 从页面开始渲染到能够流畅响应用户输入的耗时
- FID 从用户输入到浏览器响应交互的时间
- CLS cumulative layout shift 所有布局偏移分数的总汇总
三个比较关键的 LCP FID CLS
浏览器内置了performance API,用于性能监控 此对象包含了 内存 路由 以及各种起止时间
我们可以直接调用
performance.timing 来打印基本信息
performance.getEntriesByName(name) 来获取指定名称的性能指标
performance.now() 可以打印出一个耗时任务的时间
Dns
首先在本地查找Dns 缓存,没有找到以此去本地域名服务器,根域名服务器,顶级域名服务器等找
优化 : Dns本地缓存 Dns负载均衡 根据地理位置,承载数量决定dns 服务器
TCP 协议
首先Tcp协议是一种面向连接的,提供可靠传输服务的协议
面向连接 就体现在需要三次握手建立可靠连接
- 三次握手: 第一次握手,客户端发送syn同步包,加入seq =x ,将SYN标志位 = 1,ACK = =0,并进入SYN_SEND等待确认状态,第二次握手,服务器端收到请求连接包,若是同意,发送SYN 包(seq = y),和ACK(ack=x+1) 确认包,并进入SYN_RECV状态,第三次,客户端收到SYN和ACK包之后,发送AC(ack=y+1),发送完毕后,两者进入建立连接状态。
为什么三次 。三次能确认两边都是能收能发,不会出现失效的连接,防止浪费资源,而进行四次握手并不能提高到100%,无法显著提高稳定性,还浪费资源
- 四次挥手:
HTTP
Http是基于tcp/ip协议的超文本传输协议,是一种无状态协议,用于在服务器端与客户端传输数据,包含请求与响应两部分
- Get与post的区别
Get在浏览器回退时是无害的,而post会重新请求
Get的参数在 URL 地址显示的,而post不会显示在地址栏,因此Get比较不安全
Get 请求会被浏览器主动Catch 而post不会除非手动设置
Get 只能进行Url编码,而Post可以进行多种
Get 请求的URL 有长度限制
Get 只能接受ascii 编码的参数
-
常见状态码
200 成功 204 无内容 301 永久移动 302 暂时移动 304 使用缓存未修改 400 语法错误 401 无权限 403 拒绝访问 404 not found 422 无法处理 500 内部错误 502 bad getway -
http 2.0
http2.0 ,每一个同域下只建立一条tcp链接,通过优先级与二进制分帧达到优先发送重要数据,可以乱序接收数据然后排序的传输方法
二进制分帧传输: 每一帧加入单独头部序号帧,可以乱序发送接收,再排序
多路复用: 复用一条tcp链接,节省资源,以流的形式传输
头部压缩: 重复的头部键值对存在缓存表中,如果不加请求头默认使用之前的请求头配置
服务器推送: 服务器可以主动推送文件,客户点可以拒绝文件,遵循同源策略,主要可以提前发送已将准备好的资源 -
http 3.0
基于UDP
Https
http+ssl 加密
ssl 用到混合加密,用对称加密保证效率,非对称加密确保通讯安全,具体来说,首先客户端会有一对密钥,包含私钥,和公钥,首先通过CA证书机构颁发的密钥加密公钥,也就是签名,然后传输给客户端,如果此时中间人截取到了加密后的服务端公钥,利用CA机构对应的公钥解密了,看见了我们传输的公钥,因为公钥本来就是公开的,看见没事,但是如果他篡改了公钥,比如改成自己的生成的一堆密钥的公钥传输给客户端,那么再次截获取,客户端密文时就可以用自己的私钥解密,从而获得对称密钥,但是因为有了CA机构的密钥签名,如果他修改了服务端公钥,等客户端收到时,利用CA公钥解密就会发现,解不开!也就知道被截胡了,停止通讯! 如果一切正常,验证签名通过,那么客户端就用这个公钥加密对称加密密钥了,然后传输给服务端,服务端用密钥解密,就获得了对称加密密钥,就可以加密明文,传输信息了.
网络基础知识
网路安全基石 - 同源策略
如果一家银行网站你登陆之后,去浏览别的网站,而其他网站可以获取银行的cookie,那会有很大的安全风险,因此同源策略
- 一限制cookie,session,local不能读取
- 二限制DOM无法获取
- 三限制Ajax不能发送请求 这些限制是必须的,但也会导致跨域问题
跨域问题
典型的例子比如iframe与window.open打开的窗口,如果跨域就无法与父级通讯
如果只有二级域名不同,可以通过设置document.domain 为一级域名解决跨域问题
如果完全不同有以下解决方法\
- 访存dom方法
- 利用#hash修改url,另一边hashchange监听
- 利用window.name
- 利用postMessage 官方方法
- ajax 方法
- 利用jsonp jsonp,就是创建一个script标签,利用标签的get开放策略,去获取src地址里的信息,而src里必须包含一个callback,返回值为callback(response),会直接执行脚本
- 利用websocket ,websocket可以设置origin来让服务端许请求
- 最终解决方案 cors(Cross-origin resource sharing)跨站资源共享
浏览器发现这次跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段,而服务端通过配置Access-Control-Allow-Origin为origin值或者为 * 来允许请求,浏览器判断是否返回有该字段,判断是否跨域报错 - Vue 常用的开启代理 原理就是 服务端的转发并不会触发浏览器的同源策略,所以啊啊a.com 请求 b.com,只需要开启一个nginx反向代理,将b.com 的响应转发到 a.com服务器,就可以了
Ajax
通过xmlHttpRequest 实现浏览器异步请求,但是json 格式比xml更具优势
fetch
Fetch API是一个用用于访问和操纵HTTP管道的强大的原生 API
浏览器解析页面
-
关系
当解析HTML构建dom树时,如果遇到js脚本代码,无论是内嵌的还是外引的,因为不知道js是否操作了dom,因此需要堵塞html解析,进行js下载与执行,而如果js里操作了css,就需要用到完整的样式表,因此需要优先下载css文件,并构建cssom树,也就会堵塞js执行. -
解析过程
首先HTML解析器解析HTML文档形成dom树,同时css parser 解析css文档形成cssom 树,然后将两棵树合并为一颗渲染树,这里会先计算每个盒模型的位置与大小,确定文档流,因此如果重新走这一步也叫回流,比如修改元素的位置,元素尺寸,内容变化,设置style,display,字号,读取offset\client\scroll 家族属性,浏览器窗口大小等,性能消耗很大,需要尽量避免,布局之后就是绘制,这一步叫重绘,对元素的颜色,visible,背景等的修改都会触发重绘. 为什么某些访问属性会立即引起回流,因为这些属性需要拿到最新的布局信息,而浏览器维护一个队列,保存着需要进行的会流与重绘操作,当到达一个阈值时,就会统一执行一次回流重绘,合并多次为一次,而当访问某些属性时,会导致浏览器强制清空当前队列,保证获取到最新的布局信息 -
优化的点
css:
尽可能在DOM树的最末端修改样式,让每次回流影响的范围最小化
将动画效果应用到position为absolute与fix的元素上,脱离文档流,不触发完整的回流
避免设置多层内联样式
js:
避免频繁操作dom 样式,可以直接修改class 类名
避免不了 就先设置为display :none ,然后再修改
避免读取会触发页面回流的属性 比如 offset\client\scroll 家族,可以存起来
复杂动画元素使用absolute,防止造成父子元素频繁回流
that is all
线程与进程
进程时为一个应用程序运行所分配的系统资源,而线程是进程的某一段执行代码,多线程是指并发执行某些代码,多进程是☞系统可以运行多个程序
浏览器是个多进程应用
- Browser 进程 ,浏览器主进程,负责与用户交互,管理标签页,管理网络资源等
- 第三方插件进程,每一个插件对应一个独立进程
- GPU 进程,最多一个,用于加速绘制,或者3D绘制
- 浏览器内核进程 包含多个常用线程,默认每个Tab页面一个进程,互不影响
- GUI 渲染引擎线程
- js引擎线程
- 事件触发线程: 归属于浏览器! 可以理解毕竟js引擎任务繁重,需要浏览器另起线程,此线程就是事件循环线程,当执行 setTimeout setInterval 或者鼠标点击回调 ajax回调等js主代码,都会加入到宏任务队列中,排队等待js执行
- 定时器触发线程 因为js为单线程,为了不影响定时器,单独线程计时,⏲完成,回调就会加入到任务队列中排队
- 异步http请求线程
事件模型
Event 是所有时间的原型,主要实现了3个方法,addEventLisener,remove...,dispatchEvent();
dispatchEvent('type') 可以手动出发某个事件
add...的第三个参数可以是bool 和 配置对象
- `capture`:布尔值,表示该事件是否在`捕获阶段`触发监听函数。
- `once`:布尔值,表示监听函数是否只触发一次,然后就自动移除。
- `passive`:布尔值,表示监听函数不会调用事件的`preventDefault`方法。
如果监听函数调用了,浏览器将忽略这个要求,并在监控台输出一行警告。
事件委托
原理: 利用事件冒泡机制
将原本需要在子组件一一绑定的事件统一绑定到父组件上,在父组件中通过冒泡捕获的事件对象来判断 目标id,实现事件代理,优点是: 减少内存消耗,减少多余的dom操作,在一定程度上优化性能,其次是可以实现动态绑定,动态加入的子组件都可以响应事件
require 实现原理
全局维护一个对象,作为缓存,当你引入一个文件时,执行此文件,碰到module.exports,将返回的值与文件名,作为键值对存在全局对象中,下次引入,直接返回
apply call bind
三者用于改变函数this的指向,第一个参数传入this要指向的对象,默认指向window,apply 接收一个数组 apply(null,[one,two,three]),call 接受的参数列表call(null,one,two,three),而bind 返回的是一个绑定了this的函数方便之后调用
我的理解:
Math.max.apply(null, [1,2,3]); // 也会返回 3
比如这个例子,我理解为: 在第一个参数指向(null 也一样)的 上下文环境中,附带着传入的参数[1,2,3] ,来执行函数 本质作用就是给当前函数换一个上下文环境,也就获得了不同的参数,而这里bind() 返回的正是这个把上下文和函数结合起来的新的函数.
闭包
又是一个大专题 加油!
闭包是指有权访问另一个函数作用域中的变量的函数 ——《JavaScript高级程序设计》
当函数可以记住并访问所在的词法作用域时,就产生了闭包,
即使函数是在当前词法作用域之外执行 ——《你不知道的JavaScript》
图来自
大佬解释闭包原理,怎一个透彻了得!
背景: 闭包是为了解决 子函数未被调用而父函数已经执行完毕了,需要销毁时发现子函数引用了自己作用域的变量
咱一点点解释
1.为什么子函数没调用而父函数却执行完了?
// 正常来说 顺序执行 没问题
function father(){
const a = 1;
function child(){
console.log(a);
};
child();
}
// 但是js 里 函数可以return出去!
function father(){
const a = 1;
return function child(){
console.log(a);
};
}
let child = father();
child 就可以任意时候执行了,那么问题来了father 销毁吗? 销毁了的话, console.log(a) 打印 undefined 了, 这可不行,于是便有了闭包
通俗理解就是 父函数:" 你需要a? 那我打包给你,你存起来带着走吧,我销毁了,不多占内存了".
闭包们储存在属性[[scopes]]里,复数也可说明打包的闭包并不会合并,比如父一个 ,爷一个,祖师爷一个,而需要注意的是Global全局作用域总是会打包进去,这也是为什么函数无论在哪调用,都能访问到全局变量的原因
2.不销毁父函数行吗?
不行,性能消耗太多
3.都打包哪些变量?
只会打包在静态作用域链中用到的变量,这里的静态作用域链不是啥高级东西,就是一个函数,代码块等会形成一个作用域,就类似于函数的互相嵌套,作用域链也分父子,也会在自己的作用域中找不到,就去问爸爸要,还没有就去问爷爷要,一直找到全局作用域,这就是作用域链.
4.Eval
eval()函数会打包所有作用域,因此会产生性能问题,因为无法检测会用到哪些调用,毕竟你传入一个字符串是动态的,只能全部打包,因此少用
5.具体闭包是存在 堆 中的\
- 闭包的具体应用 防抖节流函数 闭包可以用于在对象中创建私有变量
- 闭包与函数作用域可用于实现let
事件循环题目
js设计之初为了网页交互,为了避免多线程同时操作dom设置为单线程,而为了解决单线程导致的堵塞问题,加了一层调度逻辑,也就是loop循环,和task队列,把会堵塞的任务比如定时器,网络请求,UI渲染,事件和IO等放在其他线程执行,从而支持了异步,当异步任务执行完成会把回调加入到任务队列中,告诉主线程可以继续执行了.为了解决高优先级任务调度,加入了微任务队列,每次都会插队执行完成所有微任务.这就是浏览器的事件循环机制
而nodejs 需要有更精细化的事件循环机制来保证服务端的高性能,精细化在优先级除了分宏微任务,在每一个任务内部区分了更细的优先级,宏任务分为Timer>Pending>Poll>Check>Close,微任务区分process.nextTick()与其他微任务,每执行一定数量的某一优先级任务,就清空微任务队列,然后执行下一优先级的一定数量的任务,在清空微任务,以此类推,特别注意如果没有任务,会堵塞在poll,等待IO事件
如何做题?
0.第一次执行栈执行完毕,也就是主线程跑完所有代码,会把微任务队列里的任务依次全部执行,然后再从宏任务队列取一个任务执行,执行完全部再去清空微任务队列,以此类推,就是事件循环
1.牢记 setTimeout setInterval 为宏任务 promise nextTick 为微任务
2.记住第一次运行promise().then()时,就会运行到then函数之前,并将then的回调函数,加入到微任务队列最后去排队
3.注意观察promise的resolve或者reject 才会运行then或者catch的回调函数
4.注意await async ,await someasyncFun(); 之后的语句相当于promise的then,处理也一致
为什么用settimeout 模拟setinterval
原因: 首先两者都是在计时结束后,将任务回调推入到任务队列中,而后者会检查队列中是否已有实例,如果有未执行的实例就会跳过当前循环,进入下一次计时,这就导致了如果等待时间过长回调函数会被跳过
而用前者模拟,虽然有可能会连续执行,但一定会执行多少次
``` js
function myInterval(fn,t){
// 用time模拟实现循环,肯定得一直循环,那么就是递归了
// 每一层递归创建一个timeout
let timer = null
function _interval(){
fn();
timer = setTimeout(_inerval,t)
}
_interval();
return {
cancel:()=>{
clearTimeout(timer);
}
}
}
```
Vue
!"加油"!
-
vue 生命周期详解 再beforeCreate 之前,只初始化了生命周期函数,事件机制,渲染函数,之后触发再beforeCreate钩子,预构建完成,开始构建Vue实例,先初始化inject,再依次初始化props/method/data/computed/watch,然后初始化provide,到这里就构建了vue的基本数据,成为created,可以访问数据了,因此可以在这里请求后端数据了,做一些数据处理,之后调用$mount(el),执行挂载操作吗,如果有render加载render,然后执行beforeMounted,之后把渲染好了的html元素挂载到目标上,然后调用mounted,以此再mounted里就可以操作dom,获取dom了
-
vue 优化策略\
- 非响应式的数据放在data之外
- data 对象层级不要太深 影响性能
- watch 与 computed的使用场景
- 组件销毁时 要清理定时器,事件绑定,全局变量等防止内存泄漏
- v-if v-show 使用区分
- v-for 使用唯一不变的id等值作为key
- 懒加载
- ssr 与 预渲染
- 防抖与节流
-vue 修饰符
- .stop 阻止事件继续传播
- .prevent 阻止标签默认行为
- .capture 启用捕获模式,先自己执行,再传递给子元素
- .self 只有target 为当前元素时 ,也就是自己为事件源才会执行
- .once 触发一次
- .passive 不阻止默认行为
- .sync 一个语法糖 封装了默认update:value 的方法,直接$emit就可以,使用直接在后边加:value.sync = 'data'
-
组件通讯方式
- props 与 $emit 父子组件通讯
- children
- $refs 获取组件实例
- 父组件通过provide提供变量,在子,孙子组件用过inject 注入,推荐只在写组件库时使
- eventBus 事件总线的方式实现兄弟组件通讯
- vuex状态管理
-
vue nextTick
采用微任务优先执行的方式,比如promise,再下次dom更新结束之后立即执行回调方法' -
history与hash
- hash url包含# 但#之后的不包含到Http请求中,基于
location.hash实现,可以用浏览器,js,hashchange事件控制路由跳转 - history 基于H5的history Api,包括
pushState() replaceState(),可以在不刷新的前提下,操作 浏览器记录,也就为vue单页面应用提供基础,但是需要后端配合,毕竟没有hash,会把所有的url 包括前端配置的路由名称,后端是肯定找不到类似于 home login的页面的,只需要返回给我们index就行. - 调用
history.pushState()或history.replaceState()不会触发popstate事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back()或者history.forward()方法)
- hash url包含# 但#之后的不包含到Http请求中,基于
-
vue 响应式原理
源码讲解
原理讲解
一句话:首先响应式有三个需要实现的点,一个是侦听数据,然后需要收集用到数据的视图,最后是数据修改了自动更新视图\
侦听数据有两种方式通过Object.defineProperty() 来劫持数据,或者通过proxy 来代理数据
后两种解决方法,用到了观察者模式,每一个被劫持的数据有自己的dep()与对应的Watcher()类,形成当数据的get被访问,就会加入到dep的依赖列表中,而加入的就是watcher类,每一个watcher类都对应着一个视图,当set方法触发时,自动循环dep中的依赖列表,让每一个watcher类 也就是订阅者去更新自己的视图.
- vue 模板编译原理
通俗讲,首先会根据正则去匹配模板字符串里的开始结束标志,以及标签名和属性,不断的截取剩余字符串去解析,根据解析获得的开始,结束标签,属性值等组装成AST语法树,之后进行静态优化筛选,最后根据AST 生成render函数,
- vue 源码解析
- vue v-if v-show
v-if 渲染为一个三目运算符,渲染本身或者返回一个注释占位符 <! v-if >,v-show 通过操作 display实现 显隐 - vue v-for
不能用需要设置key ,为了添加diff虚拟dom树与dom树的节点对应,便于快速准确更新,而key 不要用 index ,因为index 的值会根据数据项的增删而变化,比如删除了第二项 后续的index都会与内容绑定发生变化,导致不必要的更新,影响性能
- vue v-if v-show
- 拓展 : 元素显隐方式
-
vue 单向数据流
vue 数据自上而下传递,子组件维护自己的数据但不能直接修改父组件数据,自上而下用props,自下而上用$emit,通知父组件去修改数据.
v-model是一个语法糖<input v-model = 'text'> <input :value='text' @input="val => text = val"> // 两者是一样的功能 无非就是把数据绑定与事件传递封装为一个语法糖 // 因此 基于v-model 可以实现简易版组件 Vue.component( 'custom-input', { template:'<input :value='value' @input='changeText($event.target.value)' type = text> </input>' props:['value'], methods:{ changeText(val){ .... let result =...; this.$emit('input',result) } } } ) // 进阶用法 model! // 高级东西 可以修改v-model 默认的 props(value),和 event(input) model:{ prop:'checked', event:'change' } // 这样v-model 就可以绑定在checkbox 上了!.sync依旧是一个语法糖
当你在父子组件传值的 prop上 加入 :vis.sync = 'dialogVisible' 时<com :vis.sync = dialogVisible></com> <com :vis="dialogVisible @update:vis="val = dialogVisible = val"></com> // 因此使用时 直接可以实现类似于双向绑定 this.$emit('update:vis',false); -
vue diff 算法
-
vuex vuex是一个集中式的数据管理方式,通过state,getter,mutation,action,在main.js中引入,Vuex.store()实例,
Vue 的打包相关
通过在vue.config.js 里配置webpack的打包路径等相关配置,
vue 与 react 异同
- 数据驱动试图,vue 可以进行双向绑定,视图自动更新,react 遵循数据不可变的单向数据流原则,更新视图需要setstate({});
- 组件时编程,vue与react都遵循组件化编程,vue 用的类似于html结构的template格式,比较易于上手,react 的jsx来声明UI
- virtual dom ,与 diff 算法,都构建了虚拟dom数来优化dom性能,diff 算法 用于新老dom 比较
Webpack 啊
webpack 是一个 静态模块打包工具,他会递归的生成一个程序依赖关系图,将所有模块打包成一个或多个bundles文件,这些均为静态文件,用于展示你的页面\
- 基础配置
mode可选参数development(打包更快,省略了代码优化部分),production(会启动tree shaking和代码压缩),none,
// webpack.config.js
const path = require('path')
modlue.exports = {
mode:'development',
entry:'./src/index.js',
output:{
filename:'bundle.js',
path:path.join(_dirname,'dist')
},
module:{
rule:[
// 这里就是模块的转换规则
// 将所有css文件转换
{
test:'/\.css$/,// $ 以.css结尾的
uae:['style-loader','css-loader'] // 从后往前执行 先用css-loader 解析css 再利用style-loader 动态添加一个style标签到html中,才能展示样式
}
]
}
}
webpack 只能原生理解json 与 js ,其他类型文件必须借助loader来处理
常用的loader: css-loader style-loader postcss-loader sass-loader babel-loader ts-loader vue-loader
webpack 可以通过plugin在各个周期执行不同的任务
常用的plugin: eslint的plugin babel的plugin\
- 两种模式的区分点
- 生产环境 需要压缩图片 压缩代码 代码分割 tree-shaking
- 开发环境 更快的构建速度 打印debug信息 需要hot reload 需要sorce map 来定位代码
可以通过 mini-css-extract-plugin 来分离样式文件
webpack 无法处理.png等图片文件 常用于处理图片的有\
- file -loader : 解决图片引入问题,并将图片拷贝到dist
- url-loader : 依赖file-loader ,当图片小于阈值时,会将图片转为base64编码,大于的使用file拷贝,可用于处理字体文件.
- img-loader :压缩图片
在 webpack5,内置了资源处理模块,
file-loader和url-loader都可以不用安装 直接设置babel 的预设插件集合 @babel/preset-env 为避免webpack关于babel配置过多,导致臃肿,单独分出 .babelrc.js/babel.config.js 来配置babel
module.exports = {
presets: [
[
"@babel/preset-env", // "@vue/app"
{
// useBuiltIns: false 默认值,无视浏览器兼容配置,引入所有 polyfill
// useBuiltIns: entry 根据配置的浏览器兼容,引入浏览器不兼容的 polyfill
// useBuiltIns: usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加
useBuiltIns: "entry",
corejs: "3.9.1", // 是 core-js 版本号
targets: { chrome: "58", ie: "11", },
},
], ], };
- 开启dev-tool: 'scurce-map' 执行打包后,dist 目录下会生成以
.map结尾的 SourceMap 文件 webpack 优化策略 - 优化webpack 构建速度,使用一个插件检测各个包的构建时间
- 优化 resolve 其一:使用webpack alias
- 指定插件应用范围 比如babel的include
- 使用缓存 比如babel-loader 的缓存开启
cacheDirectory: true // 启用缓存 - catch-loader 专用于缓存开销很大的loader的缓存
hard-source-webpack-pluginwp4 的模块缓存 webpack 构建结果分析- 使用
webpack-bundle-analyzer来分析文件的体积大小 各模块的依赖关系 ,再vue-cli中已经集成,是哦也能够 vue-cli-service build --report - 压缩代码 css/js 优化运行时
- 入口点分割,多入口打包
- 代码懒加载 prefetch preload /* webpackPreload: true */
Css啊
- 盒模型的box-sizing
我以为我明白,看了看文章才发现我是小丑!
首先不要混了盒模型的content区域宽高,与属性width,height! 这不是一回事 !!!
其实就一句话: border-box(IE盒模型),指定width,height 属性即为盒模型宽高,content-box(标准盒模型),指定width,height属性为content内容区宽高.而盒模型宽高为content,padding,border三部分之和,所以前者盒模型宽高被设定了,所以三部分之和定了,那么conntent内容宽高根据padding,border计算得出,后者内content容宽高被设定了,那么盒模型宽高根据padding,border计算得出 - BFC Block formatting context(块级格式化上下文)
主要概念: 类似于一个密闭的大箱子,内部的布局无论如何变化都不会影响外部布局
会生成BFC的元素:\
- < html >
- float 设置且不为none
- position 为 absolute fixed
- display:inline-block|table-*|
- overflow:非visible
- display 为flex/grid的直接子元素(弹性元素) BFC 问题: \
- 同一个BFC下会发生下边距合并
应用: 1.分属于不同的
BFC时,可以防止margin重叠 2.清除内部浮动 3.自适应多栏布局
- 三大特征为 继承 层叠 优先级
- FLEX BOX
作为当今最常用属性我们必须熟悉!- 关于flex的 align-content 与align-items,align-items在任何时候都有效,align-content 只有在单行设置了flex-wrap:wrap时或者 多行,设置了高度时才会生效,通俗的解释就是,align-content 将每一行作为一个整体,来进行布局,而align-items是针对每一个子元素
- flex: auto 为 flex-grow :1 ,如果子元素都为1意思就是存在剩余空间,所有子元素等比例放大,如果为0就是不放大,flex-grow意思一样,不过时缩小,flex-basis:auto.,项目本身大小,也可以设为固定宽高,none 为 0 0 auto
- flex 允许子元素覆盖align-items ,这个属性叫 align-self,自己决定自己的交叉轴位置.默认为auto,继承.
- 圣杯布局 首先三个盒子left,content,right,放在container中,三个盒子都设置float:left,container设置上overflow:hidden,撑开文档流,left,right分别设置上宽度,然后content设置上width:100%,此时,left,right 都跑了地图外边去了,所以设上position:relative,left(right):-宽度,让他们回来,这是content文字又被挡住了,就把背景container加padding吧,padding:0 rightWidth,0,leftWidth;
- 双飞翼布局 与圣杯布局的区别是,没用position,加了一个inner自适用,脱离文档流,会挤压中间区域
- css 性能优化
- 合并css 文件,总量一致,多个小文件加载总是比单个大文件慢
- 减少css 嵌套,控制在三层以内
- 建立公共样式类
- 减少通配符,属性选择器等需要挨个遍历的选择器
- css继承机制需要合理应用,父节点定义了继承属性,子节点就不需要了
- cssSprite,用宽高加background-position 显示图标
- GZIP压缩,css文档压缩
- css 选择器优先级
Babel 啊 [ˈbeɪbl]
Babel是一个js 编译器, 能让我们随处使用es6 es7等最新语法,而不用担心兼容性
Babel 可以把js 中不兼容的语法编译为当下环境可执行的语法
- 解析 通过AST parser [ˈpɑrsər] 解析js 为AST
- 转换 在此过程中通过深度优先遍历,进行节点的增删改操作,这里也是babel插件介入工作的地方
- 生成 将转换后的AST 生成为 js代码
RAF 与 RIC
- RAF :requestAnimationFrame() ,在下一帧渲染之前执行传入的回调,这个函数会自己适应设备刷新率,确保每一帧都会执行,这里的具体应用 比如three.js的 渲染流程
- RIC :requestIdleCallback() , 顾名思义,在浏览器空闲时候执行的回调
前端存储
- LoaclStore SessionStore
- LocalStore 每个源有固定大小的私有存储空间,不能跨域访问操作数据,基本安全
- cookie
- token 前端通过凭据登录,后端验证后返回JWT token,JWT类型token,包括,头部,载荷,签名三部分,用. 分割 xxx.yyy.zzz,头部一般为算法与token类型,载荷包含用户基本信息,签名使用私钥对载荷进行签名,防止内容被更改
Promise
Promise 必为一下状态:Pending,Fulfilled,Rejected,状态一旦确定就不可变更
Promise 链式就是指当前promise到达fulfilled状态,然后进入下一个promise
Promise(), 接收一个(resolve,reject){} 函数,可以是匿名函数,当然你也可以声明函数,直接调用Promoise(myfnc).then().catch(),更加简洁,then,返回一个Promise对象,因此可以继续链式调用,当前者执行resolve(),才会继续走then,否则直接reject()到catch,用于一个一个顺序执行的任务,后一个任务依赖于前一个的结果
- promise.all([]) 接收一个promise的iterable类型
- promise.all([ p1, p2]).then() 当所有promise对象都执行到resolve时,会执行then,否则reject
- promise.race([p1 , p2]).then() 用于容错,同时向多个接口请求用户信息,返回了一个就结束,丢弃其他结果,走then()
// 手写Promise
v8 垃圾回收机制
一种定时清除不用的变量的机制
在v8中,将内存具体分为新生代与老生代,分别存放存活时间较短的新生,和存活时间较长的老生,而针对新老生又分别采用不同的垃圾清除算法
- 新生代
采用scavenge [ˈskævɪndʒ] 算法,该算法主要通过一种复制的算法,该算法将新生代内存区分为from to 两块,from 为使用中的区域存着新生变量,to 为空闲区域,当分配新的变量是存放到from中,当进行垃圾回收时,将存活的变量从from中 复制到to中,不活的就不救了,然后from就空出来了,此时两者互换角色,当反复多次垃圾回收,有一个新生一直存在,那就晋级为老生,还有特例,如果新生体积过大,超过了内存的25% 就直接移入老生 - 老生代
采用增量标记清除整理算法,标记就是标记哪些变量存活,清楚就是垃圾回收阶段清楚没有标记的变量,整理阶段,整理垃圾回收产生的碎片,而增量是为了,解决老生代内存较大,回收时消耗的时间比较多,而垃圾回收时js代码需要等待执行完毕,所以需要渐进式的标记变量,为了解决回收老生代产生的代码影响 - 有垃圾回收自然有内存泄漏
产生内存泄露的途径有哪些? - 闭包,作用域未释放 - 缓存,内存中的数据一直未清理 - 没必要的全局变量 - 定时器未清除 - 无效的DOM引用 - 事件监听未解除
Map 的内部实现原理
对于Map这个结构来说,相比于Object,内部封装了get,set,has,delete 等方法,实现了forearch方法,clear(),方法,有size属性,Set结构类似,不过用的是add,delete,也有只读size属性
针对Flutter首屏优化的方法
Flutter 优化白屏: 白屏是安卓原生问题,