失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 【JavaScript】阶段性复习

【JavaScript】阶段性复习

时间:2023-10-16 19:08:39

相关推荐

【JavaScript】阶段性复习

【JavaScript】阶段性复习

View / MVVM 框架对比 React 、Angular 和 Vue相同点不同点如何实现一个组件,前端组件的设计原则是什么?Vuecomputed 与 watch 的区别Vue.nextTick 原理和作用Vue3.x 的新特性ReactReact 生命周期React 中使用 useEffect 如何清除副作用?对比 React 和 Vue 的 diff 算法React 中有哪几种类型的组件,如何选择?Css如何实现单行居中,多行居左?如何实现 1px 边框?多方法实现高度 100% 容器多方法实现圆角边框多方法实现文字描边JavaScriptes3相关题事件事件传播的过程Event Loop 的执行顺序?为什么 Vue.$nextTick 通常比 setTimeout 优先级高,渲染更快生效?一些ES6新特性列举类数组对象闭包什么是闭包?什么是词法?什么是函数柯里化?bind / apply / call / new手写bind手写 call手写apply手写new手写防抖手写节流原型链对比各种继承模块化webpack 中 loader 和 plugin 的区别?如何自定义一个 webpack 插件?对比 import、import() 和 requrie算法位运算什么是原码、反码、补码?位运算求绝对值?数组去除数组中的指定元素数组去重方法判断一个对象是不是数组 Array移动零空间复杂度为 O(1) 的中序遍历(莫里斯)遍历递归求最大公约数排序插入排序快速排序归并排序冒泡排序全栈node.jsexpress 中 next 的作用?对比 express 和 koa?计算机网络对比持续通信的方法网络结构按五层和七层分别是什么是 TCP 三次握手,为什么要三次握手?浏览器有哪些请求方法?提交表单的内容类型有哪些?docker 与虚拟机区别对比 Intel 的 VT-x,VT-d,VT-c对比 Cookie、LocalStorage、SessionStorage、Session、Toke什么是 Service Worker?性能如何测量并优化前端性能?SEO前端中有哪些 SEO 优化手段?安全什么是帆布指纹?对比各种分布式 ID 的生成方式各种跨域方法数据库什么是乐观锁,什么是悲观锁什么是事务文档管理工具对比 Git 和 SVN

View / MVVM 框架

对比 React 、Angular 和 Vue

相同点

React 和 Vue都提供了 Virtual Dom提供了 响应式(Reactive)和组件化(Composable)的视图组件注意力保持在核心库,而将其他功能如路由和全局状态管理交给相关的库

不同点

渲染优化

React:当组件状态发生变化时,以该组件为根,重新渲染整个组件子树

shouldComponentUpdate,采用合适方式,如不可变对象,比较 props 和 state,决定是否重新渲染当前组件使用 PureComponent:继承自 Component 类,自动加载 shoudComponentUpdate 函数,自动对

props 和 state 浅比较决定是否触发更新

Vue:自动追踪组件依赖,精确渲染状态改变的组件

HTML 和 CSS

React:支持 JSX

在构建视图时,使用完整的 JavaScript 功能开发工具多支持 JSX 语法高亮,类型检查和自动完成

Vue:提供渲染函数,支持 JSX,但默认推荐 Vue 模版

与书写 HTML 更一致的开发体验基于 HTML 的模版更容易迁移到 Vue设计师和新人开发者更容易理解和参与支持模板预处理器,如 Pug

CSS 作用域

React:通过 CSS-in-JS 方案,如 styled-components 和 emotion

Vue:

支持 CSS-in-JS 方案,如 styled-components-vue 和 vue-emotion

单文件组件中 style 标签可选 scoped 属性。支持 Sass \ Less \ Stylus 等 CSS 预处理器,深度集成

CSS Modules

规模

向上扩展:

React:

路由库和状态管理库,由社区维护支持,生态松散且繁荣提供脚手架工具,故意作了限制不允许在项目生成时进行配置只提供一个单页面应用模板不支持预设配置

Vue:

路由库和状态管理库,由官方维护支持,与核心库同步更新提供 CLI脚手架,可构建项目,快速开发组件的原型允许在项目生成时配置提供了各种用途的模板支持预设配置

向下拓展

React:学习曲线陡峭,需要前置知识:JSX ES,需要学习构建系统

Vue:既支持向上拓展与 React 一样,也支持向下拓展与 jQuery 一样,上手快

原生渲染

React Native:使用相同组件模型编写具有本地渲染能力的APP,跨平台开发

Weex:阿里巴巴发起,Apache 基金会孵化,同样支持本地渲染,跨平台开发

NativeScript-Vue,基于 Vue.js 构建原生应用的 NativeScript 插件

MobX

React:流行的状态管理框架

Vue:选择 Vue 比 React + MobX 更合理

Preact 和其它类 React 库

难以保证与 React 库 100% 兼容

Vue 和 Angular 相同点

TypeScript 都支持 TypeScript,支持 类型声明 和 组件装饰器

运行时性能,Angular 和 Vue 都很快

Vue 和 Angular 不同点

体积

Angular 用 AOT 和 tree-shaking 缩小体积

Vuex + Vue Router (gzip 后 30kB)比使用优化angular-cli (~65kB)小

灵活性

Vue 提供构建工具,不限制组织应用代码的方式

Angular 提供构建工具,有相对严格的代码组织规范

学习曲线

Vue 只需 HTML 和 JavaScript 基础

Angular 提供 API 和 概念 更多,设计目标针对 大型复杂应用,对新手有难度

如何实现一个组件,前端组件的设计原则是什么?

单一原则:一个组件只做一件事通过脑图、结构图,标识组件的 State Props Methods 生命周期,表示层次和数据流动关系State 和 Props扁平化:最多使用一层嵌套,便于对比数据变化,代码更简洁无副作用:State 仅响应事件,不受其他 State 变化影响 松耦合组件应该独立运行,不依赖其它模块配置、模拟数据、非技术说明文档、helpers、utils 与 组件代码分离 视图组件只关心视图,数据获取,过滤,事件处理应在外部 JS 或 父组件 中处理 Kiss原则(Keep it Simple Stupid)不需要 State 时,使用 函数组件不要传递不需要的 Props及时抽取复杂组件为独立组件不要过早优化 参考 CSS 的 OOSS 方法论,分离 位置 和 样式,利于实现皮肤考虑 多语言、无障碍 等后期需求

Vue

computed 与 watch 的区别

computed

支持数据缓存内部数据改变也会触发不支持异步,异步无效适用于 一个属性由其他属性计算而来,依赖其他属性的场景

watch

不支持数据缓存可以设置一个函数,带有两个参数,新旧数据支持异步监听数据必须是 data 中声明过或者父组件传递过来的 props 中数据 immediate:组件加载立即触发回调函数执行 deep:深度监听

Vue.nextTick 原理和作用

Vue 异步执行 DOM 更新,观察数据变化,开启队列缓冲同一事件循环中所有数据改变同一个 watcher 被多次触发,只会被推入到队列中一次,避免不必要的计算和 DOM 操作在下一个事件循环 tick 中,Vue 刷新队列并执行实际(已去重)工作异步队列使用 Promise.then 和 MessageChannel,不支持环境使用 setTimeout(fn, 0)在需要立即更新 DOM 的场景中使用

Vue3.x 的新特性

API 风格

Vue2.x:Options APIVue3.x:Composition API

生命周期

组件生命周期

//Vue2.x:beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyedactivateddeactivatederrorCaptured

//Vue3.x:setuponBeforeMountonMountedonBeforeUpdateonUpdateonBeforeUnmoutonUnmountedonActivatedonDeactivatedonErrorCapturedonRenderTriggeredonRenderTracked

指令生命周期

//Vue2.x:bindinsertedupdatecomponentUpdatedunbind

//Vue3.x:beforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted

数据

Vue2.x:dataVue3.x ref 基础类型和对象的双向绑定reactive 对象的双向绑定 ---- 通过 toRefs 转为 ref

监听

Vue2.x:watchVue3.x:watchEffect

传入回调函数,不需要指定监听的数据源,自动收集响应式数据

可以从回调函数中,获取数据的最新值,无法获取原始值

slot

Vue2.x:通过 slot 使用插槽,通过 slot-scope 给插槽绑定数据Vue3.x:v-slot: 插槽名 = 绑定数据名

v-model

Vue2.x:.async 绑定属性和 update:+ 属性名 事件Vue3.x:无需 .async 修饰

新功能

Teleport:适合 Model 实现

Suspense:

一个组件两个 template

先渲染 #fallback 内容,满足条件再渲染 #default

适合异步渲染显示加载动画,骨架屏等

defineAsyncComponent:定义异步组件

允许多个根节点

性能

使用 Proxy 代替 definePoperty 和 数组劫持标记节点类型,diff 时,跳过静态节点支持 ES6 引入方法,按需编译配套全新的 Web 开发构建工具 Vite

React

React 生命周期

React 16.4 + 生命周期MountingconstructorgetDerivedStateFromPropsrenderReact 更新 DOM 和 refscomponentDidMountUpdationprops 变化 → getDerivedStateFromPropssetState() → getDerivedStateFromProps → shouldComponentUpdateforceUpdate() → getDerivedStateFromPropsrendergetSnapshotBeforeUpdateReact 更新 DOM 和 refscomponentDidUpateUnmoutingcomponentWillUnmount

React 中使用 useEffect 如何清除副作用?

在 useEffect 中返回一个清除函数,名称随意,可以是匿名函数或箭头函数,在清除函数中添加 处理副作用的逻辑,如移除订阅等JavaScriptfunction component(props) {function handleStatusChange(status) {console.log(status.isOnine) }useEffect(() => {API.subscribe(props.id, handleStatusChange))}return function cleanup() {API.unsubscribe(props.id, handleStatusChange)}}

对比 React 和 Vue 的 diff 算法

(1)相同点

虚拟 Dom 只同级比较,不跨级比较使用 key 标记和复用节点,不建议使用数组索引 index 作为 key

(2)不同点

顺序

Vue:两端到中间React:从左到右

节点元素类型相同,ClassName 不同

Vue:不同类型元素,删除重新创建React:相同类型元素,修改

节点类型

Vue 3.x:VNode 创建时,即确定类型

React 中有哪几种类型的组件,如何选择?

无状态组件

更适合函数组件负责展示无状态,复用度高

有状态组件

函数组件 + hooks 或 类组件useState 或 声明 stateuseEffect 或 使用生命周期

容器组件

子组件状态提升到此,统一管理异步操作,如数据请求等提高子组件的复用度

高阶组件

接收组件,返回组件为原有组件增加新功能和行为代替 mixins,避免状态污染

回调组件

高阶组件的另一种形式将组件本身,通过 props.children 或 prop 属性 传递给子组件适合不能确定或不关心传给子组件数据的场景,如路由,加载组件的实现

Css

如何实现单行居中,多行居左?

父级元素:CSStext-align: center;自身元素:CSStext-align: left;display: inline-block;

如何实现 1px 边框?

方法一:border-widthCSS.border {border: 1px solid;}@media screen and {min-resolution: 2dppx} {.border {border: 0.5px solid;}}@media screen and (min-resolution: 3dppx) {.border {border: 0.333333px solid;}}方法二:伪类 + transformCSSdiv::after {content: '';display: block;border-bottom: 1px solid;}@media only screen and (min-resolution: 2dppx) {div::after {-webkit-transform: scaleY(.5);transform: scaleY(.5);}}@media only screen and (min-resolution: 3dppx) {div::after {-webkit-transform: scaleY(.33);transform: sacleY(.33);}}

多方法实现高度 100% 容器

(1)百分比<style>html, body {height: 100%;}div {height: 100%;background-color: azure;}</style><div></div>(2)相对单位<style>div {height: 100vh;background-color: azure;}</style><div></div>(3)calc<style>html, body{height: 100%;}div {height: calc(100%)}</style><div></div>

多方法实现圆角边框

背景图片:绘制圆角边框的图片,4个圆角 + 4个边框的小图片,拼成圆角边框border-radius: 5pxclip-path: inset(0 round 5px)

多方法实现文字描边

text-shadow: 0 0 1px black;-webkit-text-stroke: 1px black;position: relative / position: absolute 子绝父相

JavaScript

es3相关题

a = [],a.push(...[1, 2, 3]) ,a = ?a = [1, 2, 3],考核点如下:[].push:调用数组 push 方法apply:第一参数:指定 push 执行时的 this,即正在调用 push 的对象为数组 a第二参数:传入数组或类数组对象作为参数数组,展开作为 push 的参数列表push的语法:支持将一个或多个元素添加到数组末尾arr.push(element1, ..., elementN)

a = ?, a==1 && a==2 && a==3 成立== 会触发隐式转换,=== 不会对象转字符串先尝试调用对象的 toString()对象无 toString()或 toString 返回非原始值,调用 valueOf() 方法将该值转为字符串,并返回字符串结果否则,抛出类型错误对象转数字先尝试调用对象的 valueOf(),将返回原始值转为数字对象无 valueOf() 或 valueOf 返回不是原始值,调用 toString() 方法,将返回原始值转为数字否则,抛出类型错误对象转布尔值True代码JavaScriptconst a = {count: 0,valueOf() {return ++this.count}}数组隐式转换会调用数组的 join 方法,改写此方法JavaScriptconst a = [1, 2, 3]a.join = a.shift

null == undefined 结果比较 null 和 undefined 的时候,不能将 null 和 undefined 隐式转换,规范规定结果为相等

常见的类型转换

对比 get 和 Object.defineProperty相同点都可以定义属性被查询时的函数不同点在 classes 内部使用get 属性将被定义到 实例原型Object.defineProperty 属性将被定义在 实例自身

对比 escape encodeURI 和 encodeURIComponentescape对字符串编码ASCII 字母、数字 @ * / + - _ . 之外的字符都被编码encodeURI对 URL 编码ASCII 字母、数字 @ * / + 和 ~ ! # $ & () =, ; ?- _ . '之外的字符都被编码encodeURIComponent对 URL 编码ASCII 字母、数字 ~ ! * ( ) - _ . ' 之外的字符都被编码

事件

事件传播的过程

事件冒泡

DOM0 和 IE支持(DOM1 开始是 W3C 规范)从事件源向父级,一直到根元素(HTML)某个元素的某类型事件被触发,父元素同类型事件也会被触发

事件捕获

DOM2 支持从根元素(HTML)到事件源某个元素的某类型事件被触发,先触发根元素,再向子一级,直到事件源

事件流

事件的流向:事件捕获 → 事件源 → 事件冒泡

阻止事件冒泡

标准:event.stopPropagation()IE:event.cancelBubble = true

Event Loop 的执行顺序?

宏任务

Task Queue常见宏任务:setTimeout、setInterval、setImmediate、I/O、script、UI rendering

微任务

Job Queue常见微任务:浏览器:Promise、MutationObserverNode.js:process.nextTick

执行顺序

首先执行同步代码,宏任务同步栈为空,查询是否有异步代码需要执行执行所有微任务执行完,是否需要渲染页面重新开始 Event Loop,执行宏任务中的异步代码

为什么 Vue.$nextTick 通常比 setTimeout 优先级高,渲染更快生效?

Vue.$nextTick 需要异步执行队列,异步函数的实现优先使用

Promise、MutationObserver、setImmediate都不兼容时,使用 setTimeout

Promise、MutationObserver、setImmediate 是微任务

setTimeout、UI rendering 是宏任务

根据执行顺序

Promise、MutationObserver、setImmediate 创建微任务,添加到当前宏任务微任务队列。队列任务执行完,如需渲染,即可渲染页面setTimeout 创建宏任务,如果此时正在执行微任务队列,需要等队列执行完,渲染一次后,重新开始 Event Loop,执行宏任务中的异步代码后再渲染

一些ES6新特性

let 和 constPromiseClass箭头函数函数参数默认值模版字符串解构赋值展开语法构造数组,调用函数时,将 数组表达式 或 string 在语法层面展开对象属性缩写键名和键值相同函数省略 function模块化

列举类数组对象

定义

拥有 length 属性若干索引属性的任意对象

举例

NodeListHTML Collections字符串arguments$ 返回的 jQuery 原型对象

类数组对象转数组

新建数组,遍历类数组对象,将每个元素放入新数组Array.prototype.slice.call(ArrayLike) 或 [].slice.call(ArrayLike)Array.from(ArrayLike)apply 第二参数传入,调用数组方法

闭包

什么是闭包?

闭包是由函数以及声明该函数的词法环境组合而成

一个函数和对其周围状态(词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包可以在内层函数中访问到其外层函数的作用域每当创建一个函数,闭包就会在函数创建的同时被创建出来

什么是词法?

词法,英文 lexical ,词法作用域根据源代码声明变量的位置来确定变量在何处可用

嵌套函数可访问声明于它们外部作用域的变量

什么是函数柯里化?

函数调用:一个参数集合应用到函数部分应用:只传递部分参数,而非全部参数柯里化(curry):使函数理解并处理部分应用的过程保存调用 curry 函数时传入的参数,返回一个新函数结果函数在被调用后,要让新的参数和旧的参数一起应用到入参函数

bind / apply / call / new

手写bind

第一个参数接收 this 对象

返回函数,根据使用方式

直接调用改变 this 指向拼接参数调用函数构造函数不改变 this 指向,忽略第一参数拼接参数new 函数

Function.prototype.myBind = function(_this, ...args) {const fn = thisreturn function F(...args2) {return this instanceof F ? new fn(...args, ...args2): fn.apply(_this, args.concat(args2))}}//使用function Sum (a, b) {this.v= (this.v || 0)+ a + breturnthis}const NewSum = Sum.myBind({v: 1}, 2)NewSum(3) // 调用:{v: 6}new NewSum(3) // 构造函数:{v: 5} 忽略 myBind 绑定this

手写 call

第一参数接收 this 对象改变 this 指向:将函数作为传入 this 对象的方法展开语法,支持传入和调用参数列表调用并删除方法,返回结果

Function.prototype.myCall = function(_this, ...args) {if (!_this) _this = Object.create(null)_this.fn = thisconst res = _this.fn(...args)delete _this.fnreturn res}// 使用function sum (a, b) {return this.v + a + b}sum.myCall({v: 1}, 2, 3) // 6

手写apply

第一参数接收 this 对象改变 this 指向:将函数作为传入 this 对象的方法第二个参数默认数组展开语法,支持调用参数列表调用并删除方法,返回结果

Function.prototype.myApply = function(_this, args = []) {if (!_this) _this = Object.create(null)_this.fn =thisconst res = _this.fn(...args)delete _this.fnreturn res}// 使用function sum (a, b) {return this.v + a + b}sum.myApply({v: 1}, [2, 3]) // 6

手写new

第一参数作为构造函数,其余参数作为构造函数参数继承构造函数原型创建新对象执行构造函数结果为对象,返回结果,反之,返回新对象

function myNew(...args) {const Constructor = args[0]const o = Object.create(Constructor.prototype)const res = Constructor.apply(o, args.slice(1))return res instanceof Object ? res : o}// 使用function P(v) {this.v = v}const p = myNew(P, 1) // P {v: 1}

手写防抖

声明定时器

返回函数

一定时间间隔,执行回调函数

回调函数

已执行:清空定时器未执行:重置定时器

function debounce(fn, delay) {let timer = nullreturn function (...args) {if (timer) clearTimeout(timer)timer = setTimeout(() => {timer = nullfn.apply(this, args)}, (delay + '') | 0 || 1000 / 60)}}

手写节流

声明定时器

返回函数

一定时间间隔,执行回调函数

回调函数

已执行:清空定时器未执行:返回

function throttle(fn, interval) {let timer = nullreturn function (...args) {if (timer) returntimer = setTimeout(() => {timer = nullfn.apply(this, args)}, (interval +'')| 0 || 1000 / 60)}}

原型链

对比各种继承

(1)原型链继承

子类原型指向父类实例

function Parent() {}function Child() {}Child.prototype = new Parent()const child = new Child()

好处:

子类可以访问到父类新增原型方法和属性

坏处:

无法实现多继承创建实例不能传递参数

(2)构造函数

function Parent() {}function Child(...args) {Parent.call(this, ...args) // Parent.apply(this, args)}const child = new Child(1)

好处:

可以实现多继承创建实例可以传递参数

坏处:

实例并不是父类的实例,只是子类的实例不能继承父类原型上的方法

(3)组合继承

function Parent() {}function Child(...args) {Parent.call(this, ...args)}Child.prototype = Object.create(Parent.prototype)Child.prototype.constructor = Childconst child = new Child(1)

好处:

属性和原型链上的方法都可以继承创建实例可以传递参数

(4)对象继承

斜体样式

Object.create

JavaScriptconst Parent = {property: 'value', method() {} }const Child = Object.create(Parent)createJavaScriptconst Parent = {property: 'value', method() {} }function create(obj) {function F() {}F.prototype = objreturn new F()}const child = create(Parent)

好处:

可以继承属性和方法

坏处:

创建实例无法传递参数

传入对象的属性有引用类型,所有类型都会共享相应的值

(5)寄生组合继承

JavaScriptfunction Parent() {}function Child(...args) {Parent.call(this, args)}function create(obj) {function F() {}F.prototype = objreturn F()}function extend(Child, Parent) {const clone = create(Parent.prototype)clone.constructor = ChildChild.prototype = clone}extend(Child, Parent)const child = new Child(1)

(6)ES6继承

JavaScriptclass Parent {constructor(property) {this.property = property}method() {}}class Child extends Parent {constructor(property) {super(property)}}

模块化

webpack 中 loader 和 plugin 的区别?

loader

在打包前或期间调用根据不同匹配条件处理资源调用顺序与书写顺序相反写在前面的接收写在后面的返回值作为输入值

plugin基于 Tapable 实现事件触发调用,监听 webpack 广播的事件不同生命周期改变输出结果

如何自定义一个 webpack 插件?

声明一个自定义命名的类或函数在原型中新增 apply 方法声明由 Compiler 模块暴露的生命周期函数使用 webpack 提供的 API 或 自行处理内部实例的数据处理完成后,调用 webpack 提供的回调函数

示例:

实现一个 MyPlugin,获取指定图片,新增静态资源到本地

class MyPlugin {// 声明类apply(compiler) {// 新增 apply 方法// 声明生命周期函数compiler.hooks.additionalAssets.tapAsync('MyPlugin', (compilation, callback) => {download('https://img.shields.io/npm/v/webpack.svg', res => {if (res.status === 200) {// 调用 APIcompilation.assets['webpack-version.svg'] = toAsset(res)// 异步处理后,调用回调函数callback()} else {callback(new Error('[webpack-example-plugin] Unable to download the image'))}})})}}

对比 import、import() 和 requrie

算法

位运算

什么是原码、反码、补码?

原码、反码和补码,均有符号位数值位两部分

符号位:用 0 表示正,用 1 表示负

在计算机系统中

数值一律用补码来表示和存储,好处

符号位和数值位统一处理加法和减法统一处理

正数

原码:符号位 0

反码:与原码相同

补码:与原码相同

负数

原码:符号位 1

反码:符号位不变,数值位取反

补码:符号位不变,数值位取反 +1

0

原码:+0 0000 0000 -0 1000 0000

反码:+0 0000 0000 -0 1111 1111

补码:+0 0000 0000 -0 0000 0000 相同

位运算求绝对值?

数在计算机中,用补码表示

负数的补码 = 负数的原码 → 符号位不变,其余位取反,+1

-2

原码:1000 0010

反码:1111 1101

补码:1111 1110(反码加一,计算机实际存储的值)

取反:0000 0001

加一:0000 0010 = 2

解法一:根据符号位,正数返回本身,负数返回 取反 + 1

const abs = x => {

const y = x >>> 31 // 看符号位

return y === 0 ? x : ~x + 1

}

解法二:任何数与 -1 异或 ^ 运算,相当于取反。任何数与 0 异或 ^ 运算,还是本身

-1

原码:1000 0001

反码:1111 1110

补码:1111 1111

无符号右移

const abs = x => {const y = x >>> 31return (x ^ -y) + y}

有符号右移

const abs = x => {const y = x >> 31return (x ^ y) - y}

数组

去除数组中的指定元素

输入:a = [‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’] target = ‘4’

输出:a = [‘1’, ‘2’, ‘3’, ‘5’, ‘6’]

方法一:

const removeByValue = (a, target) => {for (let i = 0; i < a.length; i++) {if (target === a[i]) {a.splice(i, 1)}}return a}

方法二:

const removeByValue = (a, target) => a.splice(a.findIndex(v => v === target), 1)

方法三:

const removeByValue = (a, target) => {let j = 0, i = -1while (++i < a.length) {if (a[i] === target) i++a[j++] = a[i]}a.length--return a}

数组去重方法

function unique (arr) {

return Array.from(new Set(arr))

}

Set 去重 + 扩展运算符 …

function unique (arr) {return [...new Set(arr)]}

Object

function unique (arr) {const h = Object.create(null)arr.forEach(v => h[v] = 1)return Object.keys(h).map(v => v | 0)}

Map

function unique (arr) {const h = new Maparr.forEach(v => h.set(v, 1))return Array.from(h.keys())}

for 循环 + splice

function unique (arr) {for (let i = 0; i < arr.length; i++) {for (let j = i + 1; j < arr.length; j++) {if (arr[i] === arr[j]) {arr.splice(j, 1)j--}}}return arr}

Sort 排序 + 相邻相同 splice

function unique (arr) {arr.sort()for (let i = 0; i < arr.length - 1; i++) {if (arr[i] === arr[i + 1]) {arr.splice(i, 1)i--}}return arr}

filter + indexOf

function unique (arr) {return arr.filter((v, index, ar) => ar.indexOf(v) === index)}

filter + hasOwnproperty

function unique (arr) {const h = {} // 注意只有 {} 才有 hasOwnPropertyreturn arr.filter(v => !h.hasOwnProperty(v) && (h[v] = 1))}

indexOf + 辅助数组

function unique (arr) {const r = []arr.forEach(v => r.indexOf(v) === -1 && r.push(v)) return r}

includes + 辅助数组

function unique (arr) {const r = []arr.forEach(v => !r.includes(v) && r.push(v))return r}function unique (arr) {const r = []arr.forEach(v => !r.includes(v) && r.push(v))return r}

判断一个对象是不是数组 Array

isPrototypeOf

测试一个对象是否在另一个对象的原型链上prototype 不可省略

function isArray(o) {return Array.prototype.isPrototypeOf(o)}

instanceof

用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

function isArray(o) {return o instanceof Array}

Array.isArray

用于确定传递的值是否是一个 Array

function isArray(o) {return Array.isArray(o)}

Object.prototype.toString

方法返回一个表示对象的字符串

function isArray(o) {return Object.prototype.toString.call(o) === '[object Array]'}

移动零

问题

给定一个数组 nums,编写一个函数所有 0 移动到数组的末尾,同时保持非零元素的相对顺序

示例:

输入:[0, 1, 0, 3, 12]

输出:[1, 3, 12, 0, 0]

说明:

(1)必须在原数组上操作,不能拷贝额外的数组

(2)尽量减少操作次数

解析

(1) 辅助数组

const moveZeros = a => {const tmp = new Uint32Array(a.length)for (let i = 0, j = 0; i < a.length; i++) if (a[i]) tmp[j++] = a[i]return tmp}

(2) 双指针交换

const moveZeros = a => {let i = j = -1while (++i < a.length) if (a[i]) a[++j] = a[i]while (++j < a.length) a[j] = 0return a}

(3) Sort 排序

const moveZeros = a => a.sort((a, b) => -(b === 0 ^ 0))

空间复杂度为 O(1) 的中序遍历(莫里斯)遍历

const isValidBST = (root, l = -Infinity) {while(root) {if (root.left) {const p = root.leftwhile (p.right && p.right !== root) p = p.rightif (p.right === null) p.right = root, root = root.leftelse {root = root.rightp.right= null}} else {root= root.right}}}

递归

求最大公约数

辗转相除法 (又称 欧几里得算法)

递归

function gcd(a, b) {const t = a % bif (t === 0) return breturn gcd(b, t)}

迭代

function gcd(a, b) {let twhile (t = a % b) {a = bb = t}return b}

更相减损法(又称 九章算术)

递归

function gcd(a, b) {if (a === b) return ba > b ? a -= b : b -= areturn gcd(a, b)}

排序

插入排序

const sort = a => {for (let i = 1; i < a.length; i++)for (let j = i; j-- && a[j + 1] < a[j];)[a[j + 1], a[j]] = [a[j], a[j + 1]]return a}

快速排序

const sort = (a, s = 0, e = a.length - 1) => {if (s >= e) returnlet l = s, r = ewhile (l < r) {while (l < r && a[r] >= a[s]) r--while (l < r && a[l] <= a[s]) l++[a[l], a[r]] = [a[r], a[l]]}[a[l], a[s]] = [a[s], a[l]]sort(a, s, l - 1)sort(a, l + 1, e)return a}

归并排序

const sort = (a, l = 0, r = a.length - 1) => {if (l === r) return const m = l + r >>> 1, t = []sort(a, l, m)sort(a, m + 1, r)let p1 = l, p2 = m + 1, i = 0while (p1 <= m || p2 <= r) t[i++] = p2 > r || p1 <= m && a[p1] < a[p2] ? a[p1++] : a[p2++]for (i = 0; i < t.length; i++) a[l + i] = t[i]return a}

冒泡排序

const sort = (a) => {for(let i = 0; i < a.length - 1; i++)for (let j = 0; j < a.length - 1 - i; j++)if (a[j] > a[j + 1])[a[j], a[j + 1]] = [a[j + 1], a[j]]return a}

全栈

node.js

express 中 next 的作用?

next 是 中间件函数的第三参数

执行 next(),控制权交给下个中间件不执行

终止请求

挂起请求

对比 express 和 koa?

Handler 处理方式

Express:回调函数Koa:Async / Await

中间件

Express:同一线程,线性传递Koa:洋葱模型,级联传递

响应机制

Express:res.send 立即响应Koa:设置ctx.body,可累加,经过中间件后响应

计算机网络

对比持续通信的方法

轮询

通过 setInterval 或 setTimeout 定时获取并刷新页面上的数据定时查询,不一定有新数据并发较多时,增加服务器负担

长连接

页面其纳入 iframe,将 src 设为长连接减少无用请求并发较多时,增加服务器负担

长轮询

服务端收到请求后,hold 住链接,直到新消息返回时才响应减少无用请求返回数据顺序无保证

Flash Socket

客户端通过嵌入 Socket 类 Flash与服务器端的 Socket 接口通信真正即时通信非 HTTP 协议,无法自动穿越防火墙

WebSocket

在客户端和服务器间打开交互式通信绘话兼容 HTTP 协议。与 HTTP 同属应用层。默认端口是 80 和 443建立在 TCP 协议基础之上,与 HTTP 协议同属于应用层数据格式轻量,性能开销小,通信高效可以发送文本,也可以发送二进制数据没有同源限制协议表示符:ws,加密 wss

socket.io

跨平台的 WebSocket 库,API 前后端一致,可以触发和响应自定义事件

// 服务端const io = require("socket.io")(3000)io.on('connection', socket => {socket.on('update item', (arg1, arg2, callback) => {console.log(arg1, arg2)callback({status: 'fulfilled' })})})// 客户端const socket = io()socket.emit('update item', "1", {name: 'updated' }, res => {console.log(res.status) // ok})

网络结构按五层和七层分别是

TCP / IP 体系结构

网络接口层网际层 IP运输层 TCP 或 UDP应用层(TELNET FTP SMTP等)

五层

物理层数据链路层网络层运输层应用层

七层:Open System Inerconnect Reference Model 开放式系统互联通信参考模型

物理层数据链路层网络层传输层会话层表达层应用层

什么是 TCP 三次握手,为什么要三次握手?

(1)什么是 TCP 三次握手?

起初

客户端 CLOSED

服务端 CLOSED

第一次握手

客户端发送请求报文

传输自身通讯初始序号客户端 SYN-SENT

第二次握手

服务器接收请求报文

1.同意连接

传输自身通讯初始序号服务端 SYN-RECEIVED

2.拒绝连接

服务端 REFUSED

第三次握手

1.客户端接收应答报文

客户端发送确认报文

客户端 ESTABLISHED

2.服务端接收确认报文

服务端 ESTABLISHED

(2)为什么要三次握手?

客户端首次发送请求无应答,TCP 启动超时重传服务器收到重传的请求,建立连接,接收数据,关闭连接服务器收到客户端首次发送请求,再次建立连接,但客户端已经关闭连接需要三次握手,客户端发送确认报文后再建立连接,避免重复建立连接

浏览器有哪些请求方法?

提交表单的内容类型有哪些?

application/x-www-form-urlencoded:初始的默认值

Content-Type:application/x-www-form-urlencoded

key1=value1&key2=value2

multipart/form-data:适用于使用 标签上传的文件

Content-Type:multipart/form-data; boundary=------数据边界值

------数据边界值

Content-Disposition: form-data; name=“key1”

value1

------数据边界值

Content-Disposition: form-data; name=“key2”

value2

text/plain:HTML5 引入类型

Content-Type:text/plain

key1=value1

key2=value2

docker 与虚拟机区别

启动速度

Docker:秒级启动虚拟机:几分钟启动

需要资源

Docker:操作系统级别虚拟化,与内核直接交互,几乎没有性能损耗

虚拟机:

通过虚拟机监视器(Virtual machine monitor,VMM,Hypervisor)厂商:Citrix XenServer,Hyper-V,开源 KVM 、Xen、VirtualBSD

轻量

Docker:内核与应用程序库可共享,占用内存极小,资源利用率高虚拟机:同样硬件环境,Docker 运行的镜像数量远多于 虚拟机数量

安全性

Docker:安全性更弱。租户 root 与宿主机 root 等同,有提权安全风险

虚拟机:

租户 root 权限与宿主机的 root 虚拟机权限分离用如 Intel 的 VT-d 和 VT-x 的 ring-1 硬件隔离技术,虚拟机难以突破 VMM 直接与宿主或彼此交互

可管理型

Docker:集中化管理工具还不算成熟虚拟机:拥有相对完备的集中化管理工具

高可用和可恢复性

Docker:依靠 快速重新部署 实现虚拟机:负载均衡、高可用、容错、迁移、数据保护,VMware可承诺 SLA(服务级别协议)99.999% 高可用

快速创建、删除

Docker:秒级别,开发、测试、部署更快虚拟机:分钟级别

交付、部署

Docker:在 Dockerfile 中记录了容器构建过程,可在集群中实现快速分发和快速部署虚拟机:支持镜像

对比 Intel 的 VT-x,VT-d,VT-c

VT-x

运行 ESXI 上的 64位 Guest OS 基本指令

Intel 运用Virtualization 虚拟化技术中的指令集

减少 VMM 干预,硬件支持 VMM 与 Guest OS

需要 VMM 干预,更快速、可靠、安全切换

更灵活、更稳定

VT-d

使一个 Guest OS 独占并直接存取硬件设备,加快读写速度

开启条件:北桥芯片支持,BIOS 开启

VT-c

Virtual Machine Direct Connect

虚拟机的虚拟网络卡传送透过 VMM 来进行传输Guest OS 可以直接对 实体网络 I/O 进行存取加强 VT-d,让多个虚拟机与实体 I/O 装置同时建立通道

Virtual Machine Direct Queues

由 VMM 管理的虚拟化 Switch 转送封包给虚拟机封包流向哪个虚拟机,需要额外 CPU 资源透过网卡内建 Layer2 classifier / sorter 加速网络资料传送在芯片里安排并通过队列排序好封包给虚拟机,不需要 VMM 支持大大降低网络负载和 CPU 使用率

对比 Cookie、LocalStorage、SessionStorage、Session、Toke

Cookie

保存位置:客户端生命周期:expires 前有效,不设置 会话期间有效存储大小:一般 4KB数据类型:字符串特点:请求头 cookie ,同域名每次请求都附加响应头 set-cookie 设置 cookie

LocalStorage

保存位置:客户端生命周期:不清空缓存,一直有效存储大小:一般 5MB数据类型:字符串SessionStorage保存位置:客户端生命周期:会话期间有效存储大小:一般 5MB数据类型:字符串

Seesion

保存位置:服务端,sessionid 存在 cookie 或 跟在 URL 后生命周期:默认 20分钟,可更改。清空 cookie 或 更改 URL 传参,影响 Session存储大小:不限制数据类型:字符串、对象、数组,序列化后以字符串存储

Token

保存位置:客户端 Cookie / WebStorage / 表单 等生命周期:根据存储位置决定存储大小:根据存储位置决定数据类型:字符串组成uid 用户身份标识time 时间戳sign 签名其他参数

JWT - JSON Web Token

特点将部分用户信息存储本地,避免重复查询数据库组成Header(头部)alg 表示签名的算法,默认是 HMAC SHA256 (HS256)typ 表示令牌的类型,JWTPayload(负载)7 个官方字段jti 编号iss 签发人sub 主题aud 受众exp 过期时间nbf 生效时间iat 签发时间私有字段name 姓名admin 是否管理员Signature(签名)secret 密钥使用指定 alg 签名算法,生成签名HMAC SHA256(btoa(Header) + '.' + btoa(Payload), secret)算出签名

什么是 Service Worker?

Web 应用程序、浏览器与网络之间的代理服务器拦截网络请求,并根据网络采取适当动作,更新服务器资源完全异步,不阻塞 JavaScript 线程,不能访问 DOM提供入口以推送通知和访问后台同步 API用于离线缓存,后台数据同步,响应来自其它源的资源请求,集中接收成本高的数据更新

性能

如何测量并优化前端性能?

(1)前端页面加载阶段

网络

重定向

redirectStart

redirectEnd

DNS 查询

domainLookupStart

domainLookupEnd

TCP 连接

connectStart

conentEnd请求响应

发送请求

requestStart

收到响应

responseStart

responseEnd解析渲染

DOM 解析

domLoading

domInteractive

DOM 渲染

domContentLoadedEventStart

domContentLoadedEventEnd

domComplete

(2)衡量性能的时间点

首字节时间:收到服务器返回第一个字节的时间。反映网络后端的整体响应耗时

白屏时间:第一个元素显示时间

首屏时间:第一屏所有资源完整显示时间。SEO 指标,百度建议2秒内为宜,不超过3秒

(3)测量性能的工具

Chrome 浏览器 ,开发者工具

Performance:通过录制页面加载过程,可以获得加载,脚本执行,渲染,布局等时间分布情况

Lighthouse:可以分别模拟移动端和电脑端打开URL,从性能、可访问性、最佳实践(安全、用户体验、函数是否符合标准等)、SEO、单页应用等角度,获得修改建议

NodeJS

SiteSpeed:

npm i -g sitespeed.io && sitespeed.io https://leetcode-

线上测试工具

Speedcurve:

webpagetest:

(4)性能优化方法

DNS 预读取:

<link rel="dns-prefetch" href="//leetcode-" />

预加载

Image / Audio

prefetch:

<link rel="prefetch" href="//leetcode-" />

懒加载

图片懒加载

DOM懒加载渲染

减少重排

减少重绘

节流

防抖缓存

离线缓存

HTTP 缓存图片

合并图片请求

Base64 将图片嵌入 CSS

SVG sprites

响应式图片:不同分辨率和 DPR 下显示不同图片

媒体查询

IMG 的 srcset 属性

图片压缩

image-webpack-loader、imagemin-webpack-plugin 工程化压缩图片工具

WEBP 和 AVIF 图片格式HTTP

使用 HTTP2 / HTTP3

使用 CDN

使用 gzip / br 压缩静态资源

SEO

前端中有哪些 SEO 优化手段?

文字

字号 14px 以上,16px 为宜,避免小于 12px,有行距、换行和段落

避免使用 visibility:hidden,display: none, 绝对定位,与背景相同颜色,堆砌关键词,隐藏主要内容

图片

Logo 的 DIV 中写关键词,设置 text-ident 为负数隐藏

图片添加 width 和 height,避免读取元数据,再重排

图片添加 alt,描述图片内容,便于搜索引擎蜘蛛理解,提升可访问性

主要图片与内容相关,不要过长或过窄,正方形为宜

主要图片设置 src 属性,避免全部图片都使用懒加载

图片可以点击放大,多张图片间可以切换

适配

1.独立 H5 网站

H5移动版与PC版域名不同,移动版子域名,推荐使用 等移动网站常用域名

向搜索引擎提交适配规则:PC版和H5移动版组成一一对应的URL对或正则匹配关系

不同域名,使用不同蜘蛛抓取

2.代码适配

H5 移动版和PC版域名相同,返回代码不同

后端根据 User-Agent,返回不同的代码

添加响应头:Vary: User-agent

同一URL,蜘蛛应使用不同UA或不同蜘蛛多次抓取

3.自适应

H5 移动版和PC版 域名相同,返回代码相同

前端通过相对单位、媒体查询、srcset 实现响应式或自适应布局

同一URL,蜘蛛可以只抓取一次

SSR:Server Side Render 服务端渲染

安全

什么是帆布指纹?

const canvas = document.createElement('canvas')const ctx = canvas.getContext('2d')const txt = 'canvasfingerprinting'ctx.fillText(txt, 0, 0)const b64 = canvas.toDataURL().replace('data:image/png;base64,', '') // 返回一个包含图片的 data URI,并替换 Mine-type,返回 base64 编码const bin = atob(b64) // 函数能够解码 base64 编码的字符串数据const canvasFingerprinting = bin2hex(bin.slice(-16, -12)) // 将 Unicode 编码转为十六进制

对比各种分布式 ID 的生成方式

UUID

生成简单,本地生成

无序,无意义

数据库自增ID

auto_increment 实现简单,单调自增,查询快

数据库服务器宕机风险

使用 SELECTLAST_INSERT_ID() 代替 max(id) 提高高并发时的查询效率

不同数据库服务器,设置不同自增起始值和步长

数据库自增ID段

CREATE TABLE ids_increment (

type int(8) NOT NULL COMMENT ‘业务类型’,

max_id int(8) NOT NULL COMMENT ‘当前业务的最大ID’,

step int(8) NOT NULL COMMENT ‘当前业务的ID段步长’,

version ini(8) NOT NULL COMMENT ‘当前业务的版本号,每次分配 +1,乐观锁’

)

Redis

set 字段的初始值 incr 该字段,每次 +1

使用 RDB 定时持久化

使用 AOF 命令持久化

雪花算法

64 位 ID:符号位(1 位) + 时间戳(41 位) + 机器 ID(5 位) + 数据中心 ID(5 位)+ 自增值(12 位)

各种跨域方法

(1)跨域

协议、域名、端口不同,需要跨域

(2)跨域解决方案

jsonp:JSON with Padding

<script> 标签没有同域限制服务端返回页面上 callback 包裹的 json 数据兼容性好仅支持 GET

CORS:Cross-origin resource sharing 跨域资源共享

请求类型:

简单请求 :

满足以下 2 条件

GET HEAD POSTContent-Typeapplication/x-www-form-urlencodedmultipart/form-datatext/plain** 复杂请求** :不满足以上2条件需发送 OPTION 预检请求,查询服务器支持的方法按请求类别处理const express = require('express')const app = express()const whiteList = ['http://localhost']app.use((req, res, next) => {const origin = req.headers.originif (whiteList.includes(origin)) {// 允许访问源,附带凭证请求时,必须指定 URI。相反,可设置为 *res.setHeader('Access-Control-Allow-Origin', origin)// 允许请求头,逗号隔开res.setHeader('Access-Control-Allow-Headers', 'authorization, username')// 允许请求方法,逗号隔开res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, POST, OPTIONS')/* 允许凭证客户端需配置 const xhr = new XMLHttpRequest()xhr.withCredentials = true */res.setHeader('Access-Control-Allow-Credentials', true)// 预检请求的返回结果,允许请求头和请求方法可以被缓存多久,缓存期间不再发送预检请求,单位秒res.setHeader('Access-Control-Allow-Max-Age', 10)// 允许响应头res.setHeader('Access-Control-Expose-Headers', 'authorization, username')if (req.method === 'OPTIONS') {res.end() // 预检请求直接返回空响应体}}})

postMessage

window属性

跨窗口,跨框架frame、iframe,跨域

允许不同源脚本采用异步方式进行有限通信

otherWindow.postMessage(message, targetOrigin, [transfer])// otherWindow:iframe.contentWindow 属性window.open 返回window.frames 访问window.onmessage = e => console.log(e.data) // 接收返回数据

Websocket

基于 TCP

全双工通信

应用层协议

// 客户端const socket = new WebSocket('ws://localhost:3000')socket.onopen = () => socket.send('message') // 向服务器发送数据soket.onmessage = e => console.log(e.data) // 接收服务器返回数据// server.jsconst express = require('express')const app = express()const ws = require('ws')const wss = new ws.Server({port: 3000})wss.on('connection', ws => {ws.on('message', data => console.log(data))})

代理服务器转发

代理服务器设置 CORS 请求头字段nginxadd_headerkangle回应控制,增加表,标记模块,add_headernodejsresponse.writeHead代理服务器向源服务器继续请求nginx七层 HTTP 代理:配置 httphttp {server {listen 80;location / {proxy_pass http://127.0.0.1:3000}}}四层 TCP 代理:配置 streamsteam {upstream proxyServer {server 127.0.0.1:3000;}server {listen 80;proxy_pass proxyServer;}}kangle七层 HTTP 代理请求控制,增加表目标设置为代理匹配模块设置原域名和端口,标记模块设置目标域名和端口nodejs七层 HTTP 代理const http = require('http')http.request({host: '127.0.0.1',port: 3000,url: '/'})

document.domain

主域名相同,子域名不同设置值为 主域名iframewindow.name不超过 2MB同一个iframe加载跨域页,跨域页设置 window.name加载同域页,读取 iframe.contentWindow.namelocation.hash不会被 URL 编码加载跨域页,参数跟在 # 后。window.onhashchange 监听 hash 变化跨域页获取 location.hash,加载同域页,参数跟在 # 后同域页设置 window.parent.parent.location.hash = location.hash,将参数回传

数据库

什么是乐观锁,什么是悲观锁

并发控制

需要保证并发情况下的数据准确性

保证一个用户的工作不会对另一个用户的工作产生不合理的影响

避免

脏读:一个事务看见另一个事务未提交的数据不可重复读:一个事务两次读取到的数据内容不同,另一事务修改了数据幻读:一个事务两次读取到的数据条数不同,另一事物增删了数据

乐观锁:多读 适用

仅在数据提交更新时,检测数据冲突。冲突时,采用竞争机制依靠数据本身来保证数据正确性,如添加字段 version

悲观锁:多写 适用

共享锁(读锁):多个事务同一数据共享锁,只读不可写排他锁(写锁):一次仅一个事务可读写数据依赖数据库的锁机制,如 MySQL:select … for update

什么是事务

(1)定义

访问并可能更新数据库中数据项的一个程序执行单元(Uint)一条 SQL、一组 SQL 和 整个程序都可以是事务

(2)特点

原子性(atomicity):不可分割,要么都做,要么都不做一致性(consistency):一个一致性状态变到另一个一致性状态隔离性(isolation):一个事务的执行不能被其他事务干扰,操作和数据使用不依赖和干扰其它并发事务持久性(durability):提交后的改变永久生效。不受故障或其它操作影响

(3)在 Mysql 中使用事务

# 关闭自动提交SET autocommit = 0# 或者开始一个事务BEGIN若干SQL语句# 提交事务COMMIT 对数据库所有修改都永久生效若干SQL语句# 回滚事务ROLLBACK 结束当前事务,撤销所有未提交的更改# 设置保存点SAVEPOINT id 创建名为 id 的保存点# 回滚事务到保存点ROLLBACK TO id 把事务回滚到保存标记点# 设置事务的隔离级别SET TRANSACTION InnoDB 存储引擎提供事务的隔离级别有(1) READ UNCOMMITTED(2) READ COMMITED(3) REPEATABLE READ(4) SERIALIZABLE

##项目管理

文档管理工具

API 接口协作管理工具SwaggerSpringApizza文档协作工具石墨文档有道笔记 + 有道云协作印象笔记自动文档生成apiDoc:根据 API 描述,自动生成文档jsDoc:根据 JS 注释,自动生成文档

对比 Git 和 SVN

架构Git:分布式,本地克隆版本库无网络依然可以操作分支,提交文件,查看历史记录SVN:集中式,代码一致性高依赖网络存储Git:按元数据,即描述文件的数据SVN:按文件分支Git:移动指针,创建切换快SVN:拷贝目录权限Git:经常按项目配置更适合开源SVN:经常按目录配置更适合内部开发

如果觉得《【JavaScript】阶段性复习》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。