数组中的常用操作
生成随机id crypto.randomUUID()
push():作用:向数组末尾增加一个到多个元素。
unshift():作用:向数组开头增加一个到多个元素。
pop():作用:删除并返回数组末尾的第一个元素。
shift():作用:删除并返回数组开头的第一个元素。
concat():作用:拼接一个或多个数组。
reverse():作用:翻转数组。
splice(X,Y,Z)(死pe来死)作用:添加或删除元素,有三个参数:X:从第几个,Y:删除几个,Z:添加几个。
split(X)(分裂)分割字符串,有一个参数:X:从指定地方开始分割。
join(X):作用:转换为字符串,有一个参数:X:以什么分割字符串。
'abcabdab'.replaceAll('ab', '12')// 替换所有匹配的ab字符串变成12
matchAll() 作用:配合正则生成数组对象
let new_data = data.filter(非you特)(item => item !== 0):过滤指定的字符串数组
let ad = linklist.find( item =>{
if (item.id== 4){return true}
})
xxx.flat().map((item)=>({...item})):深层数组扁平化处理
对象 ↔ 数组“瞬移”
const score = { 语文:95, 数学:82, 英语:76 }
const pass = Object.fromEntries( Object.entries(score).filter(([k,v]) => v > 80) );
const params = Object.fromEntries(new URLSearchParams('name=张三&age=25'));
字符串补全
const orderId = '457'; const fullId = orderId.padStart(8, '0');
lodash
_.map(array, iteratee) 对数组中的每个元素执行转换操作,返回新数组。
_.filter(array, predicate) 过滤数组,返回满足条件的元素组成的新数组。
_.debounce((query) => {}, 300) 地榜次 防抖函数
_.throttle() 抓都 节流函数,在指定时间内只执行一次。
_.isEmpty(value) 安培踢 检查值是否为空(空数组、空对象、空字符串等)
_.find(users, { name: 'Bob' });
_.isArray([1, 2, 3]);
_.isObject({});
白屏优化
代码分割与动态加载 使用 `React.lazy` 和 `Suspense` 实现路由级代码分割
组件级按需加载 结合 `Intersection Observer` 实现视口触发加载
通过 Next.js 等服务端框架生成首屏 HTML
分块渐进渲染 利用 `requestIdleCallback` 或 `setTimeout` 拆分渲染任务,降低主线程阻塞
骨架屏占位
预加载关键资源 `<link rel="preload">` 提前加载首屏必需字体、图片或 JS 模块
静态资源部署至 CDN 启用 Brotli/Gzip 压缩 图片转 WebP 格式
21
git命令
git clone(可楼) git://xxx.com/schacon/grit.git
git reset(瑞色)
git commit(科咩特)提交 push 推送
git branch(贝瑞ch)查看分支
git checkout(切克out) -b 切换指定分支
git pull(破) 切换分支后拉取代码
git merge 合并分支
flex布局
flex-direction(第类深) 属性决定主轴的方向(即项目的排列方向)row | row-reverse | column | column-reverse
flex-wrap 如果一条轴线排不下,如何换行。nowrap | wrap | wrap-reverse
justify-content(爵士fai 康腾) 定义了项目在主轴上的对齐方式
align-items(阿来个 i腾) 属性定义项目在交叉轴上如何对齐。
align-content(阿来个 康腾) 多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用
浏览器的结构
1、用户界面。包括地址栏、前进/后退按钮、书签菜单等。除了浏览器主窗口显示的您请求的页面外,其他显示的各个部分都属于用户界面。
2、浏览器引擎。在用户界面和呈现引擎之间传送指令。
3、呈现引擎。负责显示请求的内容。如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
4、用户界面后端。用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
5、JavaScript 解释器。用于解析和执行 JavaScript 代码。
6、数据存储。这是持久层。浏览器需要在硬盘上保存各种数据,例如 Cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整(但是轻便)的浏览器内数据库。
7、网络。用于网络调用,比如 HTTP 请求。其接口与平台无关,并为所有平台提供底层实现
浏览器渲染
始解析 HTML 文档->
转化成“内容树”上的 DOM 节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据->
创建另一个树结构:呈现树->进入“布局”处理阶段->
绘制 - 呈现引擎会遍历呈现树.
浏览器优化
`<script>`标签添加异步属性async
**缓存优化**:首屏 HTML 用协商缓存(禁用强缓存),静态资源用强缓存 + 哈希
可以合并所有CSS成一个文件
加载部分HTML
HTTP缓存
2)Cache-Control: Cache-Control定义了缓存的策略,它规定在什么条件下可以缓存响应以及可以缓存多久。\
a、no-cache: no-cache表示必须先与服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续对同一网址的请求(每次都会根据ETag对服务器发送请求来确认变化,如果未发生变化,浏览器不会下载资源)。no-store直接禁止浏览器以及所有中间缓存存储任何版本的返回响应。简单的说,该策略会禁止任何缓存,每次发送请求时,都会完整地下载服务器的响应。\
b、public&private: 如果响应被标记为public,则即使它有关联的HTTP身份验证,甚至响应状态代码通常无法缓存,浏览器也可以缓存响应。如果响应被标记为private,那么这个响应通常只为单个用户缓存,因此不允许任何中间缓存(CDN)对其进行缓存,private一般用在缓存用户私人信息页面。
事件冒泡
事件会从最内层的元素开始发生,一直向上传播,直到document对象。
事件捕获
与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。
设计模式
单例模式:一个类只有一个实例 并给出一个访问它的方法
工厂模式:封装成function 带参调用复用。抽象工厂模式:一个接口来创建一系列相关或相互依赖的对象,而无需指定它们具体的类
开放封闭原则:只暴露调用接口,不暴露实现方式
js基本
防抖就是以setTimeout 执行以上代码,控制在2s后打执行请求,2s之内的操作都被清空,以最后一次的操作为准
节流拖动或者(滚动)期间触发某个毁掉,要设置一个时间间隔。不需要那么的频繁;这时候就使用节流函数,每隔一定的时间进项触发就好了
登录状态是如何保持的
会生成一段表示用户身份的字符串,并把该字符串写到响应头的 Set-Cookie 字段里,如下所示,然后把响应头发送给浏览器
大量数组循环速度排行:while = for > forEach = reduce > for of > map > for in
V8中的隐藏类和内联缓存<br/>
从第一个对象里 内存里偏移到第二个 访问速度会一样(主要是优化内存和访问速度)
经过两次成功地以具有相同隐藏类参数调用相同的方法后,V8引擎将省略隐藏类的查找过程,并直接的添加属性的偏移量
隐藏类优化<br/>
1保证以相同的顺序实例化对象属性,这样可以保证它们共享相同的隐藏类。
2在对象实例化后向对象添加属性将会迫使隐藏类改变,这将会使也已经进行行内缓存的方法的访问速度变慢。所以,请尽量保证,在构造器内进行所有的属性声明。
3不停执行相同方法的代码会比总在执行不同方法的代码速度快。
ajax是异步的 jq里可以设置async:true:异步 asycn:false 同步
JavaScript中没有栈,但是可以用数组实现栈的所有功能
JS分基本类型和引用类型,二者的区别是什么?
1.基本类型:number、boolean、string、undefined、 null(null代表"空";undefined是定义了没有赋值) Symbl(它存在的主要目的是解决全局变量冲突的问题)
2.引用类型:array(啊瑞)、object(对向)、function、regexp(正则表达式)
var声明的变量存在变量提升,即变量可以在声明之前调用,值为undefined,不存在块级作用域 允许重复声明变量
let和const不存在变量提升问题,即它们所声明的变量一定要在声明后使用,否则报错。 存在块级作用域 在同一作用域不允许重复声明变量
如何创建闭包?最简单的方式是什么?:
函数中返回函数
函数执行后返回结果是一个内部函数,并被外部变量所引用,如果内部函数持有被执行函数作用域的变量,即形成了闭包
闭包主要用来保护作用域 防止相互之间的变量命名冲突 把需要提供给别人的方法,通过 return
(function(){})()是匿名函数 js里匿名函数和闭包两者并没有关系 php里匿名函数是闭包
主要利用函数内的变量作用域,避免产生全局变量,影响整体页面环境,增加代码的兼容性。
原型链<br/>
原型与原型层层相链接的过程即为原型链 每个 函数 都有一个prototype属性。
对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在每个对象都有__proto__原型的存在
讲一下new对象的时候,做了什么?
(面试官指导:开辟内存空间,创建新的对象,然后调用构造函数并传入属性,修正this指针)
基本思想是使用工厂模式,实现一个构造函数,修正this指针指向新的对象,往内传入参数返回实例对象。
ES5常用的方式是,构造函数+原型挂载方法实现。即实现一个类的构造函数,然后往类原型prototype上去挂载函数,实现属性赋值以及函数的继承。
js常见的6种继承方式
1、原型继承 2盗用构造函数 3组合继承(原型继承+盗用构造函数继承) 寄生式继承(构造函数模式+工厂模式) 寄生式组合继承(盗用构造函数继承 + 原型式继承)
直接定义一个大括号,即空对象,然后访问toString方法,能不能访问到?
可以
JavaScript 事件循环机制
同步:会等前一个执行完再执行下一个
异步:不会等待直接出结果
现在只有微 宏任务不存在了 微队列中的任务优先于所有其他任务执行
大队列包含了其中不同类型的小队列
判断数据类型方法
判断数据类型方法:
console.log(typeof 123);
console.log(typeof "hello");
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof null);
console.log(typeof {});
console.log(typeof []);
console.log(typeof function(){});
console.log(typeof null);
console.log(typeof NaN);
console.log(typeof document.all);
判断已知对象类型的方法: instanceOf(in死腾死of): ES6
判断是否为对象Object.keys()
Array原型判断:Array.prototype.isPrototypeOf(obj)
跨原型调用:toString:Object.prototype.toString.call(obj)
字符串常用操作
indexOf,返回一个字符在字符串中首次出现的位置,lastIndexOf返回一个字符在字符串中最后一次出现的位置,这两个方法功能类似,在实际使用中都是根据需要使用,下面看代码: `
includes(in克鲁次)用来检测目标字符串对象是否包含某个字符
concat()拼接字符串
slice(死来死):截取字符串
replace(瑞pe累死):替换字符串
trim(雀们):清空字符串
清空空格:trimStart()、trimEnd()
拷贝
伪数组转数组
function doSomething () {
let args = Array.from(arguments);
console.log(args);
}
doSomething('一', '二', '三');
浅拷贝的是对象的属性的引用,而不是对象本身
object.assign()
深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。
js对象深拷贝
const data = primary.map((item) => {
return {
...item,
};
});
第二种const data= JSON.parse(帕尔斯)(JSON.stringify(primary))、
第三种cloneDeep() 基于lodash.js
第四种(es12) const copied = structuredClone(person)
浅冻结
它只是冻结了第一层对象,第二层无法冻结(对象的内部对象无法被冻结)
深冻结完全冻结对象,必须实现深度冻结
var constantize = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key,value) => {
if( typeof obj[key] === 'object' ) {
constantize( obj[key] );
}
});
};
Nginx
Nginx默认端口 localhost:8080(默认)<br/>
Nginx基于SSL协议下,利用http basic身份验证,可以实现简单访问WebApi,达到集群负载均衡的效果 WebApi基于SSL协议数据传输加密,结合http basic在一定上保证了通信的安全性
正向代理:
反向代理:个人电脑访问到服务器集群服务器的时候无法访问,必须通过第三方服中间服务器中介 才能访问集群
负载平衡:
Web加速:例如SSL加密,以减轻Web服务器的负载,从而提高其性能。
安全性和匿名性:它还确保无论您的局域网结构如何,都可以从单个记录定位器或URL访问多个服务器。
负载均衡:用户访问——》中间服务器——》访问请求引入选择的压力较小的服务器
跨域的解决方案有哪些?就举例最常用的代理,是怎么实现的?
一般是Nginx反向代理欺骗配置跨域nodejs跨域和后端代理跨域和 CORS跨域 jsonp跨域(只支持GET方法) websocket跨域
Nginx生成密钥证书<br/>
什么情况下算跨域?
如果不同源的脚本即为跨域,当浏览器执行一个脚本时会检查是否同源,只有同源的脚本才会执行
ES6新特性(异步)?
Set? 用于数组去重 用于字符串去重 实现并集、交集、和差集
Map? Map类型是键值对的有序列表,而键和值都可以是任意类型。对象保存键值对。任何类型的值都可以作为一个键或一个值
1、Set
成员唯一、无序且不重复;
[value, value],键值与键名是一致的(或者说只有键值,没有键名);
可以遍历,方法有:add、delete、has;
2、WeakSet
成员都是对象;
成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏;
不能遍历,方法有add、delete、has;
3、Map
本质上是键值对的集合,类似集合;
可以遍历,方法很多可以跟各种数据格式转换;
4、WeakMap
只接受对象作为键名(null除外),不接受其他类型的值作为键名;
键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的;
不能遍历,方法有get、set、has、delete;
import引入两个重复的数据 var一次 判断第二个会执行缓存
Map和对象有什么区别?
Map 是数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值==(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应
讲一讲Promise?(拓展到了async/await) 先走微任务 再走宏任务
Promise主要用来做异步操作 Promise必须接受一个函数作为参数,我们称该函数为执行器函数,执行器函数又包含resolve和reject两个参数,它们是两个函数。
有.then方法 .prototype.catch()方法处理异常
.race()是一组集合中最先解决或最先拒绝的Promise,返回一个新的Promise。
.all方法.any与 Promise.all可以看做是相反的 Promise.any 中只要有一个 Promise 实例成功就成功
async..await 就是和Promise和Generator(间呢瑞德)生成器的语法糖 在不阻塞主线程的情况下使用同步代码实现异步访问资源的能力,并且使得代码逻辑更加清晰
Promise正常是不可取消的
Promise暂停取消方法
Promise.resolve().then(() => {
console.log('ok1')
return new Promise(()=>{})
}).then(() => {
console.log('ok2')
}).catch(err => {
console.log('err->', err)
})
Promise.race竞速方法暂停 谁先搞出结果 就单一返回谁的值
let p1 = new Promise((resolve, reject) => {
resolve('ok1')
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {resolve('ok2')}, 10)
})
Promise.race([p2, p1]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error)
})
5、await暂停法
const handleSearch =async e => {
await setPageNum(1)
if (1==1) return;
await setPageSize(10)
};
6.异步基本写法
async function fn() {
try {
await new Promise((resolve, reject) => reject('报错'));
await new Promise(resolve => resolve(1));
console.log('do something...');
} catch (e) {
console.log("\nfilePath: E:\\myapp\\src\\main.js\nfuncName: fn\nError:", e);
}
}
Nestjs
getServerSideProps(ssr)中后台用 每次请求服务器实时渲染
getStaticProps(ssg)+(isr)电商用 静态页面 服务端低负载 SEO搜索优化用
isr用revalidate指定时间定期跟新ssg内容
动态路由用getStaticProps+fallback实现部分静态部分动态
React18 新特性
1 自动批处理:一次执行多个set会只渲染一次,
并发渲染:可以中断、暂停、恢复或放弃渲染
2 startTransition() 不紧急渲染 useDeferredValue() 延迟更新 页面上有大量的渲染并发时候 一步一步的逐渐渲染
3 useId() 组件生成唯一ID
React19 新特性
React 19 带来了革命性的 JSX 元素创建速度提升
ref不用再去包forwardRef了useImperativeHandle不清楚要不要包
<context..Provider>不用 .Provider了
react
useCallback||useMemoizedFn缓存function(配套memo优化父,子组件(添加元素或者删除元素时才会刷新,不然会变动就刷新),[监听的变量变动才会触发刷新])
useMemo(使用上一次缓存的值)
useEffect无法解决多次刷新后再使用useLayoutEffect
useEffect会产生过期闭包会造成内存泄漏与优化负担
useRef
和useState类似 不过不会触发刷新
ref返回的对象在组件的整个生命周内持续存在,通常作为访问DOM的方式
React合成事件
在React中,所有事件都是合成的,不是原生DOM事件,可以通过 e.nativeEvent 属性获取原生DOM事件。合成事件不会映射到原生事件
浏览器兼容,实现更好的跨平台\\避免垃圾回收\\方便事件统一管理和事务机制
react数据流,每个组件都被设计成了能够在整个生命周中输出稳定、语义化的标签。
组件不会独立存,随着父组件将props推送给他们的子组件,以及那些子组件渲染它们自身的子组件你必须谨慎的考虑数据是如何流经整个应用的。
调用setState后发生了什么
代码中调用 setState 函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。
经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面;
在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染;
在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
setState是异步还是同步:本质是同步模拟异步(合成事件) 18版本纯异步
合成事件中是异步
hooks钩子函数中的是异步
原生事件中是同步
setTimeout中是同步
react 异步 避免多余的渲染
class不在render里做多余的操作
hooks可以不用this 复用简单 方便封装 hooks只能在fun最外调用 不能在循环条件判断中调用
setState在运行上是基于同步代码实现,只是行为上看起来像异步
react18後,所有的setState都會是异步
在setTimeout等异步事件中是同步执行的 每一次setState和useState,都会调用一次render
react的fiber
Fiber架构 任务分解切片,避免主线程的持续占用造成卡顿问题 把渲染任务拆分成多块
更新时候能够暂停,终止,复用渲染任务 给不同类型的更新赋予优先级 有5个优先级
React Fiber 是一个新的任务调和器简称协调 比较大的js计算与dom计算 每帧都是有规律的执行
react是用messageChannel模拟实现了requestidlecallback的功能
React 通过分层求异的策略,对 tree diff 进行算法优化;
React 通过相同类生成相似树形结构,不同类生成不同树形结构的策略,对 component diff 进行算法优化;
React 通过设置唯一 key的策略,对 element diff 进行算法优化;
建议,开发时保持稳定的DOM结构有助于性能的提升;
高阶组件就是就是组件返回组件 能用来做
是一个fun 入参:原来的react 返回值:新的react组件
是一个纯fun 没有副作用
1普通方式 2装饰器 3多个高阶组件的组合
高阶组件能干嘛? 1继承与2劫持 3操作props 4操作组件实例
ref
需要在数据流之外强制修改子组件、获取组件的属性 是dom操作
被修改的子组件可能是一个React组件的实例,也可能是一个原生HTML元素。
useState
在setTimeout,Promise.then等异步事件中
setState和useState是同步执行的(立即更新state的结果)
多次执行setState和useState,每一次的执行setState和useState,都会调用一次render
useContext 解决多层跨级传值的问题
什么是useContext :接收一个 context 对象并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 value 决定。
当上层组件提供的value属性发送变化,useContext(葵诶Context)会让当前组件重新渲染
主要组件创建:const XxxContext=React.createContext()
const {Provider,Consumer}=XxxContext
接收:<Consumer>{value=>{return`${value.username}`}}</Consumer>
Suspense与lazy
用来(代码分割技术)让子组件延迟加载的优化方式,在服务端渲染中尚不可用,
如果想在服务器渲染的应用(next.js)`Loadable` 组件仍是强烈推荐的
const Artists = lazy(() => import(‘./Artists’))
const Performers = lazy(() => import(‘./Performers’))
return( <div className=”App”>
<Suspense fallback={<h1>Still Loading…</h1>}>
<Artists />
<Performers />
</Suspense>
</div>);
react生命周期
### 挂载阶段
constructor(肯死爵克特): 构造函数,最先被执行,我们通常在构造函数里初始化state对象或者给自定义方法绑定this
componentDidMount(地的毛特)** – 仅在第一次渲染后在客户端执行(用于ajax请求数据)
render 返回 JSX 元素的方法,是 React 组件的核心。
### 更新阶段
更新shouldComponentUpdate 如果你希望更新组件,请返回true 否则返回 false。默认情况下,它返回 true。
更新componentDidUpdate**()** – 在每一次重新渲染后调用。
getSnapshotBeforeUpdate: getSnapshotBeforeUpdate(prevProps, prevState),这个方法在render之后,componentDidUpdate之前调用,
有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate
getSnapshotBeforeUpdate,然后在 componentDidUpdate 中统一触发回调或更新状态
更新render
卸载componentWillUnmount**()** – 从 DOM 卸载组件后调用。用于清理内存空间。
react父子组件加载顺序
可以发现挂载时,子组件的挂载钩子先被触发;卸载时,子组件的卸载钩子后被触发。
卸载的时候父节点先被移除,再从上至下依次触发子组件的卸载钩子
都是走的componentDidMount
react父子通信
子组件向父组件通讯: props+回调的方式,父组件向子组件传递props进行通讯,此props为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到父组件的作用域中
兄弟组件通信: 找到这两个兄弟节点共同的父节点,结合上面两种方式由父节点转发信息进行通信
跨层级通信: Context设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言,对于跨越多层的全局数据通过Context通信再适合不过
父组件执行子组件方法
ref直接调用:
useImperativeHandle(in培睿特含的):将ref的实例传递给父组件。1、需要自定义props属性
2、需要自定义暴露的方法 然后forwardRef(Index) for ward ref
父组件调用子组件的fun
onRef
React.createRef()(葵诶德ruaF)
react super(props)
React.Component 可以初始化 this.props:
入果写super()不带props 那么this.props就是未定义
写 React / Vue 项目时为什么要在列表组件中写 key
在开发过程中为了保证遍历同级元素的唯一性,用来提高更新 dom 的性能
key 来判断元素是否需要重新渲染
如果遍历渲染数据输入框 <input />没有key , 可能导致无法准确定位到 input,获取不到预计的value值
React性能优化
使用React.Memo来缓存组件
使用useMemo缓存大量的计算
使用React.PureComponent , shouldComponentUpdate
避免使用内联样式
避免使用匿名函数
延迟加载不是立即需要的组件
调整CSS display而不是强制组件渲染生成加载和卸载
React.Fragment分段避免添加额外的DOM
减少rerender次数的方式
当父组件重新属性更新触发重新渲染的时候,子组件也会重新渲染
其实我们的子组件属性没有任何变化,此时我们不希望子组件重新渲染,我们可以将子组件使用memo将子组件包一下。
当父组件的方法通过props传给了子组件时,对子组件只使用memo是不行的,我们需要将父组件的方法通过useCallback钩子包一下,同时需要将钩子的依赖设置为[]空依赖。
React长列表优化 虚拟列表
使用虚拟列表 增加一层div包裹列表项
数组存储各个列表项的位置 使用useState将该缓存数组进行初始化
需要根据容器滚动实时去计算。使用useMemo当作计算属性,依赖缓存数组进行实时更新
当缓存数组更新的时候会触发ListBox高度重新计算。
在滚动时 重新计算起始索引(使用二分查找),结束索引
在滚动时 缓存数组获取列表项中的自定义属性data-id获取到当前项索引,然后通过计算得到当前项真实的位置。踩坑提示:注意这里要用data-id,不要用当前循环的那个索引。
react-router-dom嵌套路由,
可以保证子路由共享父路由的界面而不会覆盖。为此React提供了Outlet组件,将其用于父组件中可以为子路由的元素占位,并最终渲染子路由的元素。
redux系列
Redux是将整个应用状态存储到到一个地方,称为store
里面保存一棵状态树(state tree)
组件可以派发(dispatch)行为(action)给store,而不是直接通知其它组件
其它组件可以通过订阅store中的状态(state)来刷新自己的视图
Redux-thunk可以把同步 dispatch 变成异步 dispatch 的中间件,可以使我们把异步请求或非常复杂的逻辑放到action去处理
view视图层》Action:对变化的描述》
Reducer:一个函数,负责对变化(也就是action)进行分发和处理,最终把最新的数据返回给Store》
Store只读的一个单一的数据源(里面存放了state)=》更新视图view=》发起更新动作Reducer=》更新状态=》Store
1. Store 创建:使用 `createStore` 函数创建一个 Store,该存储包含应用程序的整个状态。
1. Reducer(瑞丢色) 定义:
Reducer 是一个纯函数,它接受当前状态和一个 action,并返回新的状态。
Reducer 定义了如何根据 action 更新状态。
1. Dispatch动作:
使用 `dispatch` 函数分发 action 到 Redux 存储。
当 action 被分发时,相应的 Reducer 函数会被调用,并返回新的状态。
1. 订阅状态更新:
React-Redux 的 `connect` 或 Hooks(如 `useSelector`)会订阅 store 的变化。
1. 组件更新:当 state 变化时,订阅的组件会重新计算 props 并触发渲染。
1. redux-thunk(当克)异步 它的核心作用是让 action 创建函数返回一个函数而非普通对象 从而实现异步函数
这是一个redux的经典案例 整个应用只能有一个Store
定义reducer函数根据action的类型改变state
actions 定义指令
通过Redux提供createStore(亏A死dou儿)创建store
当前的State 可以用store.getState()拿到
调用store.dispatch()发出修改state的命令
Mobx的目的是更轻松的管理State对象 mobx-react:既支持函数组件也支持类组件
mobx最新版本装饰器声明已经无效 要实现Observable(啊be色ve博)(可观察的),Action(行动)等声明。必须makeObservable或者makeAutoObservable显性声明
React 组件中某些操作触发 action 执行,由此就完成了一个数据流闭环
Mobx可以轻松实现在多个界面中控制同一个状态的效果,再也不用考虑State应该怎么传递才能够实时更新,耦合度更低
Mobx没有模板代码 数据是相应式的 可直接修改数据
mobx可以直接处理异步 写法自由
事件触发了 Actions-》修改 State —》更新了计算值 Computed,计算值的改变—》Reactions 的改变—》导致了 UI 的改变,
Reactions 可以经过事件调用—》 Actions
useLocalObservable 可以代替 useState 来给 函数式组件提供 ”可观察的“ state
webpack
loader:拓展webpack能够去处理javaScript和JSON以外类型的文件 只能是普通函数,不能是箭头函数
plugin:主要处理打包优化,资源管理,注入环境变量 是一个类,其中必须实现一个 apply 方法,apply 方法接收 webpack 的 compiler 对象,从中可以定义插件自己的钩子或者订阅其他插件的钩子
1、热更新基本定义:webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。
2、热更新核心定义
2,1)HMR 的核心就是客户端从服务端拉去更新后的文件,准确的说是 chunk diff (chunk
需要更新的部分),实际上 WDS 与浏览器之间维护了一个 websocket,当本地资源发生变化时,
WDS 会向浏览器推送更新,并带上构建时的 hash,让客户端与上一次资源进行对比
2,2)客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、
hash),这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该 chunk 的增量更新
2,3)后续的部分(拿到增量更新之后如何处理?哪些状态该保留?哪些又需要更新?)
由 HotModulePlugin 来完成,提供了相关 API 以供开发者针对自身场景进行处理,像
react-hot-loader 和 vue-loader 都是借助这些 API 实现 HMR
白屏优化
1代码分割 `React.lazy` 和 `Suspense`
服务端优化
服务端渲染SSR
服务端渲染所有数据请求和 html内容已在服务端处理完成
白屏优化
SSR Nuxt.js这类的SSR框架帮我们简化了服务端的部分,但是在要做定制或是解决bug时还是无法避免要对服务端部分进行调试
预渲染 prerender-spa-plugin插件做预渲染
骨架屏
切片上传
客户端
使用Blob.prototype.slice方法对文件切片
使用spark-md5库计算文件hash,并创建web worker开启新的线程执行。(该库不能直接计算整个文件,需要分片进行计算,具体可以看它官网的例子spark-md5-example。
将各个分片文件上传到服务器,并携带hash
当所有文件上传完后,需要发送合并请求,携带文件hash、后缀名、分片大小
服务端
根据接收到的hash创建文件夹,将分片文件存储到文件夹中
收到合并请求后,读取各个分片文件。根据hash和后缀名,合并生成完整文件
删除存储分片文件的文件夹及其内容
秒传
客户端
上传文件之前,先计算hash,然后将hash和文件名发送到服务器
服务器返回文件是否存在的状态
如果存在,客户端提示文件上传成功,否则执行上传动作
服务端
服务器根据hash和后缀名,在服务器中查找该文件
返回该文件存在的状态
断点续传
观察图片左边发现上传了部分文件切片
点击上传时,发送请求响应已上传的块序号
使用array.filter过滤掉已上传的块。(87段变为了81段)
合并完后,左上角变成了完整的文件
时间分片
与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。
SVN上传冲突
两个人对项目的同一个文件做修改
如果修改的是同一行,那么合并时会产生冲突!
1、放弃自己的更新,使用svn revert(回滚),然后提交。在这种方式下不需要使用svn resolved(解决)
3、手动解决:冲突发生时,通过和其他用户沟通之后,手动更新目标文件。然后执行resolved filename来解除冲突,最后提交。
在冲突的文件上(选中文件–右键菜单—TortoiseSVN—Edit conflicts(解决冲突))
如果要使用服务器版本,在Theirs窗口选中差异内容,右键,选择Use this text block(使用这段文本块)。
HTTP
浏览器输入url后,会发生什么?
1. 浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。
2. 进行DNS域名解析。
3. 建立TCP连接。
4. 发起HTTP请求。
5. 服务器响应请求并返回结果。
6. 浏览器将HTML代码转化为DOM树,CSS样式会被应用到DOM树上,而JavaScript代码则会执行并与DOM树交互。
7. 关闭TCP连接,即浏览器和服务器之间交换四个数据包以确认连接已经关闭。
HTTP的整个流程
http流程是(用户端) 构建请求, 查找缓存, 准备IP地址和端口, 等待TCP队列
http流程是(服务器端)返回请求 断开连接 重定向
HTTP报文
客户端向服务器发送数据,称之为请求报文 GET 可以被缓存,POST 不会被缓存
服务器向客户端返回数据,称之为响应报文
http 80端口 对称加密 是明文传输,敏感信息容易被中间劫持
https 443端口 非对称加密 https劫持了也无法解密 https有证书 现代浏览器已开始强制https协议
tcp
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的syn(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了,但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。
缓存机制
1.浏览器发送请求前,根据请求头的expirse 和 cache-control判断是否命中强缓存策略,如果命中,直接从缓存获取资源,并不会发送请求。如果没有命中,则进入下一步。
2.没有命中强缓存规则,浏览器会发送请求,根据请求头的last-modified和etag判断是否命中协商缓存,如果命中,直接从缓存获取资源。如果没有命中,则进行下一步。
3.如果前两者都没有吗,命中,则直接从服务端获取资源
强缓存 当浏览器去请求某个文件的时候,服务端就在respone header里面对该文件做了缓存配置。缓存的时间、缓存类型都由服务端控制,具体表现为respone header 的cache-control,常见的设置是max-age public private no-cache no-store等
强缓存就是给本地缓存设置时间,客户端每次请求资源都会看是否过期,如果过期就会请求服务器,这时候就可以设置协商缓存。
协商缓存
上面说到的强缓存就是给资源设置个过期时间,客户端每次请求资源时都会看是否过期;只有在过期才会去询问服务器。
所以,强缓存就是为了给客户端自给自足用的。而当某天,客户端请求该资源时发现其过期了,这是就会去请求服务器了,
而这时候去请求服务器的这过程就可以设置协商缓存。这时候,协商缓存就是需要客户端和服务器两端进行交互的。
关于前端权限控制一般有两种方案:
前端固定路由表和权限配置,由后端提供用户权限标识
后端提供权限和路由信息结构接口,动态生成权限和菜单
localStorage(楼扣死都瑞g)
localStorage的最大存储为5m cookie单个的最大存储为4k
权限信息存放于localStorage
localStorage是持久化存储,除非主动clear掉
sessionStorage(保存在“sessionstore.jsonlz4”的文件中)仅在当前会话有效,关闭页面或浏览器后被清除
cookie可以设置过期时间
通过框架插件提供的工具方法进行权限 get 以及 set
Cookie就是浏览器里存储的一种数据 Cookie由服务器生成,通过响应头Set-Cookie字段发送给浏览器,浏览器把Cookie以key value形式保存到某个目录下的文本文件里,下一次请求同一网站时会把该Cookie发送给服务器 cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名
Expires 用于设置 Cookie 的过期时间。
Session(赛森)是基于 cookie 实现的 是另一种记录服务器和客户端会话状态的机制 session 存储在服务器端
Token是在服务端将用户信息经过Base64Url编码过后传给在客户端,每次用户请求的时候都会带上这一段信息,因此服务端拿到此信息进行解密后就知道此用户是谁了
indexDB:一般用来储存3D可视化模型贴图。浏览器厂商的限制存储的大小
HTTPS其实就是HTTP+RSA+数字证书+会话秘钥
RSA实现了非对称加密,可以让公钥任意分发,私钥即使丢失了,也可以迅速换一对公私钥。解决了对称加密密码本的漏洞。
数字证书保证了分发的公钥不能被篡改。
CA保证了数字证书的安全性。
CA的安全性由谁保证是个玄学
会话秘钥是对称加密,目的是为了加快加密解密速度
RSA算法精髓:
加密使用模运算,完全不能反解
n取一个超大数,超出了数学界理论极限和计算机的工业极限
破解HTTPS:
虚假证书欺骗
注册大概逻辑
实际开发中不能将密码直接传到数据库存储,数据库中应该存的是用户的密码加密后的数据,然后用户如果登录,服务端得到密码,将其加密后再和数据库中的信息做对比
前端需要在用户提交数据后,获取到这些数据
前端需要将上一步所获得的数据通过post请求发送给服务端
后端接收前端发来的请求并从中读取到需要的信息(如传过来的邮箱和密码)后端从数据库中验证是否有重复的用户信息
得到post请求的数据,Node没有直接读取请求体的api,所以先封装一个函数。(因为客户端向服务端发送post请求传输数据时,实际上是一段一段传的,所以无法直接获取数据
后端必须得有数据校验
前端的数据校验是很容易被绕过去的(比如curl 命令可以绕过前端直接向后台发请求),我们不能过于依赖网页中的JS校验,必须得有后台的数据校验来确保数据的安全。
错误
邮箱id是否被注册
后端拿到数据后,进行简单的数据校验,出错后将错误信息返回给前端,前端进行解释展示给用户。
前端解析错误信息展示给用户
成功
注册成功后将用户数据传到数据库中
登录大概逻辑
前端需要将用户写的登录信息拿到并传给数据库,并会做一些简单的数据校验
后端从数据库中验证用户信息 后端设置cookie
只要在某一个页面设置了Set-Cookie头,那么从此以后,只要是相同的源(即同一个域名)的请求,在请求头里面都会带上你设置的cookie。
后端通过读取cookie响应对应的用户信息(Session(赛森))
RSA 非对称加密 规则 jsencrypt.js
后端拿着:私钥A(privateKeyA)、公钥B(publicKeyB),前端拿着:公钥A(publicKeyA)、私钥B(privateKeyB)。
公钥(publicKey)加密、私钥(privateKey)解密。不能逆向,私钥(privateKey)加密、公钥(publicKey)解密。说白了就是前后端都需要用公钥(publicKey)进行加密,用私钥(privateKey)进行解密。
为啥不可逆呢?前端代码的安全性差,是总所周知的。之所以称为私钥(privateKey),就是因为是私密的,不可公开的,需要确保 key 的安全。
前端使用公钥A(publicA)对数据进行加密,后端通过公钥A(publicKeyA)对应的私钥A(privateKeyA)进行解密。
秘钥对B – 前端解密,后端加密
后端使用公钥B(publicKeyB)进行加密,前端通过公钥B(publicKeyB)对应的私钥A(privateKeyA)进行解密。
这样就能保证,虽然私钥(privateKeyB)和公钥(publicKeyA)都在前端代码中,但是这两个并不是一对,就算是全部拿到,也无法成功解密。也符合公钥(publicKey)加密、私钥(privateKey)解密的规则。完美解决!
注意事项 这个插件对res加密的字符串最长是 117字符,有时加密时,会遇到加密参数过长而无法加密的现象在源码中加入以下代码,通过调用encryptLong方法,重新定义加密函数即可。
权限系列
单点登录(共享cookie)
是时候给小项目加上权限系统了,初步使用SSO(Single Sign On)单点登录。
Cookie的缺点:Cookie允许被js等拦截解析,不安全。 如果浏览器禁用Cookie,那么Cookie将无法被使用。 Cookie可以保持文本文件和二进制文件,其他信息无法被保存。
Session 是与Cookie协同工作产生。Cookie是用户信息存储在浏览器中,而Session的信息是存储在服务器端。
单点注销
微前端
功能模块可热插拔
算法
获取数组最大值
console.log(Math.max(...arr))
数组合并并排序
var a=[11,2,3],b=[4,5,16]
var c=[...a,...b]
c.sort(function(a,b){
return a-b
})
对象合并
var summary = {...person, ...tools, ...attributes}
数组去重
filter
let arr = ['1', '2', '3', '1', 'a', 'b', 'b']
const unique = arr => {
return arr.filter((ele, index, array) => {
return index === array.indexOf(ele)
})
}
console.log(unique(arr)) // ['1','2','3','a','b']
set
let arr = ['1', '2', '3', '1', 'a', 'b', 'b']
const unique = arr => {
return [...new Set(arr)]
}
console.log(unique(arr)) // ['1','2','3','a','b']
//数组对象去重
let map = new Map()
for (let item of arrObj) {
if (!map.has(item.id)) {
map.set(item.id, item)
}
}
arr = [...map.values()]
console.log(arr)
数组中最大差值
let arr = [23, 4, 5, 2, 4, 5, 6, 6, 71, -3]
const difference = arr => {
let max = Math.max(...arr),
min = Math.min(...arr)
return max - min
}
console.log(difference(arr)) // 74
冒泡排序
let arr = [43, 32, 1, 5, 9, 22]
const sort = arr => {
arr.forEach((v, i) => {
for (let j = i + 1
if (arr[i] > arr[j]) {
[arr[i],arr[j]] = [arr[j],arr[i]]
}
}
})
return arr
}
console.log(sort(arr)) // [1, 5, 9, 22, 32, 43]
实现并集、交集、和差集
let a = new Set([1, 2, 3])
let b = new Set([4, 3, 2])
// 并集
let union = new Set([...a, ...b])
// 交集
let intersect = new Set([...a].filter(x => b.has(x)))
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)))
浅拷贝的是对象的属性的引用,而不是对象本身
object.assign()
深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。
js对象深拷贝
const data = primary.map((item) => {
return {
...item,
}
})
第二种const data= JSON.parse(帕尔斯)(JSON.stringify(primary))、
浅冻结
它只是冻结了第一层对象,第二层无法冻结(对象的内部对象无法被冻结)
深冻结完全冻结对象,必须实现深度冻结
var constantize = (obj) => {
Object.freeze(obj)
Object.keys(obj).forEach( (key,value) => {
if( typeof obj[key] === 'object' ) {
constantize( obj[key] )
}
})
}
如何判断一个变量是对象还是数组
function isObjArr(value){
if (Object.prototype.toString.call(value) === "[object Array]") {
console.log('value是数组')
}else if(Object.prototype.toString.call(value)==='[object Object]'){//这个方法兼容性好一点
console.log('value是对象')
}else{
console.log('value不是数组也不是对象')
}
}
扁平数据结构转Tree
function arrayToTree(items) {
const result = []
const itemMap = {}
for (const item of items) {
const id = item.id
const pid = item.pid
if (!itemMap[id]) {
itemMap[id] = {
children: [],
}
}
itemMap[id] = {
...item,
children: itemMap[id]['children']
}
const treeItem = itemMap[id]
if (pid === 0) {
result.push(treeItem)
} else {
if (!itemMap[pid]) {
itemMap[pid] = {
children: [],
}
}
itemMap[pid].children.push(treeItem)
}
}
return result
}
给出一个只含数字的字符串(11位),顺序可乱,请输出符合IP格式的结果。
JS回溯+双边界完全剪枝
var restoreIpAddresses = function(s) {
if (s.length > 12 || s.length < 4) return []
const res = []
function dfs(cur, tmp, pos) {
if (pos === 5) {
res.push(tmp)
return
}
const base = Math.max(s.length - cur - (4 - pos) * 3, 1)
const top = s.length - cur - (4 - pos)
if (s[cur] === "0") {
if (base === 1) {
dfs(cur+1, pos < 4 ? tmp + "0." : tmp + "0", pos+1)
}
return
}
// 因为slice为左闭右开,所以i为实际右边界+1
for (let i=cur+base
if (i === cur + 3 && parseInt(s.slice(cur, i), 10) > 255) return
dfs(i, pos < 4 ? tmp + s.slice(cur, i) + "." : tmp + s.slice(cur, i), pos+1)
}
}
dfs(0,"",1)
return res
}
3.输入类似于“1+1”或“1+(2-4)”这种字符串,请设计一个计算器,可以返回结果正确的值。
var calculate = function(s) {
const ops = [1]
let sign = 1
let ret = 0
const n = s.length
let i = 0
while (i < n) {
if (s[i] === ' ') {
i++
} else if (s[i] === '+') {
sign = ops[ops.length - 1]
i++
} else if (s[i] === '-') {
sign = -ops[ops.length - 1]
i++
} else if (s[i] === '(') {
ops.push(sign)
i++
} else if (s[i] === ')') {
ops.pop()
i++
} else {
let num = 0
while (i < n && !(isNaN(Number(s[i]))) && s[i] !== ' ') {
num = num * 10 + s[i].charCodeAt() - '0'.charCodeAt()
i++
}
ret += sign * num
}
}
return ret
}
防抖 一定时间内持续触发是不会重复调用,当超过一定时间后再回执行,主要应用在输入框这种地方,当需要查询一个东西的时候,持续输入是不会请求接口
function debounce(fn, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
节流表示的是不一直触发,一定时间触发一次,常用在滑动滚动或者视窗大小变化的控制
function throttle(fn, delay) {
let start = +Date.now()
let timer = null
return function(...args) {
const now = +Date.now()
if (now - start >= delay) {
clearTimeout(timer)
timer = null
fn.apply(this, args)
start = now
} else if (!timer){
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
}
合并两个有序数组
function merge(left, right) {
let i = 0
let j = 0
const temp = []
while(i < left.length && j < right.length) {
if (left[i] < right[j]) {
temp.push(left[i])
i++
} else {
temp.push(right[j])
j++
}
}
while(i < left.length) {
temp.push(left[i])
i++
}
while(j < right.length) {
temp.push(right[j])
j++
}
return temp
}
爬楼梯
var climbStairs = function(n) {
if (n <= 2) return n
let n1 = 1
let n2 = 2
let nn = 0
for (let i = 3
nn = n1 + n2
n1 = n2
n2 = nn
}
return nn
}
买卖股票的最佳时机
var maxProfit = function(prices) {
let start = prices[0],n = prices.length
let profit = 0
for(let i = 1
start = Math.min(start,prices[i])
profit = Math.max(profit,prices[i]-start)
}
return profit
}
Promise.all
Promise.all = function(promises) {
const values = []
let count = 0
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise).then(res => {
count++
values[index] = res
if (count === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
Promise.allSeleted
Promise.allSeleted = function(promises) {
let count = 0
let result = []
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise).then(res => {
result[index] = {
value: res,
reason: null,
}
}, err => {
result[index] = {
value: null,
reason: err,
}
}).finally(() => {
count++
if (count === promises.length) {
resolve(result)
}
})
})
})
}
实现maxRequest,成功后resolve结果,失败后重试,尝试超过一定次数才真正的reject
function maxRequest(fn, maxNum) {
return new Promise((resolve, reject) => {
function help(index) {
Promise.resolve(fn()).then(value => {
resolve(value)
}).catch(error => {
if (index - 1 > 0) {
help(index - 1)
} else {
reject(error)
}
})
}
help(maxNum)
})
}
米哈游系列
1.实现图片懒加载的思路
判断图片所在位置是否在可视区内,图片移到可视区内进行加载,提供三种判断方法
3.表单可以跨域吗
表单提交是可以进行跨域的,不受浏览器的同源策略限制,估计是历史遗留原因,也有可能是表单提交的结果js是拿不到的,所以不限制问题也不大。
但是存在一个问题,就是csrf攻击,因为可以自动带上cookie造成攻击成功,而cookie的新属性SameSite就能用来限制这种情况
4.请为什么说js是单线程,而不是多线程呢,说说你的理解
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。
这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
5.为什么typeof可以检测类型,有没有更好的方法
typeof 一般被用于判断一个变量的类型,我们可以利用 typeof 来判断number, string, object, boolean, function, undefined, symbol 这七种类型,这种判断能帮助我们搞定一些问题,
js在底层存储变量的时候会在变量的机器码的低位1-3位存储其类型信息(000:对象,010:浮点数,100:字符串,110:布尔,1:整数),但是null所有机器码均为0,直接被当做了对象来看待。
那么有没有更好的办法区分类型呢,一般使用Object.prototype.toString.call()
6.说说你对GraphQL的理解
多终端的出现,APP、小程序、PC端都需要相同的接口,但是又略有差异,常规接口需要提供几套,GraphQL的话只需要写好查询语句即可
天生的聚合接口,以前一个页面需要请求不同的数据,我们可以请求多个接口,我们可以让服务端进行聚合,有了GraphQL后我们可以自己去聚合想要的数据
不用被版本困扰,之前写接口的时候为了兼容老的项目可以正常被访问,尤其是APP,线上的项目,我们接口肯定是不能影响线上的,所以有比较大的改变的时候,只能升级版本,有了GraphQL后就无需关心版本问题了,接口还是那个接口查询语句变一下就好了
迁移很简单,服务端在之前的接口上稍加改造就好,前端写查询语句
7.什么是事件委托?它有什么好处?
事件委托是利用事件冒泡机制处理指定一个事件处理程序,来管理某一类型的所有事件
利用冒泡的原理,将事件加到父级身上,触发执行效果,这样只在内存中开辟一块空间,既节省资源又减少DOM操作,提高性能
动态绑定事件,列表新增元素不用进行重新绑定了
8.使用js如何改变url,并且页面不刷新? location.href(喽kei神)实现url跳转
window\.history.pushState({}, 0, window\.location.href + '?type=china');
最简单的就是改变hash,改变hash是并不会刷新页面的,也会改变URL,也能监听hashchange事件进行页面的渲染
还有一种就是使用history.pushState()方法,该方法也可以改变url然后不刷新页面,但是该方法并不能够触发popstate事件,
不过pushState使我们手动触发的,还能不知道url改变了么,其实这时候并不需要监听popstate我们就能够知道url改变拿到参数并渲染页面
9.要实现一个js的持续动画,你有什么比较好的方法?
使用requestAnimationFrame
10.使用css3动画代替js的动画有什么好处?
不占用JS主线程
可以利用硬件加速
浏览器可对动画做优化(元素不可见时不动画,减少对FPS的影响)
11.js中自定义事件的使用与触发
var event = new Event('build');
elem.addEventListener('build', function (e) { ... }, false);
elem.dispatchEvent(event);
14.请解释JSONP的工作原理
JSONP 是一种非正式传输协议,允许用户传递一个callback给服务端,然后服务端返回数据时会将这个callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
当GET请求从后台页面返回时,可以返回一段JavaScript代码,这段代码会自动执行,可以用来负责调用后台页面中的一个callback函数。
18.如何实现H5手机端的适配?
淘宝flexible + rem进行适配
19.如何解决在移动端1px的问题?
缩放
29.说说你理解的同步和异步的区别是什么?
异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。、
同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到返回的值或消息后才往下执行其他的命令。
30.实现异步编程有哪些方式?推荐用哪种?
回调函数、Generator(间呢瑞德)、Promise、async/await
Promise正常是不可取消的
32.https的请求可以拦截么,如何做?
完全是有可能的,不然charles抓包是怎么做到的,当然前提是客户端上装了相应的证书
非对称加密的加解密效率是非常低的,只作用在证书验证阶段,对称加密在数据传输阶段
36.说说你对XSS和CSRF的理解,他们之间的区别是啥?
xss前端主要是控制好输入,cookie设置http-only
CSRF需要严格的cookie策略,增加token校验等
38.中间人攻击是什么?
中间人攻击是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方 直接对话,但事实上整个会话都被攻击者完全控制。
39.webpack打包速度太慢怎么办?
使用高版本的 Webpack、多线程/多实例构建、缩小打包作用域、充分利用缓存提升二次构建速度、DLL
40.loader和plugin的区别?
loader,它是一个转换器,将A文件进行编译成B文件,比如:将A.less转换为A.css,单纯的文件转换过程。
plugin是一个扩展器,它丰富了webpack本身,针对是loader结束后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听webpack打包过程中的某些节点,执行广泛的任务
43.知道npm ci么,和npm install的区别是啥?
npm ci(以持续集成命名)直接从package-lock.json安装依赖关系,并且仅使用package.json来验证没有不匹配的版本。如果缺少任何依赖项或版本不兼容,则将引发错误。
npm install读取package.json以创建依赖关系列表,并使用package-lock.json告知要安装这些依赖关系的版本。如果依赖项不在package-lock.json中,它将由npm install添加。
速度上ci明显比install快,线上发布打包的时候使用ci是优于install的
三个项目 都要用一个组件 我想imput引入
npm install