面试题

130 阅读26分钟

1.闭包/作用域

什么是闭包

在 js 中,函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。有时候需要获取到函数内部的局部变量 闭包就是能够读取其他函数内部变量的函数

使用闭包的注意点

  • (1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
  • (2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值

作用域

   全局作用域:在代码中任何地方都能访问到的对象拥有全局作用域
   局部作用域:局部作用域一般只在固定的代码片段内可访问到,最常见的例如函数内部

块级作用域: ES6 之前 JavaScript 没有块级作用域,只有全局作用域和函数作用域。ES6 的到来,为我们提供了‘块级作用域’,可通过新增命令 let 和 const 来体现。 块级作用域所声明的变量在指定块的作用域外无法被访问。块级作用域在如下情况被创建: 在一个函数内部,在一个代码块(由一对花括号包裹)内部

2.优化函数执行的频率(防抖、节流)

vue 的 ssr 服务端渲染

服务端渲染:默认情况下,Vue 组件的职责是在浏览器中生成和操作 DOM,vue 也支持服务端渲染 Vue 也支持将组件在服务端直接渲染成 HTML 字符串,作为服务端响应返回给浏览器,最后在浏览器端将静态的 HTML“激活”(hydrate) 为能够交互的客户端应用

3.类型转化

typeof 对于原始类型来说,除了 null 都可以显示正确的类型

typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型

如果我们想判断一个对象的正确类型,这时候可以考虑使用 instanceof,因为内部机制是通过原型链来判断的,在后面的章节中我们也会自己去实现一个 instanceof

4.手写原生的ajax及里面的一些考点。

axios用法要熟悉,最好能知道它的请求拦截和响应拦截的原理。

5.浏览器存储方式(cookie/localStorage/sessionStorage)

cookie:

  1. 每个 cookie 存放的内容大小不同, 一般为 4kb
  2. cookie 默认会话结束自动销毁, 可以设置过期时间
  3. cookie的生命周期当浏览器关闭的时候就消亡了

LocalStorage:

  1. 持久化的本地存储, 需要手动删除数据, 否则不会过期

sessionstorage:

类似于 localstorage, 用于临时保存同一窗口(或标签页)的数据,与 localstorage 不同的是, sessionstorage 会在关闭窗口或标签页之后将会删除这些数据

6.输入url到整个页面渲染出来经历的一系列过程要知道(老生常谈)。

1,输入地址 2、浏览器查找域名的 IP 地址 3、浏览器向 web 服务器发送一个 HTTP 请求 4、服务器的永久重定向响应 6、服务器处理请求 7、服务器返回一个 HTTP 响应 8、浏览器显示 HTML 9、浏览器发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等

1.说一下你常用的es6新增的一些api

1)变量申明: let定义变量,const定义常量。和之前ES5 var声明变量的不同:

​ var会有变量提升的效果(函数声明整体提升,变量声明提升)

​ ex:(一般会有编程题让你让你打印变量的执行顺序)

​ let: 1.不存在变量提升 2.块级作用域/相同作用域内,不允许重复声明同一个变量。先声明,在使用,并没有把变量定义在window上...

​ const:变量声明之后不可更改。但有一个例外: 数组可以添加删除元素/对象可以新增属性。引用类型不改变变量的引用地址就能修改

(这个地方会涉及到基本数据类型和引用数据类型,typeof 和instanceof。会让你手写instanceof实现原理。)

基本数据类型: number string boolean null undefined symbol(ES6新增)

引用数据类型: object

2)变量的解构赋值 let { name, age } = { name: '张三', age: 18 }

3) 字符串: 新增模板字符串

4)字符串新增常用方法:includes(), startsWith(), endsWith() repeat(),剩下的用的不多。

ES5常用的字符串方法: indexOf/substring/slice/substr/charAt/charCodeAt/split/

5)正则的扩展:

​ ES6新增的正则用的不是太多。

​ ES5常用的正则方法 : test/search/match/replace/exec

​ 掌握几个常用的正则表达式,面试可能会让手写

6)数值的扩展:

Number.isInteger()用来判断一个数值是否为整数。

​ ES5常用的方法: parseInt/parseFloat/Math.ceil/Math.floor/Math.random/Math.round/

  1. 函数的扩展:

​ 函数参数默认值/...剩余参数/

​ 箭头函数和普通函数的区别:

​ 箭头函数有几个使用注意点。

​ (1)箭头函数没有自己的this对象。

​ (2)不可以当作构造函数,也就是说,不可以对箭头函数使用new命令,否则会抛出一个错误。

​ (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

​ (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

​ ES5普通函数的this是在调用的时候才会确定下来,所以会有矫正this指向的操作。常用的有call/apply/bind。

​ (面试会让手写bind函数的概率更大。bind执行完后会返回一个函数,须由调用方自行去调用)。

​ call/apply也要会手写。这个地方也会有编程题,让你去打印输出的到底是哪个对象里的变量

8)数组的扩展:

新增的api: ...扩展运算符/Array.from(接收类数组)/Array.of()/find()/findIndex()/includes()/flat()/for...of循环/

ES5常用的数组操作: push/pop/shift/unshift/splice/sort/reverse/join/sort/reverse

ES5常用的数组循环操作: forEach/map/filter/reduce/every/some/(之前面试有让手写map和reduce的实现)

数组常用的排序算法: 快排、冒泡、归并、选择、插入排序

二次查找算法也要熟悉一下。

常见的深拷贝 和浅拷贝的方法 ***

常用api: Object.assign(浅拷贝)/Object.is()/Object.keys/Object.values/Object.entries

会有深浅拷贝的面试题。让你手写深拷贝(可用递归的方式去实现)

es5对象常用的方法: for..in循环/JSON.stringify/JSON.parse/

10.Symbol这个可以熟悉一下(基本类型)可以构造一个独一无二的属性值

11.Set 和 Map 数据结构(掌握一下实例上面的方法及遍历map/set的方法)

​ set可以实现数组去重 [...new Set(arr)]/Array.from(new Set(arr))

这个地方会考数组去重的实现方式。

let set = new Set() // set实例上的方法: add / has / delete / clear /

Map: const map = new Map(); // map实例上的方法: set(key, value) / get(key) / delete(key) / clear / has(key)

weakMap

新增的Map/Set非常有用。vue3响应式依赖收集就是用它们来实现的。

12.proxy(vue3响应式实现的关键所在),可以支持13种拦截操作。***

​ Let proxy = new Proxy(target, {

​ get(target, key, recevier) {}, // 拦截对象属性的读取

​ set(target, key, value, recevier) {}, // 拦截对象属性的设置

​ has(target, key),

​ deleteProperty(target, key)

​ ....

​ })

和Object.defineProperty的区别;

Proxy 可以直接监听对象而非属性; - Proxy 可以直接监听数组的变化; - Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的; - Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改; - Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;

这也是vue2.0和vue3.0实现响应式的核心了。

13.Reflect(和proxy相辅相成)。也要熟悉一下


异步编程的解决方案

1)初期的回调函数(回调地狱)

2)Promise(重中之重)

最好是熟悉promise内部的原理,熟悉常用的方法的实现。

Promise.all/Promise.race/Promise.resolve/Promise.reject。面试手写过Promise类及promise.all方法

promise.then/promise.catch/promise.finally

3)async/await:异步编程的终极解决方案。

需要知道一下promise和async的区别,为什么有了promise还要再出现async。

4)EventLoop事件循环(宏任务和微任务)。

考题如下:关于async/await , promise 异步执行顺序(常考)

5)扩展: 浏览器事件循环和node的事件循环的区别


15.class类

​ ES6新增的继承方式

​ ES5里面的原型和原型链。继承方式(比较推荐的原型链+构造函数的组合继承方式)

​ new运算符背后的实现原理

​ Instanceof运算符背后的实现原理

16.模块化

​ Import/export。

​ 会问你之前的模块化的方案(seajs/requirejs可以了解一下)

​ ESModule和commonjs(node)之前的异同点。


(三)http协议(常考)。

1.http常见的状态码,常用的请求头,响应头,请求报文,响应报文...

2.http的发展历史: http1.0、http1.1、http2.0、http3.0的一些特性。

3.缓存(强缓存、协商缓存)

4.http三次握手和四次挥手要熟悉。

简述一下同源策略

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

简述跨域 以及跨域的解决方案

跨域: 通过jsonp跨域

通常为了减轻web服务器的负载,我们把js、css,img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。(运用了 script 中的 src 属性)

nginx反向代理接口跨域

跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。

跨域资源共享 CORS

CORS是一个W3C标准 全称是"跨域资源共享" 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

每次发送 ajax 请求的时候, 会默认在请求头中配置一个字段, 服务器接收到请求的字段 , 服务器返回的响应


vue 双向数据绑定的原理

双向数据绑定:数据改变触发页面视图改变

Vue 数据双向绑定原理是通过 数据劫持 + 发布者-订阅者模式 的方式来实现的,首先是通过 ES5 提供的 Object.defineProperty() 方法来劫持(监听)各属性的 getter、setter,并在当监听的属性发生变动时通知订阅者,是否需要更新,若更新就会执行对应的更新函数

vuex 的属性

state, getter , Mutation, Action, Module

state 存储数据, 存储的数据是响应式的, Vue 组件从state 中读取数据, store 中的数据发生改变时, 依赖这个数据的组件也会发生更新

getter 方法可以对 state 进行计算操作, state 的计算属性

getter 中的计算属性可以在多个组件之间复用

Mutation 方法用来修改 state 中的数据的, 在组件中 通过this. $commit 的方式把数据传递过去

Action 方法 里类似与 mutation 方法, Action 方法改变的是 mutation 里面, 不能直接改变状态 Action 可以操作异步的状态

vuex 的优点

1, 可维护性好, 代码统一, 方便数据的管理 ( 如果是传值的方式)

2, 可读性好, 数据放在 vuex 中, 方便维护

vue 中传值的方法

vue 项目中遇到的问题

1, 路由变化数据不刷新的问题

一般情况出现在 vue- router 的 history 模式下, 初次进入会执行钩子函数, 再次进入则不会

解决方案: 监听路由变化vue 的生命周期

2, mounted 钩子函数中请求数据页面闪屏问题

就是一个加载时机的问题, 把请求数据放在 created 里面, 在页面挂载完成之后就看不到闪屏

3, element ui 中 el-table 使用 v-if 做一些条件渲染的时候 显示的数据 与 预期不符 的问题

给做条件渲染的数据动态绑定 一个 key 属性, 做一个标志符

4, 使用 setInterval 路由跳转 的时候继续运行 的问题 比如一些组件使用 组件已经销毁了, 定时器还在继续运行

解决方案: 在组件的生命周期之中吧 setinterval 停止

什么是跨域? 跨域的解决方案 ?

跨域是指 同域名, 同协议, 同端口号, 跨域是指 域名, 协议, 端口号有一个不同就叫跨域

跨域的解决方案?

1, 通过服务器代理的方式, 通过服务器向服务器发送请求 , 绕开同源策略

2, 通过 jsonp 跨域

通过 script 中的 src 属性进行请求资源, 请求回来的数据, 通过 回调函数的形式, 接收请求回来的数据 ( 只能实现 get 请求)

3, CORS (跨域资源共享)

CORS 是 w3c 的一个标准, 它允许向 服务器发送 跨域请求, 前端需要在 请求头中配置 跨域字段 , 后端配置跨域方法

计算属性 computed :

1, computed 是有缓存的, 只要依赖的数据发生改变, 就会进行重新计算

2, 不支持异步, 当 computed 中有异步操作时, 无法监听数据的变化

3, 如果一个属性是由其他属性计算而来的, 这个属性依赖于其他属性, 是一个 多对一 或者 一对一 , 就用 computed

监听器 watch

1, 不支持缓存, 数据发生改变, 直接触发相应的操作

2, watch 可以监听异步的操作

3, 监听的函数可以接收两个参数, 第一个参数是最新的值, 第二个参数是输入之前的值

4, 当一个属性发生变化时, 需要执行对应的操作, 一对多

5, watch 监听数据必须是 data 中声明的数据 获取 在父组件中传递过来的 props 中的数据, 当数据反生改变是, 触发相应的操作

6, deep 可以实现深度监听, 可以实现深度监听对象内的数据的变化 ( deep 无法监听数组的变化 )

vue 中的单项数据流

项目中的代码的优化

1, 使用组件库的时候 进行按需导入

2, 使用路由懒加载的方式

只有当用户点击或者跳转路由时才去加载, 提升首页的加载速度

3, 使用 keep-alive 去包裹动态组件 通过 include 和 exclude 属性 决定组件中的 数据是否渲染

4, 使用图片懒加载的方式

图片 加载是非常的消耗性能的, 通过第三方 vue-lazyload 插件 实现图片的懒加载, 只有滚动到当前视图的时候才会去加载图片

5, v-for 在做一些循环的时候, 向 循环的数据中绑定一个 key 值, 做一个标志, 方便更新数据的时候, 去查找这个数据, 尽量使用 id 做这个 key 值, 因为 id 值是唯一 的, 如果使用 index 作为这个 key 值的话, 如果要修改数据中的中的一条数据的话, index 下标会进行重新计算

vue 的生命周期

v-if 和 v-show 的区别

组件传值的方法

localStorage 和 sessionStorage 和 cookie 的区别

es6 的新语法

vuex 的状态管理

1, vuex 的属性

state, getter , Mutation, Action, Module

state 存储数据, 存储的数据是响应式的, Vue 组件从state 中读取数据, store 中的数据发生改变时, 依赖这个数据的组件也会发生更新

getter 方法可以对 state 进行计算操作, state 的计算属性

getter 中的计算属性可以在多个组件之间复用

Mutation 方法用来修改 state 中的数据的, 在组件中 通过this. $commit 的方式把数据传递过去

Action 方法 里类似与 mutation 方法, Action 方法改变的是 mutation 里面, 不能直接改变状态 Action 可以操作异步的状态

vuex 的优点

1, 可维护性好, 代码统一, 方便数据的管理 ( 如果是传值的方式)

2, 可读性好, 数据放在 vuex 中, 方便维护

vue 中传值的方法

对执行上下文的理解

执行上下文分为三类, 全局执行上下文, 函数上下文, 执行上下文栈

1, 全局执行上下文

​ 全局上下文只有一个, 就是 window 对象 , 可以通过 在全局作用域中通过 this 访问

2, 函数执行上下文

​ 函数执行上下文有无数个, 每当一个函数被调用时都会创建一个函数上下文

3, 执行上下文栈

​ 执行上下文栈,用于存储代码执行期间创建的所有上下文,

js 代码首次执行, 都会先创建一个全局执行上下文放入执行栈中, 之后每当有函数被调用, 都会创建一个新的函数执行上下文并放入栈内 ( js 代码执行完毕前在执行栈底 永远有个全局执行上下文)

this 指向问题

1, 全局 , this 指向全局对象 window

2, 函数内, this 默认 调用者

箭头函数 this 指向

1, 箭头函数没有 this 指向 , 在定义阶段已经确定了 this 指向 默认 this 绑定 执行的上下文

2, 箭头函数 没有调用者 默认指向 window ( 全局执行的上下文)

改变 this 指向

1, 通过 new 关键字 改变 this 指向

2, 通过 call 改变 this 指向

3, 通过 appay 改变 this 指向

4, 通过 bind 改变 this 指向

什么是跨域? 跨域的解决方案 ?

跨域是指 同域名, 同协议, 同端口号, 跨域是指 域名, 协议, 端口号有一个不同就叫跨域

跨域的解决方案?

1, 通过服务器代理的方式, 通过服务器向服务器发送请求 , 绕开同源策略

2, 通过 jsonp 跨域

通过 script 中的 src 属性进行请求资源, 请求回来的数据, 通过 回调函数的形式, 接收请求回来的数据 ( 只能实现 get 请求)

3, CORS (跨域资源共享)

CORS 是 w3c 的一个标准, 它允许向 服务器发送 跨域请求, 前端需要在 请求头中配置 跨域字段 , 后端配置跨域方法

vue 中 keep-alive 的作用

keep-alive 是 vue 中的一个 内置组件, 能够 使 被包含的组件保留状态, 避免被重新渲染, 使用 keep-alive 时, mouted, created 等钩子函数,只会在第一个进入组件是调用( 可以做一些页面的优化)

include: 字符串或正则表达式。只有匹配的组件会被缓存。

exclude: 字符串或正则表达式。任何匹配的组件都不会被缓存。

computed 和 watch 的区别

计算属性 computed :

1, computed 是有缓存的, 只要依赖的数据发生改变, 就会进行重新计算

2, 不支持异步, 当 computed 中有异步操作时, 无法监听数据的变化

3, 如果一个属性是由其他属性计算而来的, 这个属性依赖于其他属性, 是一个 多对一 或者 一对一 , 就用 computed

监听器 watch

1, 不支持缓存, 数据发生改变, 直接触发相应的操作

2, watch 可以监听异步的操作

3, 监听的函数可以接收两个参数, 第一个参数是最新的值, 第二个参数是输入之前的值

4, 当一个属性发生变化时, 需要执行对应的操作, 一对多

5, watch 监听数据必须是 data 中声明的数据 获取 在父组件中传递过来的 props 中的数据, 当数据反生改变是, 触发相应的操作

6, deep 可以实现深度监听, 可以实现深度监听对象内的数据的变化 ( deep 无法监听数组的变化 )

vue 中的单项数据流

单项数据流就是只能从一个地方来修改状态, vue 就是单项数据流的, 比如 组件之间的传值, 只能单项绑定, 子组件不能直接修改父组件传递过来的数据

vue中 hash 和 history 的区别

hash 带有一个 #号 支持性好 哈希值发生变化时, 无需发送 http 请求, window 就可以监听

history 用的是传统的路由分发模式, 当用户输入一个 url 地址时, 是由服务器在接受用户的输入请求时, 就会去做一些相应的跳转, 如果想要不刷新页面, 就需要用到h5 的两个api (pushState 和 replaceState ) 去替换 url 的同时不去刷新页面, 需要后端去配置url 的重定向, 可以支持浏览器的回退, 但会有一个问题, 就是在刷新时会造成页面丢失的情况

深拷贝

function deepConle( obj = {} ) {

​ // 判断传递过来的数据 是否为 对象 不为对象, 返回

​ if ( typeof obj !== 'Object' || typeof obj == null ) {

​ return obj

​ }

​ let result

​ // 判断传递过来的数据是否为数组

​ if ( obj instanceof Array ) {

​ result = []

​ } else {

​ result = {}

​ }

​ for ( key in obj ) {

​ if ( obj.hasOwnProperty(key) ) {

​ result[key] = deepConle(obj[key])

​ }

Flex布局

主轴对其方式

justify-content : flex-start | flex-end | center | space-between | space-around;

侧轴对其方式

align-items:flex-start | flex-end | center | baseline | stretch;

stretch(默认值):如果子元素未设置高度或设为auto,将占满整个容器。

改变主轴对其方式

flex-direction:row | row-reverse | column | column-reverse;

row : 默认值: 1,2,3

row-reverse : 默认: 3, 2, 1

column:从上到下 1, 2, 3

column-reverse:从上到下/3/2/1

flex-wrap: 换行方式

flex-wrap:nowrap(不换行) | wrap(向下换) | wrap-reverse(向上换);

align-content:flex-start | flex-end | center | space-between | space-around | stretch;

flex-start:上对齐。

flex-end:下对齐。

center:居中对齐。

space-between:两端对齐,间隔平均。

space-around:间隔相等。

stretch(默认值):占满。

2)Flex布局子元素属性

order/flex-grow/flex-shrink/flex-basis/flex/align-self

1》order属性(num)

order定义自身排列顺序。数值越小,越靠前,默认为0。-1/0/1/2/3/...

2》flex-grow属性(num)

flex-grow 定义自身放大比例,默认为0不放大。例如:1/2/1=25%:50%:25%

3》flex-shrink属性(num)

flex-shrink定义了空间不足时自身缩小比例,默认为1自动缩小,0不缩小。

4》flex-basis属性

flex-basis定义最小空间,默认值为auto,即自身的本来大小。

5》flex属性

flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

6》align-self属性

align-self定义自身对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。

css 中可继承的元素

font-size, line-height, color, font-family, font-weight, list-style,

html 中的块元素以及行内元素

,
, ,

,
    ,
  • , 行内元素 :
    , , , #### 开发过程中导致内存泄漏的问题

    1, 全局变量声明过多

    2, 定时器没有关闭

    3, dom 元素没有清理引用

    vue 中 nextTick 的作用

    因为 vue 中的渲染机制是异步渲染的, 所以去操作一些数据的时候只能拿到上一次未渲染之前的数据 ( 比如 : 有一个点击事件, 实现点击数量加一, 在普通事件函数中, 只能拿到 DOM 渲染之前的值, 无法拿到数据改变之后的值, 视图数据跟拿到数据不一致 ) vue 中提供了一个 this.nextTick的方法,this.nextTick 的方法, this.nextTick 相当于一个回调函数, 在异步渲染之后会调用, 通过 this.$nextTick 可以实现在dom 更新之后做一些操作

    axios

    1, axios 是一个 基于 promise 的 HTTP 库, 支持所有 promise API

    2, 可以设置请求拦截和响应拦截 ( 比如: 请求拦截 登录请求头设置 token 字段 ; 响应拦截 在服务器返回登录状态失效, 需要重新登录的时候, 跳转回登录页面 )

    3, 可以把 响应回来的数据 自动装换为 JSON 类型的数据

    说下你了解的axios相关配置属性?

    url是用于请求的服务器URL

    method是创建请求时使用的方法,默认是get

    baseURL将自动加在url前面,除非url是一个绝对URL。它可以通过设置一个baseURL便于为axios实例的方法传递相对URL

    transformRequest允许在向服务器发送前,修改请求数据,只能用在'PUT','POST'和'PATCH'这几个请求方法

    headers是即将被发送的自定义请求头 headers:{'X-Requested-With':'XMLHttpRequest'},

    params是即将与请求一起发送的URL参数,必须是一个无格式对象(plainobject)或URLSearchParams对象 params:{ ID:12345 },

    auth表示应该使用HTTP基础验证,并提供凭据 这将设置一个Authorization头,覆写掉现有的任意使用headers设置的自定义Authorization

    vue中常用的修饰符?

    事件修饰符:

    .once只触发一次当前元素的事件

    .stop:阻止事件冒泡

    .self:只有元素本身触发时候才会执行事件函数

    .capture:在事件冒泡中优先执行当前元素上绑定的事件函数

    .prevent:阻止浏览器默认行为

    .passive:忽略preventDefault()函数,不能和prevent修饰符同时使用

    按键修饰符:

    .enter、.tab、.delete、.esc、.up、.down、.right、.left

    系统修饰键:

    .ctrl、.alt、.shift、.meta

    js里==和===有什么区别

    ==用于一般比较,===用于严格比较,==在比较的时候可以转换数据类型,===严格比较,只要类型不匹配就返回flase。

    比如: "1" == true类型不同,"=="将先做类型转换,把true转换为1,即为 "1" == 1;此时,类型仍不同,继续进行类型转换,把"1"转换为1,即为 1 == 1。

    HTTP 缓存机制 ( 强制缓存,协商缓存 )

    强制缓存是HTTP 的一种缓存机制 就是 服务器 认为一些需要缓存到本地的数据, 比如 html, css, 等, 会在请求头中默认配置一个请求字段, 会设置他的过期时间, 下次请求的时候, 缓存下来的数据会直接向本地发送请求, 避免了请求响应速度慢的问题

    协商缓存就是 通过浏览器端设置的一些需要缓存的数据, 这样每次请求的时候会发送请求, 在去对比本地缓存的数据, 如果本地缓存中的数据没有变化, 就会默认使用本地缓存的数据, 同时在响应中返回 304

    总结: 缓存对于前端优化是一个很重要的点, 通过强制缓存和协商缓存,提高页面的加载速度

    computed 和 watch 的区别

     支持缓存,只有依赖数据发生改变,才会重新进行计算
    2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
    3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
    4. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
    5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
    
    侦听属性watch:
    
    1. 不支持缓存,数据变,直接会触发相应的操作;
    2.watch支持异步;
    3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
    4. 当一个属性发生变化时,需要执行对应的操作;一对多;
    5. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
    immediate:组件加载立即触发回调函数执行,
    deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。
    注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
    
    watch工作原理:
    watch在一开始初始化的时候,会读取一遍监听的数据的值,此时那个数据就收集到watch的watcher了然后你给watch设置的handler,watch 会放入watcher的更新函数中,当数据改变时,通知watch的watcher进行更新,于是你设置的handler就被调用了。
    

    js 事件循环机制

    在 js 中 任务分为 同步任务 和 异步任务

    虽然 JS 是单线程的,但是浏览器的内核却是多线程的,在浏览器的内核中不同的异步操作由不同的浏览器内核模块调度执行,异步任务操作会将相关回调添加到任务队列中

    首先判断 js 代码是同步还是异步不停的检查调用栈中是否有任务需要执行,如果没有,就检查任务队列,从中弹出一个任务,放入栈中,如此往复循环,要是同步就进入主进程,异步就进入事件表

    异步任务在事件表中注册函数,当满足触发条件后,被推入事件队列同步任务进入主线程后一直执行,直到主线程空闲时,才会去事件队列中查看是否有可执行的异步任务,如果有就推入主进程中

    数组扁平化方法

    1, 通过 数组方法

    const arr = [1, 2, [2, 3, [3, 5 , [3 , 4]] , [1, 2, 3]]] function flatten(arr) { return arr.join(',').split(',').map(function (item) { return parseInt(item); }) }

    2, 通过递归方法

    function flatten(arr) { var res = []; arr.map(item => { if(Array.isArray(item)) { res = res.concat(flatten(item)); } else { res.push(item); } }); return res; }

    3, 通过 es6 ...语法

    function flatten(arr) { while(arr.some(item=>Array.isArray(item))) { arr = [].concat(...arr); } return arr; }

    单行文本溢出显示三个小圆点

    { overflow: hidden;

    text-overflow:ellipsis;

    white-space: nowrap;

    }

    vue 3 ref 和 reactive 的区别

    相同点:ref 和 reactive 都是用来定义响应式数据的
    不同:reactive:
    (1)它的响应式是更深层次的,底层是将传入的数据包装成一个proxy;
    (2)推荐去定义复杂的数据类型; 参数必须是对象或者数组,如果想要使用reactive让某个数值变成响应式,要在外层包裹一个对象{}; 如果要让对象的某个元素实现响应式时比较麻烦。需要使用toRefs
    ref:
    (1)函数参数可以是基本数据类型,也可以接受对象类型;
    (2)如果参数是对象类型时,其实底层的本质还是reactive,系统会自动根据我们给ref传入的值转换;
    (3)在template中访问,系统会自动添加.value;在js中需要手动.value

    promise

    1, promise 的状态

    promise 有三种状态 pedding ( 正在进行 ) resolved( 成功 会执行.then 的方法里面的回调 ), reject ( 失败, 会默认执行.catch 里面的方法 ) 执行一个回调函数, 可以进行传参, 只要状态改变, 后面的就不会执行

    一、Pomise.all的使用

    常见使用场景 : 多个异步结果合并到一起

    Promise.all可以将多个Promise实例包装成一个新的Promise实例。用于将多个Promise实例,包装成一个新的Promise实例。

    1.它接受一个数组作为参数。

    2.数组可以是Promise对象,也可以是其它值,只有Promise会等待状态改变。

    3.当所有的子Promise都完成,该Promise完成,返回值是全部值的数组。

    4.如果有任何一个失败,该Promise失败,返回值是第一个失败的子Promise的结果。

    Promise.race的使用

    顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

    类似于Promise.all() ,区别在于 它有任意一个返回成功后,就算完成,但是 进程不会立即停止

    常见使用场景:把异步操作和定时器放到一起,如果定时器先触发,认为超时,告知用户

    css 左侧定宽右侧自适应

    1, 左侧 浮动, 右侧盒子 width :100%

    2, 父盒子 display : flex , 左侧定宽, 右侧 flex : 1

    3, 设置浮动, 两个子盒字浮动 , 使用 call 函数 减去左侧 定宽 px

    重绘和回流

    重绘:当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要 UI 层面的重新像素绘制,因此损耗较少。

    回流: 当元素的尺寸、结构或者触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。

    常见的回流操作有: 页面初次渲染 浏览器窗口大小改变 元素尺寸/位置/内容发生改变 元素字体大小变化 添加或者删除可见的 DOM 元素 激活 CSS 伪类(:hover……)回流必定会触发重绘,重绘不一定会触发回流。重绘的开销较小,回流的代价较高。

    在工作中我们要如何避免大量使用重绘与回流呢?

    避免频繁操作样式,可汇总后统一一次修改 尽量使用 class 进行样式修改,而不是直接操作样式 减少 DOM 的操作,可使用字符串一次性插入

    公司 bug 管理工具

    bugfree, bugzilla, jira

    直接给一个数组项赋值,Vue 能检测到变化吗?

    通过 vue.set 方法进行检测

    数组去重

    function unique (arr) {
      return Array.from(new Set(arr))
    }
    var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
    console.log(unique(arr))
    
    function unique(arr){            
            for(var i=0; i<arr.length; i++){
                for(var j=i+1; j<arr.length; j++){
                    if(arr[i]==arr[j]){         //第一个等同于第二个,splice方法删除第二个
                        arr.splice(j,1);
                        j--;
                    }
                }
            }
    return arr;
    }
    
    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return
        }
        var array = [];
        for (var i = 0; i < arr.length; i++) {
            if (array .indexOf(arr[i]) === -1) {
                array .push(arr[i])
            }
        }
        return array;
    }