一、HTML
1、html5的语义化的理解
对html内容的结构化选择合适的标签,使代码的可读性增强,并且有助于seo搜索引擎的优化。并且支持读屏软件,根据文章自动生成目录。
常见的语义化标签有:
全局作用域:一个 html 页面就是一个全局作用域,打开页面的时候, 作用域就生成了, 直到关闭页面为止=> 自当前 script 标签开始的任何位置
局部作用域:每一个函数就是一个私有作用域。
ECMAScript 6(简称 ES6)中新增了块级作用域。
块级作用域:块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作用域。
作用域链:多个作用域形成的链式结构
10、this的指向问题
- 第一种是函数调用模式,当一个函数不是一个对象的属性时,直接作为函数来调用时,this 指向全局对象。
- 第二种是方法调用模式,如果一个函数作为一个对象的方法来调用时,this 指向这个对象。
- 第三种是构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。
- 第四种是 apply 、 call 和 bind 调用模式,这三个方法都可以显示的指定调用函数的 this 指向。其中 apply 方法接收两个参数:一个是 this 绑定的对象,一个是参数数组。call 方法接收的参数,第一个是 this 绑定的对象,后面的其余参数是传入函数执行的参数。也就是说,在使用 call() 方法时,传递给函数的参数必须逐个列举出来。bind 方法通过传入一个对象,返回一个 this 绑定了传入对象的新函数。这个函数的 this 指向除了使用 new 时会被改变,其他情况下都不会改变。
这四种方式,使用构造器调用模式的优先级最高,然后是 apply、call 和 bind 调用模式,然后是方法调用模式,然后是函数调用模式。
11、promise的理解
promise是为了解决回调地狱产生的,它是一个对象,可以获取异步操作的消息。它有三个状态:pending、rejected(已拒绝)、resolved(已完成),它的状态一旦从进行状态转换成其他状态就不能再改变了。
promise的特点: - 一旦创建立即执行,无法取消
- 如果不设置回调,其内部抛出的错误无法反应到外部
- 处于pending状态时无法得知进度
Promise有五个常用的方法:then()、catch()、all()、race()、finally()
- Promise.all和Promise.race的区别的使用场景
(1)Promise.all
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
Promise.all中传入的是数组,返回的也是是数组,并且会将进行映射,传入的promise对象返回的值是按照顺序在数组中排列的,但是注意的是他们执行的顺序并不是按照顺序的,除非可迭代对象为空。
需要注意,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,这样当遇到发送多个请求并根据请求顺序获取和使用数据的场景,就可以使用Promise.all来解决。
(2)Promise.race
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。当要做一件事,超过多长时间就不做了,可以用这个方法来解决。
13、什么是事件流
事件捕捉 目标事件 事件冒泡
14、axios如何拦截请求
axios有两种拦截方式:
1.axios.interceptors.request.use() 请求拦截
axios.interceptors.request.use( function ( config ) { return config })
这个方法的参数是一个函数,发送请求之前就会执行这个函数,函数里面的参数就是执行这个函数拦截到的配置对象config。
可以发起请求之前,做一个拦截,把数据(参数,配置…)做了处理再发送请求。
2.axios.interceptors.response.use() 响应拦截
axios.interceptors.response.use( function ( res ) { return res } )
这个方法的参数是一个函数,请求成功之前后就会执行这个函数,函数里面的参数就是执行这个函数拦截到的结果数据
在请求成功之后,对数据有固定处理方式,就可以在响应拦截里面做。比如直接返回data,减少返回数据获取的代码量;还有一些状态的固定处理,不同的后端,他们代码风格可能是不一样。有些可能是用code 表示状态 eg:20000代表成功 50002没有权限 …。还有些用status表示状态, eg: success 代表成功, fail代表失败,只能要是后端代码风格确定了,这些状态通常是不会变的了。比如: 返回的请求失败的状态码,那就可以做一个通用的处理,给一个弹框提示。
六、vue
1、vue的生命周期
-
规律:
生命周期整体分为四个阶段,分别是:创建、挂载、更新、销毁,每个阶段都有两个钩子,一前一后。 -
Vue2
的生命周期
创建阶段:beforeCreate、created
挂载阶段:beforeMount、mounted
更新阶段:beforeUpdate、updated
销毁阶段:beforeDestroy、destroyed
另外还有 keep-alive 独有的生命周期,分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 activated 钩子函数。 -
Vue3
的生命周期
创建阶段:setup
挂载阶段:onBeforeMount、onMounted
更新阶段:onBeforeUpdate、onUpdated
卸载阶段:onBeforeUnmount、onUnmounted
2、组件通信的方式
1.父子通信:props/$emit,ref / r e f s , refs, refs,parent / c h i l d r e n 2. 父 子 、 非 父 子 通 信 : e v e n t B u s 事 件 总 线 ( children 2.父子、非父子通信:eventBus事件总线( children2.父子、非父子通信:eventBus事件总线(emit / o n ) 3. 祖 孙 , 层 级 很 深 通 信 : 依 赖 注 入 ( p r o v i d e / i n j e c t ) 4. 祖 孙 : on) 3.祖孙,层级很深通信:依赖注入(provide/ inject) 4.祖孙: on)3.祖孙,层级很深通信:依赖注入(provide/inject)4.祖孙:attrs / $listeners,实现组件之间的跨代通信
3、两种路由模式的区别 -
history模式:url更美观,不带有#,但后期项目上线需要后端配合处理路径的问题,否则刷新会有404的错误
-
hash模式:兼容性更好,在seo优化方面相对较差
hash模式
这里的 hash 就是指 url 尾巴后的 # 号以及后面的字符。这里的 # 和 css 里的 # 是一个意思。hash 也 称作 锚点,本身是用来做页面定位的,她可以使对应 id 的元素显示在可视区域内。
由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件,浏览器的进后退也能对其进行控制,所以人们在 html5 的 history 出现前,基本都是使用 hash 来实现前端路由的。
history模式
已经有 hash 模式了,而且 hash 能兼容到IE8, history 只能兼容到 IE10,为什么还要搞个 history 呢?
首先,hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。其次,hash 的传参是基于 url 的,如果要传递复杂的数据,会有体积的限制,而 history 模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。
最重要的一点:
如果不想要很丑的 hash,我们可以用路由的 history 模式—— 引用自 vueRouter 文档
history 模式改变 url 的方式会导致浏览器向服务器发送请求,这不是我们想看到的,我们需要在服务器端做处理:如果匹配不到任何静态资源,则应该始终返回同一个 html 页面。例如404
4、vuex的理解
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。 -
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
-
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化。
Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 提交修改信息, Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action ,但 Action 也是无法直接修改 State 的,还是需要通过Mutation 来修改State的数据。最后,根据 State 的变化,渲染到视图上。
有五种属性,分别是 State、 Getter、Mutation 、Action、 Module -
state => 基本数据(数据源存放地)
-
getters => 从基本数据派生出来的数据
-
mutations => 提交更改数据的方法,同步
-
actions => 像一个装饰器,包裹mutations,使之可以异步。
-
modules => 模块化Vuex
5、Mutation和actions
两者的不同点如下: -
Mutation专注于修改State,理论上是修改State的唯一途径;Action业务代码、异步请求。
-
Mutation:必须同步执行;Action:可以异步,但不能直接操作State。
-
在视图更新时,先触发actions,actions再触发mutation
-
mutation的参数是state,它包含store中的数据;store的参数是context,它是 state 的父级,包含 state、getters
6、pinia
也是一个状态管理工具
Store是一个保存:状态、业务逻辑 的实体,每个组件都可以读取、写入它。
它有三个概念:state、getter、action,相当于组件中的: data、 computed 和 methods。
相比于vuex: -
比如 mutations 不再存在
-
更友好的TypeScript支持,Vuex 之前对TS的支持很不友好;
-
不再有modules的嵌套结构:
-
你可以灵活使用每一个 store,他们是通过扁平化的方式来相互使用的;
-
也不会再有命名空间的概念,不需要记住他们的复杂关系;
7、query和params的区别
用法:query要用path来引入,params要用name来引入,接收参数都是类似的,分别是 this. r o u t e . q u e r y . n a m e 和 t h i s . route.query.name 和this. route.query.name和this.route.params.name 。
url地址显示:query更加类似于ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示
注意:query刷新不会丢失query里面的数据 params刷新会丢失 params里面的数据。
8、Vue的性能优化有哪些
(1)编码阶段 -
尽量减少data中的数据,data中的数据都会增加getter和setter,会收集对应的watcher
-
v-if和v-for不能连用
-
如果需要使用v-for给每项元素绑定事件时使用事件代理
-
SPA 页面采用keep-alive缓存组件
-
在更多的情况下,使用v-if替代v-show
-
key保证唯一
-
使用路由懒加载、异步组件
-
防抖、节流
-
第三方模块按需导入
-
长列表滚动到可视区域动态加载
-
图片懒加载
(2)SEO优化 -
预渲染
-
服务端渲染SSR
(3)打包优化 -
压缩代码
-
Tree Shaking/Scope Hoisting
Scope Hoisting 会分析出模块之间的依赖关系,尽可能的把打包出来的模块合并到一个函数中去。
Tree Shaking 可以实现删除项目中未被引用的代码 -
使用cdn加载第三方模块
-
多线程打包happypack
-
splitChunks抽离公共文件
-
sourceMap优化
(4)用户体验 -
骨架屏
-
PWA
-
还可以使用缓存(客户端缓存、服务端缓存)优化、服务端开启gzip压缩等。
9、watch和watchEffect的区别,watchEffect如何停止
1.都能监听响应式数据的变化,不同的是监听数据变化的方式不同
2.watch:要明确指出监视的数据,有惰性,不会立即执行;可访问新值和旧值;
3.watchEffect:不用明确指出监视的数据(函数中用到哪些属性,那就监视哪些属性);非惰性,一旦运行就会立即执行;
4.要手动停止一个侦听器,请调用 watch或watchEffect返回的函数:js
const unwatch = watchEffect(() => {})
// …当该侦听器不再需要时
unwatch()
10、数据驱动原理(vue2,vue3)
当一个Vue实例创建时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0使用proxy )将它们转为 getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实例都有相应的 watcher 程序实例,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的setter被调用时,会通知watcher重新计算,从而致使它关联的组件得以更新。
11、Vue-router 导航守卫有哪些 -
全局前置/钩子:beforeEach、beforeResolve、afterEach
-
路由独享的守卫:beforeEnter
-
组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
七、工程化
1、多入口打包 -
什么是多入口
webpack 支持多入口配置。入口就是开始打包的地方,多入口就是允许从多个文件开始打包。举个例子:一个项目中有 user 和 admin 两部分,我们希望将这两部分分别进行打包,就可以使用多入口生成不同的文件 -
多入口打包的作用
多入口打包可以合理控制包的大小,避免一次性加载太多不需要的代码。能够复用多个入口起点之间的大量代码/模块
主要在webpack.config文件里去配置,entry 配置多个打包入口
// webpack.config.js
module.exports = {
// 省略了其他相关配置…
// entry 配置多个打包入口
entry: {
admin: path.resolve(__dirname, “…/src/main.js”),
user: path.resolve(__dirname, “…/src/user.js”)
},
output: {
path: path.resolve(__dirname, ‘…/dist’), // 打包输出文件路径(__dirname指向当前文件的绝对路径
)
filename: ‘[name].[hash:8].js’ // 打包输出文件的名字, 插入hash值
},
plugins: [
// 主要是这里的配置,每个 new HtmlWebpackPlugin() 就根据html模板文件打包生成 html文件,这里我们有两个入口,也希望能分别打包生成不同的 html,就需要分别配置。
// 还有就是 chunks 指定我们要引入的 js 文件,名字可以理解为 entry 中配置的名字。例子中两个入口使用的是同一个 index.html 文件进行打包的,使用 chunks 指定要引入的 js文件就不会导致代码重复混乱。
new HtmlWebpackPlugin({
template: path.join(__dirname, ‘…/src/index.html’), // 指定模板文件
filename: ‘index.html’, // 生成文件名 - 这里生成index.html 便于 contentBase 中访问
chunks: [“user”], // 选择需要引入的js文件
minify: {
removeAttributeQuotes: true, // 删除双引号
collapseWhitespace: true // 折叠空行
} // 打包时的压缩配置
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, ‘…/src/index.html’), // 指定模板文件
filename: ‘admin.html’, // 生成文件名
chunks: [“admin”],
minify: {
removeAttributeQuotes: true, // 删除双引号
collapseWhitespace: true // 折叠空行
} // 打包时的压缩配置
}),
],
}
2、防抖和节流
前置,就是触发后立即执行
防抖-防抖后置
后置就是,触发事件后延迟n秒执行
触发高频事件后,延迟n秒执行一次,如果n秒内事件再次被触发,则重新计时
-
节流
- 高频事件触发,但在n秒内只会执行一次,节流会稀释函数的执行频率,每次触发事件时都判断当前是否有等待执行的延时函数
防抖是防止用户在短时间内多次点击请求服务器,减轻服务器压力
节流是属于限制用户在规定时间只能触发一次。从而达到节流,减轻服务器压力,优化性能
3、webpack中有哪些常见的loader
- file-loader:把⽂件输出到⼀个⽂件夹中,在代码中通过相对 URL 去引⽤输出的⽂件
- url-loader:和 file-loader 类似,但是能在⽂件很⼩的情况下以 base64 的⽅式把⽂件内容注⼊到代码中去
- source-map-loader:加载额外的 Source Map ⽂件,以⽅便断点调试
- image-loader:加载并且压缩图⽚⽂件
- babel-loader:把 ES6 转换成 ES5
- css-loader:加载 CSS,⽀持模块化、压缩、⽂件导⼊等特性
- style-loader:把 CSS 代码注⼊到 JavaScript 中,通过 DOM 操作去加载 CSS。
- eslint-loader:通过 ESLint 检查 JavaScript 代码
八、WebSocket 的11个面试知识点
1、WebSocket 协议和 HTTP 协议的区别是什么?
WebSocket 是一种实时双向通信协议,与 HTTP 协议相比,有以下几个主要区别: - 连接方式:WebSocket 提供持久的连接,通过握手过程建立连接后保持打开状态,而 HTTP 是无状态的,每次请求都需要重新建立连接。
- 数据格式:WebSocket 支持文本和二进制数据的传输,而 HTTP 主要是传输文本数据。
- 数据传输方式:WebSocket 实现了全双工通信,客户端和服务器可以同时发送和接收数据,而 HTTP 是单向的,客户端发起请求,服务器响应数据。
- 协议标识:WebSocket 使用 ws:// 或 wss:// 前缀标识,而 HTTP 使用 http:// 或 https://
2、WebSocket 的优势和适用场景是什么?
WebSocket 相对于传统的 HTTP 请求具有以下优势: - 实时性:WebSocket 提供了低延迟的实时通信能力,能够在服务器端有新数据时立即推送给客户端。
- 双向通信:WebSocket 支持客户端和服务器之间的双向通信,可以实现实时聊天、实时数据更新等场景。
- 较低的网络开销:WebSocket 使用长连接,相对于频繁的短连接请求,减少了网络开销。
- 更高的性能:由于减少了 HTTP 请求的开销,WebSocket 在性能上更高效。
- 跨域支持:WebSocket 具备跨域通信的能力,可以跨域进行实时通信。
WebSocket 的适用场景包括实时聊天应用、股票行情推送、实时协作编辑、多人游戏、实时数据监控等需要实时双向通信的场景。
3、WebSocket 的连接建立过程是怎样的?
WebSocket 的连接建立过程包括以下步骤:
1.客户端发送 WebSocket 握手请求,请求头包含 Upgrade 和 Connection 字段,指定协议升级和建立连接。
2.服务器收到握手请求后,验证请求头的字段,并返回握手响应,响应头包含 Upgrade 和 Connection 字段,以及一个随机的 Sec-WebSocket-Key 字段。
3.客户端收到握手响应后,验证响应头的字段,并生成一个 Sec-WebSocket-Accept 值进行验证。
4.验证通过后,WebSocket 连接建立成功,客户端和服务器可以开始进行实时通信。
4、WebSocket 的事件有哪些?请分别描述它们的作用。
WebSocket 提供了以下几种事件: - open:当 WebSocket 连接成功建立时触发的事件。可以在此事件中执行初始化操作或向服务器发送初始数据。
- message:当从服务器接收到新消息时触发的事件。可以在此事件中处理接收到的数据。
- error:当出现连接错误时触发的事件。错误可能包括连接失败、数据传输错误等。可以在此事件中处理错误并采取适当的措施。
- close:当 WebSocket 连接关闭时触发的事件。关闭可能是由服务器或客户端发起的,可以在此事件中执行清理操作或重新连接等操作。
这些事件可以通过设置对应的事件处理函数来处理不同的连接状态和数据传输。
5、在浏览器端如何创建和使用 WebSocket 对象?
在浏览器端,可以使用 JavaScript 中的 WebSocket 对象来创建和使用 WebSocket。示例代码如下:
const socket = new WebSocket(‘wss://example.com/socket’);
其中,new WebSocket() 通过传入服务器的 WebSocket URL 来创建一个 WebSocket 对象。然后可以通过设置事件处理函数来处理 WebSocket 的事件,例如:
socket.onopen = function(event) {
console.log(‘WebSocket 连接已打开’);
};
socket.onmessage = function(event) {
const message = event.data;
console.log(‘接收到消息:’, message);
};
socket.onerror = function(error) {
console.error(‘WebSocket 错误:’, error);
};
socket.onclose = function(event) {
console.log(‘WebSocket 连接已关闭’);
};
在连接建立成功后,可以使用 send() 方法发送消息到服务器,例如:
socket.send(‘Hello, server!’);
6、如何发送和接收消息?有哪些方法可以发送二进制数据?
通过 WebSocket 的 send() 方法可以向服务器发送消息,例如:
socket.send(‘Hello, server!’);
接收到的消息可以在 onmessage 事件处理函数中进行处理,例如:
socket.onmessage = function(event) {
const message = event.data;
console.log(‘接收到消息:’, message);
};
WebSocket 除了发送和接收文本消息外,还支持发送和接收二进制数据。对于发送二进制数据,可以使用 send() 方法传递一个 ArrayBuffer 或 Blob 对象,例如:
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setUint32(0, 1234);
socket.send(buffer);
在接收二进制数据时,可以通过 event.data 获取到 ArrayBuffer 对象,然后进行处理。
7、如何处理错误和关闭连接?
WebSocket 在出现错误时会触发 error 事件,可以通过设置 onerror 事件处理函数来处理错误,例如:
socket.onerror = function(error) {
console.error(‘WebSocket 错误:’, error);
};
当 WebSocket 连接关闭时,会触发 close 事件,可以通过设置 onclose 事件处理函数来执行一些清理操作或重新连接等操作,例如:
socket.onclose = function(event) {
console.log(‘WebSocket 连接已关闭’);
};
可以通过调用 close() 方法来显式地关闭 WebSocket 连接,例如:
socket.close();
8、WebSocket 的安全性和跨域问题如何处理?
WebSocket 支持通过 wss:// 前缀建立加密的安全连接,使用 TLS/SSL 加密通信,确保数据的安全性。在使用加密连接时,服务器需要配置相应的证书。
对于跨域问题,WebSocket 遵循同源策略,只能与同源的服务器建立连接。如果需要与不同域的服务器通信,可以使用 CORS(跨域资源共享)来进行跨域访问控制。
9、在实际应用中,如何处理连接状态的变化和重连机制?
在实际应用中,可以通过监听 open、error 和 close 事件来处理连接状态的变化。当连接关闭时,可以根据需要执行重连机制,例如使用指数退避算法进行重连,以确保连接的稳定性和可靠性。
10、WebSocket 的性能如何优化?有哪些注意事项和最佳实践?
为了优化 WebSocket 的性能,可以考虑以下几个方面:
- 减少数据量:合理控制发送的数据量大小,避免不必要的数据传输。
- 心跳机制:通过定时发送心跳消息,保持连接的活跃状态,防止连接被关闭。
- 数据压缩:可以使用压缩算法对数据进行压缩,减少网络传输的数据量。
- 服务器端优化:合理配置服务器端的连接数和资源管理,以支持更多的并发连接。
11、WebSocket 和长轮询相比,各自有什么优缺点?
WebSocket 和长轮询都可以实现实时通信,但它们具有不同的特点和适用场景。
WebSocket 的优点: - 实时性:WebSocket 建立一次连接后可以进行持久通信,实时性较高。
- 双向通信:WebSocket 支持客户端和服务器之间的双向通信。
- 较低的网络开销:WebSocket 使用长连接,减少了网络开销。
WebSocket 的缺点: - 兼容性:部分老旧的浏览器可能不支持 WebSocket,需要进行兼容处理。
- 服务器支持:服务器需要支持 WebSocket 协议和相关处理逻辑。
长轮询的优点: - 兼容性:长轮询可以在所有支持 HTTP 的浏览器中使用。
- 简单实现:相对于 WebSocket,长轮询的实现较为简单。
长轮询的缺点: - 延迟较高:由于需要不断发起轮询请求,延迟相对较高。
- 频繁的请求:长轮询需要频繁地发送请求,增加了服务器的负载。
根据具体需求和场景,选择合适的方案来实现实时通信。如果需要更高的实时性和较低的网络开销,WebSocket 是更好的选择。如果兼容性要求较高或者对实时性要求不高,可以考虑使用长轮询。
九、node.js
1、说说你对Node.js的理解?
Node.js 是一个开源与跨平台的 JavaScript 运行时环境
在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核),利用事件驱动、非阻塞和异步输入输出模型等技术提高性能
可以理解为 Node.js 就是一个服务器端的、非阻塞式I/O的、事件驱动的JavaScript运行环境
非阻塞异步
Nodejs采用了非阻塞型I/O机制,在做I/O操作的时候不会造成任何的阻塞,当完成之后,以时间的形式通知执行操作
例如在执行了访问数据库的代码之后,将立即转而执行其后面的代码,把数据库返回结果的处理代码放在回调函数中,从而提高了程序的执行效率
事件驱动
事件驱动就是当进来一个新的请求的时,请求将会被压入一个事件队列中,然后通过一个循环来检测队列中的事件状态变化,如果检测到有状态变化的事件,那么就执行该事件对应的处理代码,一般都是回调函数
比如读取一个文件,文件读取完毕后,就会触发对应的状态,然后通过对应的回调函数来进行处理
优点:
1、处理高并发场景性能更佳
2、适合I/O密集型应用,值的是应用在运行极限时,CPU占用率仍然比较低,大部分时间是在做 I/O硬盘内存读写操作
因为Nodejs是单线程,带来的缺点有:
1、不适合CPU密集型应用
2、只支持单核CPU,不能充分利用CPU
3、可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
1、Vue实现数据双向绑定的底层原理
双向绑定原理
vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发生变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点
2、前端路由的两种模式,分别有什么作用
hash模式
这里的 hash 就是指 url 尾巴后的 # 号以及后面的字符。这里的 # 和 css 里的 # 是一个意思。hash 也 称作 锚点,本身是用来做页面定位的,她可以使对应 id 的元素显示在可视区域内。
由于 hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件,浏览器的进后退也能对其进行控制,所以人们在 html5 的 history 出现前,基本都是使用 hash 来实现前端路由的。
history模式
已经有 hash 模式了,而且 hash 能兼容到IE8, history 只能兼容到 IE10,为什么还要搞个 history 呢?
首先,hash 本来是拿来做页面定位的,如果拿来做路由的话,原来的锚点功能就不能用了。其次,hash 的传参是基于 url 的,如果要传递复杂的数据,会有体积的限制,而 history 模式不仅可以在url里放参数,还可以将数据存放在一个特定的对象中。
最重要的一点:
如果不想要很丑的 hash,我们可以用路由的 history 模式—— 引用自 vueRouter 文档
history 模式改变 url 的方式会导致浏览器向服务器发送请求,这不是我们想看到的,我们需要在服务器端做处理:如果匹配不到任何静态资源,则应该始终返回同一个 html 页面。例如404
3、Vue中的数据代理
数据代理:通过一个对象代理对另一个对象中属性的操作(读/写)
1.Vue中的数据代理:
通过vm对象来代理data对象中属性的操作(读/写)
2.Vue中数据代理的好处:
更加方便的操作data中的数据
3.基本原理:
通过Object.defineProperty()把data对象中所有属性添加到vm上。
为每一个添加到vm上的属性,都指定一个getter/setter。
在getter/setter内部去操作(读/写)data中对应的属性。
4、什么是进程什么是线程
线程可以分为单线程和多线程,一个进程有多个单线程 ,一个程序的执行从开始到结束就是一个进程,一个进程中有多条线程,线程是进程中断断续续的开始和结束的过程
5、什么是防抖节流
前置,就是触发后立即执行
防抖-防抖后置
后置就是,触发事件后延迟n秒执行
: 触发高频事件后,延迟n秒执行一次,如果n秒内事件再次被触发,则重新计时
-
节流
-
高频事件触发,但在n秒内只会执行一次,节流会稀释函数的执行频率
,每次触发事件时都判断当前是否有等待执行的延时函数
防抖是防止用户在短时间内多次点击请求服务器,减轻服务器压力
节流是属于限制用户在规定时间只能触发一次。从而达到节流,减轻服务器压力,优化性能
6、UTP和TCP
UTP----用户数据报协议,是一个简单的面向数据报的运输层协议。特点:
1)面向无连接(UTP在传输数据报前不用在客户端和服务端之间建立连接);
2)每个数据包大小限制在64k内(UTP发送数据是将数据分解成多个包进行发送);
3)UTP不提供可靠性(因为是面向无连接);
4)传输速度快(因为不需要建立连接);
TCP----传输控制协议,提供是面向连接,可靠的字节流服务。特点:
1)当客户端与服务端进行交互数据前,必须先在彼此之间建立 一个TCP连接,之后再传输数据。
2)TCP提供可靠性,提供数据超时重发,筛选丢弃重复数据,检验数据,流量控制等功能。
7、什么是事件流
事件捕捉 目标事件 事件冒泡
8、宿主对象
本地对象为 array obj regexp 等可以 new 实例化
内置对象为 gload Math 等不可以实例化的
宿主为浏览器自带的 document,window 等
9、es6新增语法
let const 模块字符串`` 解构 展开运算符 箭头函数 函数的参数默认值 for of (in) class类
improt export default async/await Symbol Set map
10、简述 this 的 四种指向 问题
this指向的形式4种
a.如果是一般函数,this指向全局对象window;
b.在严格模式下"use strict",为undefined.
c.对象的方法里调用,this指向调用该方法的对象.
(call,apply,bind会改变this的指向)
.call(), call(thisScope, arg1, arg2, arg3…)
.apply(), apply(thisScope, [arg1, arg2, arg3…]);两个参数
Bind(this) 返回的是一个函数
d.构造函数里的this,指向创建出来的实例.
11.什么是优雅降级,什么是渐进增强?
渐进增强 progressive enhancement:针对低版本浏览器进行构建页面,保证最基本的
功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。
优雅降级 graceful degradation:一开始就构建完整的功能,然后再针对低版本浏览器进
行兼容。
12、什么是敏捷开发,什么是主动开发?
敏捷开发(Agile)是一种以人为核心、迭代、循序渐进的开发方法。
在敏捷开发中,软件项目的构建被切分成多个子项目,各个子项目的成果都经过测试,具备集成和可运行的特征。
简单地来说,敏捷开发并不追求前期完美的设计、完美编码,
而是力求在很短周期内开发出产品的核心功能,尽早发布出可用的版本。然后在后续的生产周期内,按照新需求不断迭代升级,完善产品。
13、http跟https 有什么不同?
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全
15、Ajax请求状态码
0:请求未初始化(还没有调用 open())。
1:请求已经建立,但是还没有发送(还没有调用 send())。
2:请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
3:请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成。
4:响应已完成;您可以获取并使用服务器的响应了。
16、http状态码 (常见)
100:请求等待
200:ok,页面正确打开,并得到完整的响应内容。
301:被请求的资源已永久移动到新位置
302:请求的资源临时从不同的 URI响应请求
304:缓存
400: 客户端请求的语法错误,服务器无法理解
404:页面不存在。
500:常指后端代码发生错误
503:由于临时的服务器维护或者过载,服务器当前无法处理请求。
17、什么是跨域
协议名、域名、端口,三项相同,称为同源。
三项中只要有一项不同,则被称为跨源(域)。
如何解决跨域
前端:jsonp,反向代理;后端:cors
jsonp-原理:引入资源不受同源策略的限制.动态创建script标签,src指向跨域文件,该跨域文件的响应内容,就会变成script标签的内容
反向代理-原理:当前端请求数据的时候,把前端地址代理成后端地址
cors-原理:允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
18、浏览器预检(OPTIONS)请求
在跨域的情况下,非简单请求会先发起一次空body的OPTIONS请求,称为"预检"请求,用于向服务器请求权限信息,等预检请求被成功响应后,才发起真正的http请求。
19、get和post请求的区别
1.get请求的数据会附加在url之后,以’?‘分割url和传输数据,多个参数用’&'连接。而post请求会把请求的数据放置在http请求包的包体中,因此get请求的数据会暴露在地址栏中,而post请求则不会。
2.post请求的安全性比get请求的高(比如,在进行登录操作,通过get请求,用户名和密码都会暴露再url上)。
3.get请求在url中传送的参数是有长度限制的,对于post,各个服务器会规定对post提交数据大小进行限制,但它不是url传值,所以它还是比get传输的数据多。
4.get请求只能进行url编码,而post支持多种编码方式。
20、单点登录
单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
21、变量提升
当 JavaScript 编译所有代码时,所有使用 var 的声明的变量,不管实际的声明是在哪里进行的,都会被提升到它们的函数/局部作用域的顶部(如果在函数内部声明的话),或者提升到它们的全局作用域的顶部(如果在函数外部声明的话)。
22、性能优化
1.优化图片资源的格式和大小
2.开启网络压缩
3.使用浏览器缓存
4.减少重定向请求
5.使用CDN存储静态资源
6.减少DNS(域名解析)查询次数
7.压缩css和js内容
8.图片懒加载
9.事件委托(大量减少内存占用,减少事件注册)
10:防抖、节流
23、深拷贝浅拷贝
拷贝,就是赋值。把一个变量赋给另外一个变量,就是把变量的内容进行拷贝。把一个对象的值赋给另外一个对象,就是把一个
对象拷贝一份。
浅拷贝就是将一个对象或数组的地址拷贝给另一个对象或数组,并没有把数据拷贝给另一个对象或数组。
深拷贝指针赋值,并且内容拷贝,开辟新的内存空间。
数组浅拷贝:如果是数组,可以使用数组的一些方法实现:slice(), concat()返回一个新数组的特性实现拷贝。
用扩展运算符spread实现数组深拷贝: JSON.parse(JSON.stringify())不仅适用于数组还适用于对象。不能拷贝函数,undefined, symbol。
25、什么是深复制什么是浅复制
浅复制只复制对象第一层,深复制遍历复制数组每一层.
26、深浅监听
深度监听:监听对象数组所有层级的改变(类似于购物车)
浅度监听:监听对象数组第一层的变化
27、prototype和_proto_的区别
任何对象实例都有一个原型,也叫原型对象,这个原型对象由对象的内置属 性_proto_指向它的构造函数的 prototype 指向的对象,即任何对象都是由一个 构造函数创建的,但是不是每一个对象都有 prototype,只有方法才有 prototype。
28、原型链
原型链的核心就是依赖对象的_proto_的指向,当自身不存在的属性时,就 一层层的扒出创建对象的构造函数,直至到 Object 时,就没有_proto_指向了。 因为_proto_实质找的是 prototype,所以我们只要找这个链条上的构造函数的 prototype。其中 Object.prototype 是没有_proto_属性的,它==null。
29、继承
1.构造函数继承:使用call或apply方法,将父对象的构造函数绑定在子对象上
缺点:只继承了父类的构造器,原型对象上(prototype)的属性和方法没有继承
2.原型继承:只会继承父类原型对象上的属性和方法,不能继承实例的属性和方法
3.组合继承:即原型继承+call()或者apply()继承。
4.es6的 class extends
30、重载和重绘
重载就是通过参数类型不同或参数个数不同可以让方法实现的功能不同,需要注意的是JS中其实没有真正意义上的重载,但是可以用typeof或argument来进行模拟,从而体现出JS面向对象思想中的重载特性。
回流:当 render tree 中的一部分或者全部因为元素的规模尺寸,布局,隐藏等 改变而需要重新构建,这就叫回流,每个页面至少需要一次回流,就是在页面第 一次加载的时候,这时候一定会发生回流,因为要构建 render tree 在回流的时候,浏览器会使渲染树中收到影响的部分失效,并重新构造这部分渲 染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,这就是重绘 当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观, 不会影响布局,就叫重绘
31、jsonp的原理
Jsonp 其实就是一个跨域解决方案。
Js 跨域请求数据是不可以的,但是js跨域请求js脚本是可以的.
所以可以把要请求的数据封装成一个 js 语句,做一个方法的调用.
跨域请求 js 脚本可以得到此脚本.得到 js 脚本之后会立即执行.
可以把数据做为参数传递到方法中.就可以获得数据。从而解决跨域问题.
jsonp 原理:(动态创建script标签,回调函数)
浏览器在 js 请求中,是允许通过 script 标签的 src 跨域请求,可以在请求的结
果中添加回调方法名,在请求页面中定义方法,就可获取到跨域请求的数据.
32、promise用法
一、什么是Promise?我们用Promise来解决什么问题?
Promise 是一种解决异步编程的方案,相比回调函数和事件更合理和更强大.
从语法上讲,promise 是一个对象,从它可以获取异步操作的消息;
二、promise 有三种状态:pending 初始状态也叫等待状态,fulfilled 成功状态,
rejected 失败状态;状态一旦改变,就不会再变.创造 promise 实例后,它会
立即执行
三、Promise的两个特点
1、Promise对象的状态不受外界影响
2、Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆
33、promise.all和promise.race的区别
Promise.all()会在多个promise全部resolve之后执行.then,而Promise.race()会在任何一个promise resolve后就会执行.then,两者都是promise并行执行代码。
一、Promise.all可以将多个Promise实例包装成一个新的Promise实例.
用于将多个Promise实例,包装成一个新的Promise实例。
1.它接受一个数组作为参数.
2.数组可以是Promise对象,也可以是其它值,只有Promise会等待状态改变.
3.当所有的子Promise都完成,该Promise完成,返回值是全部值的数组.
4.如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果.
二、Pomise.race的使用
类似于Promise.all(),区别在于它有任意一个返回成功后,就算完成,但是进程不会立即停止.
常见使用场景:把异步操作和定时器放到一起,如果定时器先触发,认为超时,告知用户.
34、promise底层原理
promise可以有三种状态,分别是pedding,Fulfilled,Rejected,
Pending Promise对象实例创建时候的初始状态
Fulfilled 可以理解为成功的状态
Rejected可以理解为失败的状态
1.构造一个Promise实例需要给Promise构造函数传入一个函数.传入的函数需要有两个形参,
两个形参都是function类型的参数.分别是resolve和reject.
2.Promise上还有then方法,then 方法就是用来指定Promise对象的状态改变时确定执行的操作,resolve 时执行第一个函数(onFulfilled),reject时执行第二个函数(onRejected)
3.当状态变为resolve时便不能再变为reject,反之同理.
35、cookie和session
Cookie 一般由服务器生成,可设置失效时间。如果在浏览器端生成 Cookie,默认是关闭浏览器后 失效,4K 左右
session 存储在服务器,不依赖浏览器环境 高效 安全
36、sessionStorage和localStorage
sessionStorage仅在当前会话下有效,关闭页面或浏览器后被清除
localStorage 除非被清除,否则永久保存,一般为 5MB
37、http1.0和http2.0
http1:线程阻塞,在同一时间,同一域名的请求有一定数量限制,超过限制数目的请求会被阻塞
http2:采用二进制格式而非文本格式;完全多路复用,而非有序并阻塞的、只需一个连接即可实现并行
38、new的时候干了什么
(1)创建一个新对象;
(2)将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
(3)执行构造函数中的代码(为这个新对象添加属性)
39、什么是可变对象什么是不可不变对象
可变对象:引用数据类型(对象数组),只执行浅拷贝
不可变对象:基本数据类型(字符串 number 布尔值)
40、什么是变异数组方法,什么是非变异数组方法
变异数组方法,例如 push、pop 等,会改变原数组,从而更新视图;// push()pop()shift()unshift()splice()sort()reverse()join()
非变异数组方法,例如 filter,map 等,不会改变原数组,会返回一个新的数组
41、数组去重的方法
利用ES6 Set去重(ES6中最常用)
利用for嵌套for,然后splice去重(ES5中最常用)
利用indexOf去重
利用includes
利用filter
42、数组排序的方法
1.冒泡排序法:将数组中的相邻两个元素进行比较,将比较大(较小)的数通过两两比较移动到数组末尾(开始),执行一遍内层循环,确定一个最大(最小)的数,外层循环从数组末尾(开始)遍历到开始(末尾).
2.选择排序法:将要排序的数组分成两部分,一部分是从大到小已经排好序的,一部分是无序的,从无序的部分取出最小的放到已经排序的最后面。
3.插入排序法:将要排序的数组分成两部分,每次从后面的部分取出索引最小的元素插入到前一部分的适当位置
4.快速排序法:快速排序法号称是目前最优秀的算法之一,实现思路是,将一个数组的排序问题看成是两个小数组的排序问题,而每个小的数组又可以继续看成更小的两个数组,一直递归下去,直到数组长度大小最大为2。
43、var let const的区别
var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
var 声明的变量存在变量提升,let 和 const 不存在变量提升
同一作用域下 var 可以被重复声明,let或const声明的变量不能被重复声明
let 和 const 声明会形成块级作用域
let和const声明变量存在暂存死区
Const 一旦声明必须赋值,不能用 null 占位,声明后不能再修改,如果声明的
是复合类型数据,可以修改属性
44、 如何区分 宿主对象 内置对象 本地对象 分别有哪些?
宿主对象 window document 简单的说就是官方未定义的都是宿主对象
内置对象 不可以实例化的对象 例如: Global Math
本地对象 可以实例化的对象 例如: Object Array Function Number RegExp Date …
45、作用域有哪几种,分别有什么作用(js作用域)
作用域:变量可以生效的范围
全局作用域:一个 html 页面就是一个全局作用域,打开页面的时候, 作用域就
生成了, 直到关闭页面为止=> 自当前 script 标签开始的任何位置
局部作用域:每一个函数就是一个私有作用域。
ECMAScript 6(简称 ES6)中新增了块级作用域。
块级作用域:块作用域由 { } 包括,if 语句和 for 语句里面的{ }也属于块作
用域。
作用域链:多个作用域形成的链式结构
46、call,apply,bind
call、apply和bind都是用于改变方法中this指向的。
call调用 将方法中的this指向call中第一个参数,当第一个参数为null、undefined时,
默认指向window; call中第一个参数之后是要传递给方法的参数列表。
apply与call相似,不同之处在于传递给方法的参数形式不一致。apply传递给方法的参数是数组的形式。
call和apply在改变方法的this指向时,会同时执行方法;而bind不会执行方法,而是返回改变this指向后的新方法。