面试高频问题汇总

541 阅读24分钟
Vue
1、Vue2和Vue3 区别与联系2、Vue生命周期及钩子函数
3、组件间通信4、Vue响应式原理
5、Watch和Computed的区别6、Dom渲染在哪个阶段完成
7、Vue是单向数据流还是双向?为什么8、v-model
9、vue2和vue3双向绑定原理10、vuex
11、vue路由12、vue中$nextTick 作用与原理
13、vue单页面优缺点
工程化
1、vite与webpack2、webpack常用配置
其它亮点技术
1、Canvas2、echarts
网络、优化
1、httphttps 不同地方2、说说跨域
3、前端优化4、防抖、节流应用
JS
1、数组去重2、原型和原型链
3、map 和 forEach4、浅拷贝与深拷贝
ES6
1、Promise2、ES6新属性
3、箭头函数4、asyncawait
5、import 和 require---
6、call、apply和bind---
HTML/CSS
1、元素居中2、H5新增属性
3、CSS3新特性4、清除浮动的几种方法
5、盒子模型6、弹性布局
7、BFC8、移动端适配
浏览器
1、cookie、sessionStorage、localStorage2、如何写一个会过期的localStorage
2、如何写一个会过期的localStorage3、浏览器渲染页面过程

Vue

1、Vue2和Vue3 区别与联系

1.1 区别:

vue2双向绑定是利用ES5的Object.definePropert()对数据进行劫持结合发布者-订阅者模式实现

vue3双向绑定是利用了ES5的Proxy代理,相比于vue2,Proxy可以进行数组的监听,无需再对数组进行单独的特异性操作处理,而Object.definePropert()只能监听某个属性,不能对全对象监听(可以使用Vue.set或vm.$set解决vue2的数组监听)

1.2 尤大为什么发布vue3:

  • 主流浏览器对新的js语言特性的普遍支持

  • 当前Vue代码库目前存在设计和体系架构的问题

1.3 vue3对比vue2的优点:

相比vue2,vue3的运行速度更快(重写了虚拟Dom实现、编译模板优化、高效的组件初始化)、体积更小(使用了webpack的tree-shaking)、更容易维护和使用(灵活逻辑组合和复用,也可以搭配其它框架使用、更好的支持ts),也更接近原生(可以自定义渲染api)

1.4 vue3比vue2有哪些变化:

image.png

新增功能包括:

framents: 组件支持多根节点

<!-- Layout.vue -->
<template>
  <header>...</header>
  <main v-bind="$attrs">...</main>
  <footer>...</footer>
</template>

Teleport:在组件逻辑位置写模板代码,在vue应用范围外渲染

<button @click="showToast" class="btn">打开 toast</button>
<!-- to 属性就是目标位置 -->
<teleport to="#teleport-target">
    <div v-if="visible" class="toast-wrap">
        <div class="toast-msg">我是一个 Toast 文案</div>
    </div>
</teleport>

composition:构建自定义渲染器,可以将利用此生成canvas画布

image.png

createRender

import { createRenderer } from '@vue/runtime-core'
const { render, createApp } = createRenderer({
  patchProp,
  insert,
  remove,
  createElement,
  // ...
})
export { render, createApp }
export * from '@vue/runtime-core'

全局Global API更改为应用程序实例

模板指令变化

组件上只能使用普通函数创建功能组件、异步组件需要defineAsyncComponents方法创建

渲染函数上API改变

2、Vue生命周期及钩子函数

image.png

2.1:Vue生命周期阶段

总体分为:初始化、运行中和销毁;

详细来讲分为:开始创建、初始化数据、编译模板、挂载Dom、渲染->更新->渲染、销毁等系列过程

2.2:Vue生命周期钩子函数

1. beforeCreate(创建前):

此时实例挂载元素$el和数据对象data都为undefined,还未初始化,无法访问到data数据和真实的dom

2. Created(创建后):

此时可以对data对象里的数据进行使用和更改,不会触发其它钩子函数,可以在这里做初始数据的获取,在此时进行dom操作时,需要借助this.$nextTick()方法

3. beforeMount(挂载前):

此时Vue实例的$el和data都已完成初始化,但此时仍然为虚拟Dom节点,具体data.filter还未替换,在此时更改数据,不会触发其它钩子函数,一般在这里做初始化数据获取

4. mounted(挂载后):

此时组件已经出现在页面中,真实数据和Dom都已挂载完毕,data.filter成功渲染,可以操纵真实Dom

5. beforeUpdate (更新前):

当组件的实例数据更之后,会立即触发此钩子函数,会重新构建虚拟Dom

6. updated(更新后):

当更新完成后,执行此钩子函数,数据已经更改完成,Dom需要重新渲染,可以操纵更新后的Dom

7. beforeDestroy(销毁前):

当调用$destroy方法后,立即执行此钩子函数,用在处理善后工作,如清除定时器和非指令绑定事件等

8. destroyed(销毁后)

此时Vue实例解除事件监听和Dom绑定,但Dom节点依然存在,也可以完成善后工作

3、组件间通信

答:组件间通信一共有八种方式。

1. props和$emit

最常见的父子组件通信方式,父组件向子组件传递数据通过prop传递,子组件向父组件是通过$emit触发事件传递

2. children/children/parent与ref

子组件通过this.parent访问父组件,子组件被推入到父组件的parent访问父组件,子组件被推入到父组件的children数组中

3. v-model

父组件通过v-model传值给子组件,会自动传递一个value的prop属性,在子组件中通过this.$emit('input',value)自动修改v-model绑定值

4. attrs和listeners

此方法解决了多层嵌套组件传值的弊端问题,组件A可以直接传值给组件C/D...

5. provider和inject

父组件通过provider提供变量,子组件通过inject注入变量,只要父组件在生命周期内,子组件无论嵌套多少层都可以调用inject来注入provider变量

6. 中央时间总线(eventBus)

组件间无父子关系(兄弟组件)时,通过新建Vue事件bus对象,再通过bus.emit触发事件,bus.on监听事件

image.png

7. vuex处理组件间数据交互

vuex抽取公共data和方法,各个组件可以对其进行读写

8. localStorage / sessionStorage

通过window.localStorage.setItem(key,value)存储数据,再通过window.localStorage.getItem(key)获取数据

4、Vue响应式原理

答:响应式是指当数据有变化时,Vue能做出响应,然后重新去渲染页面,它采用数据劫持结合发布者-订阅着模式,通过Object.defineProperty()来劫持各个属性的setter和getter,数据变动时发布给订阅者,触发响应的监听回调。具体方式为:

1. data属性被转化getter和setter,并且记录相应的依赖,当被改动时通知相应依赖

2. 所有组件实例会有对应的watcher实例,而watcher实例会依赖于相应的setter

3. 当数据变化时,setter会被调用,setter会通知对应的watcher,watcher会更新相应视图

5、Watch和Computed的区别

computed:

1. 支持缓存,只有依赖数据发生变化时,才会被重新计算

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

3. computed属性值会默认走缓存,计算属性是基于他们响应式依赖进行缓存,是基于data中声明过或者父组件传递的props中的数据通过计算得到的值

4. 适合监听多个数据

5. 当computed属性值是函数时,默认走get()方法,函数返回值是属性的属性值;computed中属性都有get和set方法,当数据变化,调用set()方法

watch:

1. 不支持缓存,当数据变化,直接触发相应操作

2. 支持异步操作,可以监听异步操作的数据变化

3. 监听函数接收两个参数,一个最新值(new)和一个旧值(old)

4. 适合监听单个数据

5. 监听的数据必须是data中声明过或者父组件传递的props中的数据,当数据变化时,触发其它操作

6. 函数有两个参数,immediate(立即触发回调)和deep(深度监听,监听数组对象内容发生变化)

6、Dom渲染在哪个阶段完成

答:Dom渲染在mounted阶段中已经完成,此时真实Dom节点已被挂载到页面中

6.1:异步加载在哪个阶段?mounted还是created

答:一般情况在beforeMount、mounted和created都可以进行ajax的异步请求,因为在这三个阶段data都已创建,可以将服务端返回的数据进行赋值。但大部分时候是在created阶段进行发送ajax请求,因为如果页面首次渲染数据就来自服务端,此时能够更快的获取数据,减少loading时间;其次在created中有助于一致性,因SSR不支持在beforeMount和mounted钩子函数

7、Vue是单向数据流还是双向?为什么

答:单向数据流,因为父级的prop的更新会向下流动到子组件中,每次父组件发生更新,子组件所有的prop都会刷新为最新值;数据从父组件传递给子组件,只能单向绑定,子组件内部不能直接修改父组件传递过来的数据(可以使用data和computed解决)

8、v-model

答:本质为语法糖,即用简练语法表达复杂语句,可以用v-model指令在表单及元素上创建双向数据绑定

8.1:v-model作用

1. 它会根据控件类型自动选取正确的方法来更新元素

2. 它负责监听用户的输入事件以更新数据,并对一 些极端场景进行一些特殊处理

3. 它会忽略所有表单元素的value、checked、selected特性的初始值,而将vue实例数据作为数据源,所以应当在data中声明初始值

8.2:v-model实现原理

1. v-bind: 绑定响应式数据

2. 触发oninput(@input) 事件并传递数据

<input v-model="data" /> 
等同于
<input :value="data" @input="data =$event.target.value" />

9、vue2和vue3双向绑定原理

9.1:vue2双向绑定的方法

答:Vue2.0的数据响应是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty () 来劫持各个属性的setter、getter

9.2:vue2缺陷与解决方案

1. vue实例创建后,无法检测到对象属性的新增或删除,只能追踪到数据是否被修改。即实例后每个数据属性都有get和set,在实例创建后允许Vue观察并触发更新,当实例后进行修改,则不会被Vue处理,改变get和set

解决方案:

// 响应式对象的子对象新增属性,可以给子响应式对象重新赋值 
data.location = { x: 100, y: 100 } 
data.location = {...data, z: 100}

2. 不能监听数组的变化。即使用了hack,无法监听数组操作的对应方法

解决方案: 重写数组Array.prototype对应的方法

const methods = ['pop','shift','unshift','sort','reverse','splice', 'push']; 
// 复制Array.prototype,并将其prototype指向Array.prototype 
let proto = Object.create(Array.prototype); 
methods.forEach(method => { 
    proto[method] = function () { // 重写proto中的数组方法 
    Array.prototype[
        method].call(this, ...arguments); 
        viewRender() // 视图更新 
        function observe(obj) { 
            if (Array.isArray(obj)) { // 数组实现响应式 
                obj.__proto__ = proto; // 改变传入数组的prototype 
                return;
            if (typeof obj === 'object') { 
                ... // 对象的响应式实现 
            } 
         } 
     } 
  })

9.3:vue3双向绑定的方法

答:通过ES6新增属性 Proxy 实现双向绑定,类似于设计模式中的代理。可以理解为外界访问该对象必须先通过这层拦截,提供了一种机制对外界访问进行过滤和改写。其核心优点为可以有它实现非核心逻辑,对象只需关注核心逻辑,降低对象复杂度。

10、vuex

答:Vue的状态管理工具,可以集中存储管理应用组件的状态,并以相应规则保证状态可以以一种预测方式发生变化

10.1: vuex作用

答:vuex是一种状态管理机制,将全局组件的共享状态抽取出来为一个store,以一个单例模式存在,应用任何一个组件中都可以使用,vuex更改state的唯一途径是通过mutation,mutation需要commit触发, action实际触发是mutation,其中mutation处理同步任务,action处理异步任务。

image.png

10.2:vuex属性哪些

1. state

state是存储的单一状态,是存储的基本数据

2. Getters

getters是store计算属性,对state进行封装派生数据,和computed一样,返回值根据依赖缓存,依赖值发生变化,重新计算

3. Mutations

mutations提交更改数据,使用store.commit方法更改state存储的状态。(mutations同步函数)

4. Actions

actions像一个装饰器,提交mutation,而不是直接变更状态。(actions可以包含任何异步操作)

5. Module

Module是store分割的模块,每个模块拥有自己的state、getters、mutations、actions

6. 辅助函数

Vuex提供了mapState、MapGetters、MapActions、mapMutations等辅助函数给开发在vm中处理store

10.3: vuex实现原理

1. store的注册

在Vue初始化钩子前,给Vue实例注入一个store属性,即可在Vue组件中通过this.store属性,即可在Vue组件中通过this.store.data访问数据和状态

2. mutations、commit实现

function registerMutation (store, type, handler, local) { 
    // 获取 type(module.mutations 的 key) 对应的 mutations, 没有就创建一个空数组 
    const entry = store._mutations[type] || (store._mutations[type] = []) 
    // push 处理过的 mutation handler 
    entry.push(function wrappedMutationHandler (payload) { 
    // 调用用户定义的 hanler, 并传入 state 和 payload 参数 
    handler.call(store, local.state, payload) }) 
}
registerMutation 是对 store 的 mutation 的初始化,它接受 4 个参数,store为当前 Store 实例,
type为 mutation 的 key,handler 为 mutation 执行的回调函数,path 为当前模块的路径。
	

3. 辅助函数

11、vue路由

11.1 vue路由声明

1. 嵌套路由(以 / 开头的为根路径,子路径不能加 /)

const router = new VueRouter({
  routes: [
    {
      path: '/user/:id', component: User,
      children: [
        // 当 /user/:id 匹配成功,
        // UserHome 会被渲染在 User 的 <router-view> 中
        { path: '', component: UserHome },

        // ...其他子路由
      ]
    }
  ]
})

2. 动态路由

const User = {
    template: '...',
    watch: {
        $route(to,from) {
           //对路由变化做出响应
        }
    }
}

3. 命名路由

const router = new VueRouter({
  routes: [
    {
      path: '/user/:userId',
      name: 'user',
      component: User
    }
  ]
})

11.2 vue路由跳转

1. 声明式导航router-link

不带参数

<router-link :to="{name:'home', params: {id:1}}">
<router-link :to="{name:'home', query: {id:1}}">  
<router-link :to="/home/:id">  
//传递对象
<router-link :to="{name:'detail', query: {item:JSON.stringify(obj)}}"></router-link> 

带参数

<router-link :to="{name:'home', params: {id:1}}">
<router-link :to="{name:'home', query: {id:1}}">  
<router-link :to="/home/:id">  
//传递对象
<router-link :to="{name:'detail', query: {item:JSON.stringify(obj)}}"></router-link> 

2. 编程式导航 this.$router.push()(指定url路径,可返回上一个页面)

不带参数

this.$router.push('/home')
this.$router.push({name:'home'})
this.$router.push({path:'/home'})

query传参(类似get,跳转后页面拼接参数?id=1)

1.路由配置:
name: 'home',
path: '/home'
2.跳转:
this.$router.push({name:'home',query: {id:'1'}})
this.$router.push({path:'/home',query: {id:'1'}})
3.获取参数
html取参: $route.query.id
script取参: this.$route.query.id

params传参(类似于post,不会拼接参数,用于传输密码等重要字段)

1.路由配置:
name: 'home',
path: '/home/:id'(或者path: '/home:id')
2.跳转:
this.$router.push({name:'home',params: {id:'1'}})
注意:
// 只能用 name匹配路由不能用path
// params传参数(类似post)  路由配置 path: "/home/:id" 或者 path: "/home:id"否则刷新参数消失
3.获取参数
html取参:$route.params.id 
script取参:this.$route.params.id

直接通过path传参

1.路由配置:
name: 'home',
path: '/home/:id'
2.跳转:
this.$router.push({path:'/home/123'}) 
或者:
this.$router.push('/home/123') 
3.获取参数:
this.$route.params.id

传递对象(obj不能过程)

query传递对象 (类似get,url后面会显示参数) JSON.stringify(obj) 转一下.
接收参数:
JS:JSON.parse(decodeURIComponent(this.$route.query.obj)) 
HTML:JSON.parse(decodeURIComponent($route.query.obj))

3. 编程式导航 this.$router.replace()(跳转指定url,点击返回跳转到上一个页面【替换】)

4. 编程式导航 this.$router.go()(向前或向后跳n个页面)

12、vue中$nextTick 作用与原理

作用:获取更新后的DOM节点,因DOM更新是异步操作,修改数据时,视图不会立刻更新,而是监听数据的变化,并缓存同一事件循环中,等同一数据变化完之后在进行视图更新,设置了$nextTick()之后,下次DOM更新循环结束后执行延迟回调,即数据发生变化后,实时更新视图

原理:在下次DOM更新循环结束后执行延迟回调,可能会采用Promise、MutationObserver、setLmmediate或setTimeout

13、vue单页面优缺点

优点:

  • 良好体验交互(页面内容改变只需局部刷新,无需整页面重新加载,没有页面间切换产生的白屏现象及闪烁现象,整体页面更加流畅)

  • 良好前后端工作分离模式(后端API通用化,一套后端服务多种客户端)

  • 减轻服务器压力(服务器只用提供数据,吞吐能力提高)

缺点:

  • 首屏加载慢

解决方案:

  • vue-router懒加载(按需加载组件,当路由被访问时候加载对应组件,而非加载首页时加载所有组件)

  • 使用CDN加速

工程化

1、vite与webpack区别

1. webpack会先打包,然后启动开发服务器,请求服务器时直接给予打包结果

2. vite在启动时不需要打包,因此启动速度快。当浏览器需要某个模块时再按需进行编译,极大缩减编译时间

3. vite充分利用ES Modules,会自动向依赖的Modules发送请求,将开发环境下模块文件作为浏览器要执行文件,而非像webpack打包合并

4. webpack是全部加载,当改动模块后,vite仅需请求该模块,webpack需要全部编译一次

5. vite主要优势在于开发阶段,当需要打包到生产环境时,vite要使用传统的rollup进行打包(或手动安装webpack)

2、webpack常用配置有哪些

1. 打包的入口(import)和出口(exports)

2. 配置html-webpack-plugin

3. 设置webpack的自动打包(webpack-dev-server)

4. webpack中加载器:

  • 4.1 style-loader,css-loader来处理样式文件
  • 4.2 babel-loader 来处理高级js语法的加载器
  • 4.3 url-loader和file-loader 来处理打包图片文件以及字体文件
  • 4.4 post-css 自动添加css的兼容性前缀

网络、优化

1、http/https 不同地方

http是超文本传输协议,信息是明文传输,端口号为80,且连接简单无状态

https是具有安全性的ssl解密传输协议,端口号为443,其协议由ssl+http协议构建的,可进行加密传输,身份认证的网络协议,比http安全

2、说说跨域

2.1:为什么会产生跨域

答:在前后端分离的模式下,前后端域名不一致,又js出于安全考虑有同源策略,即只有协议+主机名+端口号相同才能够允许相互访问和接口交互,不同源客户端脚本在没有明确授权情况下,不能读写对方资源

2.2:有哪些方法解决跨域问题

image.png

1. 通过jsonp

为了减轻服务器压力,把静态资源分离到另一台独立域名,html页面再通过相应标签从不同域名加载静态资源,而被浏览器允许,所以通过动态创建script,再请求带参实现跨域通信,但只能实现get一种请求

2. location.hash + iframe 跨域

A如果要和B相互通信,可以通过中间页C来实现,三个页面在不同域之间利用iframe的location.hash传值

3. 跨域资源共享(CORS)

在普通跨域请求,只服务端设置Access-Control-Allow-Origin,前端无需操作;带cookie请求,前后端都要

4. Nginx反向代理接口跨域

通过Nginx配置一个代理服务器(域名相同,端口不同)做跳板机,反向代理访问代理接口

5. WebSocket协议跨域

HTML5新的协议,实现浏览器与服务器双工通信,允许同时通信

3、前端优化

3.1:浏览器页面加载优化

1. 压缩合并代码,减少文本空间

2. 代码分割(code spliting),可以基于路由动态懒加载

3. 第三方模块放在CDN

4. 大模块异步加载,如Echarts可以用require.ensure在加载成功后显示对应图表

5. 小模块适度合并,速度更快

6. 可以使用pefetch预加载,适合分布场合

3.2:前端性能优化(SEO)

1. 添加各种meta信息

2. 预渲染

3. 服务端渲染

3.3: 页面渲染优化

1. 尽量减少reflow和repaint,涉及到样式,尺寸,节点增减的操作,都会触发reflow和repaint

2. 用变量缓存dom样式,不要频繁读取

3. 通过DocumentFragment或innerHTML批量操作dom

4. dom隐藏,或复制到内存中,类似virtual dom,进行修改,完成后再替换回去

3.4: 图片、样式及代码优化

1. 图片上,小图使用雪碧图、iconFont和base64内联,图片使用懒加载,图片一定要压缩,可以用webp代替其它格式,也可以使用img的srcset,根据不同分辨率显示不同尺寸图片

2. css样式上,css引入写在html头部,避免内联style样式,避免css表达式,避免写空的css

3. js上,js引入写在html底部,用defer放在头部提前加载,不阻塞dom解析,script标签加上crossorigin

3.5: Vue优化

1. 路由懒加载组件

2. keep-alive缓存组件,保持原显示状态

3. 列表项添加key,保证唯一, 避免数据混乱 , 用来提高渲染性能

4. 列表项绑定事件,使用事件代理(v-for)

5. v-if和v-for不要用在一个标签上,它会在每个项上进行v-if判断

4、防抖、节流应用

函数防抖(debounce):点击提交

在频繁触发情况下,只有足够的空闲时间,才执行代码一次

要声明一个变量当标志位,记录当前代码是否在执行,如果正在执行,取消这次方法执行,直接return,如果空闲正常触发方法执行

async f() {
    if (!this.keyword) {
        this.suggestions = []
        return
    }
    const {
        data: res
    }= await searchSuggestion(this.keyword)
    this.suggestions = res.data.options
},
hInput_debounce() {
    if(this.timer) {
        clearTimeout(this.timer)
    }
    this.timer = setTimeout(()=> {
        this.f()
    },0.4* 1000)
},

函数节流(throttle):窗口缩放、鼠标移动,input、scroll

一定时间内js方法只执行一次

需要一个延时器来辅助实现,延迟需要执行的代码,如果方法多次触发,把上次记录的延迟执行代码用cleartimeout清除掉,重新开始,如果记时完毕,没有方法来访问触发,则执行正常代码

async f() {
    if (!this.keyword) {
        this.suggestions = []
        return
    }
    const {
        data: res
    }= await searchSuggestion(this.keyword)
    this.suggestions = res.data.options
    console.log(res)
},
hInput_throttle() {
console.log('hInput_throttle.....')
    if(Date.now() - this.lastCallTime >= 0.4 * 1000) {
        this.f()
        this.lastCallTime = Date.now()
    }else{
        console.log('没有间隔0.4秒,不能调用f()')
    }
},

JS

1、数组去重

方法一:利用ES6的展开运算符和Set成员的唯一性

let arr = [1, 2, 3, 4, 4, 3, 2, 1];
    function unique(arr) {
       return [...new Set(arr)];
    }
console.log(unique(arr)) // [1, 2, 3, 4]

方法二:利用indexOf()

var arr = [1, 2, 3, 4, 4, 3, 2, 1];
    function unique(arr) {
        var res = [];
        for (var i = 0; i < arr.length; i++) {
           if (res.indexOf(arr[i]) === -1) {
              res.push(arr[i])
           }
         }
         return res;
     }
console.log(unique(arr)); // [1, 2, 3, 4]

2、原型和原型链

答:原型是一个对象,称prototype为原型对象,作用就是共享方法。每个实例对象都有一个proto属性,指向构造函数的原型对象,而构造函数也有原型对象,也有peoto属性,这样一层层往上形成原型链

3、map 和 forEach 区别

答:forEach() 是将Array里面的每个element都循环一遍;map() 会遍历原来的Array,并返回一个新的array,而且这两个Array是一样长的,使用map前前面要定义一个新的变量,forEach不用

4、浅拷贝与深拷贝

答:假设B复制A,当A修改时,看B是否发生变化,如若B跟着变化则为浅拷贝,如未发生变化则为深拷贝

1. 浅拷贝实现方式:改变堆栈地址值

  • 遍历赋值 for in
  • Object.assign

2. 深拷贝实现方式:复制堆栈内存

  • 遍历赋值 for in
  • JSON.parse和JSON.stringify

ES6

1、Promise

答:在创建Promise对象时,需要 new Promise,当内部满足条件情况下,调用resolve函数(成功);当不满足条件时,调用reject函数(失败);可以使用 Promise.then() 方法获取回调返回内容

// 创建一个promise
let promise = new Promise((resolve, reject) => {
            // 做一些事情(进行状态)
            if (/*当满足某个条件*/) {
                resolve(一些数据) // 调用resolve函数 (由进行转变到成功状态)
            } else(/*即不满足条件*/){
                reject(错误)  // 调用rejected函数(由进行转变到失败状态)
            }
        })

// 使用这个promise
promise.then((一些数据) => {
    // 这里写resolve函数的具体实现
}, (错误) => {
    // 这里写reject函数的具体实现
})

1.1: Promise解决什么问题

答:用来解决以下两个问题

1. 回调地狱问题,可以有效解决多层嵌套回调的问题

2. Promise可以支持多并发请求,获取并发请求到的数据

1.2:有几种状态,分别是哪些

答:Promise 有三种状态,即pending 初始状态(等待状态)、fulfiled成功状态和rejected失败状态,状态一旦变化后不会再变。

1.3: Promise有什么特点

1. Promise对象的状态不受外界影响

2. Promise对象的状态一旦改变,不会再变,不会逆转

1.4:有什么不足的地方,需要怎么避免

1. 一旦创建Promise对象,无法中途取消执行

2. 如果不设置回调函数,Promise内部会抛出异常,不会反映到外部

3. 当处于pending(等待)状态时,无法得知是刚开始还是已完成

可以使用async/await 来解决

1.5:Promise.all() 方法 和 Promise.race() 方法

Promise.all()是将多个Promise放在一个数组中,当整个数组全部Promise成功时才会返回成功,当数组中有一个失败就会返回失败

Promise.all([promise1, promise2]).then(success, fail)
// 只有当promise1和promise2都成功时才会调用success
// 有一个promise失败都会调用fail

Promise.race是将多个Promise放在一个数组中,数组中有一个Promise最先得到结果,不管完成(resolved)还是失败(reject),都会返回一个Rpromise

Promise.race([promise1, promise2]).then(success, fail)
// promise1和promise2只要有一个成功就会调用success;
// promise1和promise2只要有一个失败就会调用fail;
// 总结: 谁第一个成功或失败,就认为是race的成功或失败。

2、ES6新属性

答:新增let和const、模板字符串、箭头函数、函数参数默认值、对象和数据结构、 for...in、for...of、类等

1. 变量和作用域

新增let和const的块级作用域变量声明,let声明只在块级作用域中有效,可以用于for循环中变量,且不存在变量提升,但会造成暂时性死区(在声明let之前无法使用)、也不允许重复声明;const一旦声明必须赋值,不能null占位,且不可修改值

2. 原生对象方法拓展

新增String(模板字符串)、RegExp(正则表达式)、Number(Math新方法)、Function(函数参数默认值、rest参数、严格模式、箭头函数)、Array(拓展运算符)、Object和Symbol

3. 数据结构Set和Map

Set可以用来数组去重,其实例方法有:add()、delete()、has()、clear();遍历方法有:keys()、values()、entries()、forEach()、map()、filter();Map实例方法有:set()、get()、has()、delete()、clear(),遍历方法与Set相同

4. 元编程相关Proxy和Reflect

Proxy 代理,对外界对对象的访问和操作进行过滤和改写,Reflect 对象方法和Proxy一一对应

5. 异步编程Promise\Generator\Async

Generator 是一种异步编程解决方案,可以理解为一个状态机,封装多个内部状态,函数内部有 yield暂停状态。三者对比,使用Promised的异步代码存在大量自有API调用,操作本身语义夹杂不清楚;Generator函数实现异步代码语义比Promise清晰,但需要一个执行器;Async函数写法最简洁,符合语义,不需要执行器;

6. 语言层面类、模块支持

class(类,类必须使用new调用,相当于实例原型,所有类中定义的方法都会被继承,如果在方法前加上static关键字即为静态方法,不会被继承)。modules(模块, export\import)

7. JS对象分类、及其他原生对象

image.png

8. Iterator(遍历器 for-in\for-of)

3、箭头函数

3.1: this指向

答:没有自身this,箭头函数的this实际是继承它定义时所处的全局执行环境中的this,指向windowd对象。并且其继承而来的this永远不变,call/apply/bind也不能改变箭头函数的this指向

3.2:和普通函数有什么区别

1. 箭头函数不能用于构造函数,不能使用new

2. 箭头函数中this指向不同,普通函数中this总是指向调用它的对象,箭头函数没有自身的this,一般指向父级作用域

3. 箭头函数不绑定argument,而使用rest参数代替

3.3: 箭头函数可以作为构造函数使用吗,为什么?

答:不能,构造函数一般分为四步,即JS内部生成一个对象、把函数中的this指向该对象、执行构造函数语句、返回对象实例。但是箭头函数没有自己的this,是继承外层环境的this,this指向不会改变,因而不能作为构造函数,否则new调用时会报错

4、async/await

async用于声明一个异步函数,该函数执行完之后返回一个 Promise 对象,可以使用 then 方法添加回调函数

wait 操作符用于等待一个 Promise 对象,它只能在异步函数 async function 内部使用。

async函数运行的时候是同步运行,但是当async函数内部存在await操作符的时候,则会把await操作符标示的内容同步执行,await操作符标示的内容之后的代码则被放入异步队列等待。

4.1: 和 Promise 关系

答:async/await 封装的函数,其本质还是Promise,async相当于相当于Promise.then(),await相当于.then()括号里面的操作

// async function testAsync() {
  //   await getJson()  
  //   console.log(3);
  // }
  //相当于:
function testAsync(){
    return Promise.resolve().then(()=>{
      return getJson()
    }).then(()=>{
      console.log(3);
    })
  }

5、import 和 require 区别

1. 模块加载时间

requier:运行时加载

import:编译时加载(效率更高),一般提到整个模块头部

2. 模块本质

requier:CommonJS模块

import:ES6模块

3. 严格模式下

requier:CommonJS默认采用非严格模式,输出一个值的拷贝

import:ES6模块默认采用严格模式,无论是否这1模块头部加上use strict,其输出是值的引用

6、call、apply、和bind的区别与异同

varobj={
x:81,
};
varfoo={
getX:function(){
returnthis.x;
}
}
console.log(foo.getX.bind(obj)());//81
console.log(foo.getX.call(obj));//81
console.log(foo.getX.apply(obj));//81

6.1、相同:

1. 都是用来改变this对象的指向问题

2. 第一个参数都是this要指向的对象

3. 都可以利用后续参数传参

6.2、区别:

1. call()和apply()会调用函数,并且改变函数内部的this指向

2. call()和apply()传递的参数不一样,call()传递的参数以arg1,arg2形式,apply()必须是数组形式[arg]

3. bind()不会调用函数,可以改变函数内部的this指向

6.3、主要应用场景、

1. call()经常做继承

2. apply()经常跟数组有关,如实现数组的最大值最小值等

3. bind()不调用函数,但可以改变this指向,如改变定时器内部的this指向

浏览器

1、cookie\sessionStorage\localStorage

1.1: 说说他们之前的区别与共同点

共同点:都能暂时存储数据,都保存在浏览器端,并且都是同源,都是字符串类型数据

区别:

cookie:应用在数据存储和跨页面通讯

  • 数据每次都会发给服务端
  • 前端和后端都可以操作
  • 默认为会话级别的有效期,有效期内有效,即使页面被关闭,但可以手动设置过期时间
  • 只要cookie里有数据,请求都会自动携带
  • 存储大小只有4kb,受IE限制

sessionStorage:只能应用在存储数据

  • 相对于cookie较为安全,数据不会发送给服务端,不会出现数据截获
  • 只能在当前会话窗口有效
  • 始终保存存在的数据,但窗口关闭则销毁数据
  • sessionStorage为设置为永久时效的cookie,即永久时效
  • 存储大小5M

localStorage: 应用在存储数据和跨页面通讯

  • 相对于cookie较为安全,数据不会发送给服务端,不会出现数据截获
  • 只能前端进行操作存储
  • 存储时效为永久,但可以手动设置删除
  • 无论有无数据,请求都会自动携带
  • 存储大小5M

1.2:cookie有什么缺点

1. 隐私问题:第三方应用可以直接读取cookie数据

2. 存储数据不安全:明文形式存储,任何人都可以打开并进行篡改

3. 难以解密:可以手动加密或解密cookie

4. 存储大小限制:存储大小一般限制为4kb,每个站点只能容纳20个cookie

5. 可以被禁用:用户可以在浏览器上禁用掉cookie

6. 可以被手动删除

2、如何写一个会过期的localStorage

答:可使用惰性删除(某个键值过期,键值不会马上删除,而是下次被使用时,才会检查到过期,此时才得到删除,缺点为如果key一直没有被用到,即使已经过期而仍然被存放在localStorage中)

或定时删除实现(每隔一段时间执行一次删除操作,并通过限制执行次数和频率减少删除操作对CPU的长期占用)

3、浏览器渲染页面过程

1. DOM树构建

2. CSSOM树构建(CSS加载不会阻塞DOM树,但会阻塞RenderObject树)

3. RenderObject树构建

4. 布局

5. 绘制

HTML/CSS

1、元素居中

1.1:元素水平垂直居中

1. 弹性布局 flex

display: flex;
align-items: center;
justify-content: center;

2. 定位加位移

position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);

3. 盒子脱离文档流设置margin:auto

position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;

4. 定位加子盒子下边距、左边距一半

width: 100px;
height: 100px;
background-color: blue;
position: absolute;
left: 50%;
top: 50%;
margin-left: -50px;//为宽度的一半
margin-top: -50px;//为高度的一半

1.2:元素无固定高度(宽度)居中

答:子元素绝定位,父元素相对定位,子元素 top:50%,transform: translateY(-50%)

子元素设置为行内块,再加 vertical-align:middle

给父元素 display:table,子元素 display:table-cell,vertical-align: middle

2、H5新增属性

1. 拖拽释放(Draganddrop)API ondrop 自定义属性 data-id 获取li.getAttribute('data-id')或者li.dataset.type='guoji'

2. 新增语义化标签(header\nav\footer\aside\article\section\main...)

3. 新增音频、视频API(audio\video)

4. 新增画布Canvas API

5. 新增地理Geolocation API

6. 本地离线存储localStorage 长期存储数据,浏览器关闭后数据不丢失;sessionStorage的数据关闭浏览器后自动删除

7. 出现新的技术(webworker\websccket\Gelolcatoin)

3、CSS3新增哪些新特性

1. 过渡 transition:

transition: margin-right 4s ease-in-out;

2. 动画 animation:

animation-delay:设置延时时间,从加载完到开始执行 animation-direction:设置重复运动还是反向运动 animation-duration:设置动画周期时长 animation-iteration-count:设置动画重复次数 animation-timing-function:设置动画速度(ease\ease-in\ease-in-out\linear\step-star\step-end)

p {
  animation-duration: 3s;
  animation-name: slidein;
}
@keyframes slidein {
  from {margin-left: 100%;width: 300%;}
  to {margin-left: 0%;width: 100%;}
}

3. 形状转换 transform:

rotate(30deg); translate(30px,30px); scale(.8);
skew(10deg,10deg); rotateX(180deg); 
rotateY(180deg); rotate3d(10,10,10,90deg);

4. 选择器

p:first-of-type 选择属于其父元素的首个元素的每个元素。
p:last-of-type 选择属于其父元素的最后元素的每个元素。
p:only-of-type 选择属于其父元素唯一的元素的每个元素。
p:only-child 选择属于其父元素的唯一子元素的每个元素。
p:nth-child(2) 选择属于其父元素的第二个子元素的每个元素。
:enabled :disabled 控制表单控件的禁用状态。
:checked 单选框或复选框被选中。

5. 阴影 box-shadow:

/* x偏移量 | y偏移量 | 阴影颜色 */
box-shadow: 60px -16px teal;

/* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影颜色 */
box-shadow: 10px 5px 5px black;

/* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);

/* 插页(阴影向内) | x偏移量 | y偏移量 | 阴影颜色 */
box-shadow: inset 5em 1em gold;

/* 任意数量的阴影,以逗号分隔 */
box-shadow: 3px 3px red, -1em 0 0.4em olive;

/* 全局关键字 */
box-shadow: inherit;
box-shadow: initial;
box-shadow: unset;

6. 边框 border-image:

用图片作为边框背景,repeat/stretch/round

7. 背景 background-clip:

定制背景区域,默认从边框开始,paddingborder

8. 文字换行:

word-break:normal|breake-all|keep-all
word-wrap:normal|breake-all|keep-all
超出省略: text-overflow:clip|ellipsis|string

9. 文字阴影:

text-shadow:水平阴影,垂直阴影,模糊的距离,以及阴影的颜色

10. 颜色 rgba:

rgba(0,0,0,1)

11. 渐变色:

background: linear-gradient(blue, pink);

12. 弹性布局 flex:

13. 盒模型定义box-sizing:content-box的时候,边框和padding不包含在元素的宽高之内

14. 媒体查询:@media

4、清除浮动的几种方法

答:当所有的子元素浮动的时候,且父元素没有设置高度,这时 候父元素就会产生高度塌陷

1. 给父元素单独定义高度

  • [优点] :快速简单,代码少

  • [缺点] :无法进行响应式布局

2. 父元素定义 overflow:hidden;zoom:1;(兼容IE6)

  • [优点] :简单快速、兼容性高、代码少

  • [缺点] :超出部分被影藏,布局时容易出问题

3. 在浮动元素后加空标签(伪元素),clear:both;height:0;overflow:hidden;

  • [优点] :简单快速、兼容性高、代码少

  • [缺点] :增加空标签,不利于优化

4. 父元素定义 overflow:auto;

  • [优点] :简单快速、兼容性高、代码少

  • [缺点] :内部宽度和高度超过父级容易出现滚动条

5. 万能清除

.father:after{ 
    content:''; clear:both;display:block;height0;overflow:hidden;visibility:hidden;
}
  • [优点] :写法固定,兼容性高

  • [缺点] :代码多

5、盒子模型

答:css本质是一个盒子,封装周围HTML,它包括margin(外边距)、border(边框)、padding(内边距)、content(内容区)四个属性;一般也分为标准盒子和IE盒子(怪异盒子)

image.png image.png

5.1标准盒子和怪异盒子的区别

区别在于二者对盒子的高度或宽度的计算不同

标准盒盒模型: 盒子总宽度/高度 = width/height + padding + border + margin(width/height 只是内容的宽高,不包含padding 和 border值)

IE盒模型: 盒子总宽度/高度 = width/height + margin(width/height 包含padding 和 border值)

5.2怪异盒子转为标准盒子

答:标准盒子转为IE盒子: box-sizing:content-box(浏览器默认),IE盒子转为标准盒子: box-sizing:border-box;

6、弹性布局

答:当元素被设置了display:flex后,弹性容器内的子元素可以在任何方向上的排布,既可以增加尺寸以填充未使用的空间,也可以收缩尺寸避免元素溢出

6.1:有哪些属性可以设置

flex-direction: 容器中子元素排列方式

flex-direction: row;  /*横向排列 */
flex-direction: column;  /* 竖向排列 */

flex-wrap: 弹性盒子子元素超出父容器是否换行

flex-wrap: nowrap; /* 默认不换行 */
flex-wrap: wrap;

flex-flow: 前两者简写

align-item: 弹性盒子子元素在侧轴上对齐方式

/* Basic keywords */
align-items: normal;
align-items: stretch;

/* Positional alignment */
align-items: center; /* Pack items around the center */
align-items: start; /* Pack items from the start */
align-items: end; /* Pack items from the end */
align-items: flex-start; /* Pack flex items from the start */
align-items: flex-end; /* Pack flex items from the end */
align-items: self-start;
align-items: self-end;

align-content: 设置行对齐

/* 基本位置对齐 */
/*align-content不采用左右值 */
align-content: center;     /* 将项目放置在中点 */
align-content: start;      /* 最先放置项目 */
align-content: end;        /* 最后放置项目 */
align-content: flex-start; /* 从起始点开始放置flex元素 */
align-content: flex-end;   /* 从终止点开始放置flex元素 */

/* 默认对齐 */
align-content: normal;

/* 分布式对齐 */
align-content: space-between; /* 均匀分布项目,第一项与起始点齐平,最后一项与终止点齐平 */
align-content: space-around;  /* 均匀分布项目,项目在两端有一半大小的空间*/
align-content: space-evenly;  /* 均匀分布项目,项目周围有相等的空间 */
align-content: stretch;       /* 均匀分布项目,拉伸‘自动’-大小的项目以充满容器 */

justify-content: 弹性盒子子元素在主轴上对齐方式

/* Positional alignment */
justify-content: center;     /* 居中排列 */
justify-content: start;      /* Pack items from the start */
justify-content: end;        /* Pack items from the end */
justify-content: flex-start; /* 从行首起始位置开始排列 */
justify-content: flex-end;   /* 从行尾位置开始排列 */
justify-content: left;       /* Pack items from the left */
justify-content: right;      /* Pack items from the right */

/* Distributed alignment */
justify-content: space-between;  /* 均匀排列每个元素,首个元素放置于起点,末尾元素放置于终点 */
justify-content: space-around;  /* 均匀排列每个元素,每个元素周围分配相同的空间 */
justify-content: space-evenly;  /* 均匀排列每个元素, 每个元素之间的间隔相等 */
justify-content: stretch;       /* 均匀排列每个元素, 'auto'-sized 的元素会被拉伸以适应容器的大小 */

6.2:元素居中对齐

    display: flex;
    display: -webkit-flex;
    align-items:center;
    justify-content:center;

7、BFC

7.1: 解释BFC概念

答:BFC(Block Formatting Context)块级格式化上下文。是一个独立的渲染区域,规定Box内部如何布局,并且此区域与外部毫不相干

7.2:BFC的布局规则

1. 内部Box在垂直方向,一个接着一个放着

2. Box垂直方向距离由margin决定,同一个BFC相邻的Box的margin会发生重叠

3. 每个Box的margin左边与border左边接触,即使浮动也是如此

4. BFC区域不会与浮动float的Box重叠

5. BFC是一个与外部互不影响的独立容器

6. 计算BFC高度时,浮动元素参与计算

7.3: 什么情况下会发生BFC

1、根元素

2. float属性不为none

3. position 为 absolute 或 fixed

4. dispaly 为 inline-block 、table-cell 、table-caption 、flex 、inline-flex

5. overflow 不为 visible

7.4:BFC的作用

1. 自适应两(三)栏布局,避免自动换行

2. 避免元素被浮动元素覆盖

3. 可以让父元素高度包含子浮动元素

4. 去除边距重叠现象

8、移动端适配

答:设计稿一般尺寸: 750*1334

1. 写页面时,按照设计噶固定宽高,最后做统一缩放适配处理

2. 按设计稿标准尺寸开发,单位上需要缩放时使用rem(设置 font-size:100px;)、vw、vh等相对单位,无需缩放时使用px

3. 固定单位+弹性布局处理flex,<meta name="viewport" content="width=device-width">

4. 使用viewport适配<meta name="viewport" content="width=750,initial-scale=0.5">

5. initial-scale = 屏幕宽度/设计稿宽度

其它亮点技术

1、Canvas

<!-- canvas -->
<canvas id="canvas"></canvas>
<!-- javascript -->
<script>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')
    ctx.fillStyle = 'purple'
    ctx.fillRect(0, 0, 300, 150)
</script>

1.1: 使用canvas实现了哪些东西

应用场景:动画(3D动画)、截图生成、合成图生成、分享网页截图、滤镜、抠图、旋转、缩放、位移、形变、粒子、压缩图片

1.2: 有哪些API

1、uestAnimationFrame

该方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。 该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用

requestAnimationFrame 优点

1.避免掉帧,完全依赖浏览器的绘制频率,从而避免过度绘制,影响电池寿命 

2.提升性能,当Tab或隐藏的iframe里,暂停调用 

drawImage(image, sx, sy [, sWidth, sHeight [, dx, dy, dWidth, dHeight]])绘制图像方法。

toDataURL(type, encoderOptions) 方法返回一个包含图片展示的 data URI 。可以使用 type 参数其类型,默认为 PNG 格式。图片的分辨率为96dpi

getImageData(sx, sy, sw, sh) 返回一个ImageData对象,用来描述canvas区域隐含的数据,这个区域通过矩形表示,起始点为(sx, sy)、宽为sw、高为sh

2、echarts

问题汇总:blog.csdn.net/qq_40055200…