前端面试题(持续更新。。。。。)

1、倒计时如何实现的?如何解决不同地区、设备造成的时间误差,导致倒计时抢购出现问题?

1、记录登录时间,将登录时间储存在本地缓存中。

这样用户即便改变设备时间也对倒计时程序无影响

2、在数据事先存好需要倒计时的时间。

这样倒计时的时间一致,不会出现误差,

通过数据库的抢购时间,与用户登录的时间进行倒计时

2、登录注册功能的登录验证流程,token

img

1、安装并引用:jsonwebtoken、express-jwt

在登录路由中写

// 用于生成 token的模块
const jwt = require('jsonwebtoken')
// 用于解析 token的
const { expressjwt:expressJWT } = require('express-jwt')
2、加密密钥(登录路由)
// 自定义即可
// 加密密钥
const secretKey = 'zls991013@^_^'
3、在登录时,路由中生成一个token(登录路由)
// 生成 token
// username: 为登录时的用户名
// secretKey:自定义密钥
// { expiresIn: '12h' }:自定义token 过期时间
const tokenStr = jwt.sign({ username: username }, secretKey, { expiresIn: '12h' })
// 响应token给前端,带上Bearer头
res.send({
  token:'Bearer '+tokenStr
})
4、登录成功,前端接收token,存储在本地缓存
localStorage.setItem('token',response.data.token)
5、在服务器中注册解析中间件(先引入express-jwt)

切记:放在路由中间件前面,静态资源前

// 注册中间件   expressJWT({ secret: secretKey})就是用来解析 token的中间件    .unless({ path: [/^\/login/] }) 用来指定哪些接口不需要访问权限
const secretKey = 'zls991013@^_^'
app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/login/] }))
6、在服务器中设置错误中间件

切记:放在服务监听前,路由后

// 错误中间件
app.use((err, req, res, next)=> {
    if (err.name === 'UnauthorizedError') {
        return res.send({
            status: 1,
            message:'身份认证失败'
        })
    }
    res.send({
        status: 500,
        message:'未知的错误'
    })
})
7、设置拦截器,将拦截器文件放在每个页面的js文件前面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pdoniCcD-1669815959114)(…/…/…/…/…/Users/zhonglongsheng/Library/Application%20Support/typora-user-images/image-20221010112301197.png)]

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    config.headers.Authorization = window.localStorage.getItem('token')
    return config
}, function (error) {
    return Promise.reject(error)
})


// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    if (response.data.status === 1 && response.data.message === '身份认证失败') {
        location.href = 'http://127.0.0.1:3000/html/login.html'
    }
    return response
   
}, function (error) {
    return Promise.reject(error)
})

3、axios实现前后端数据交互

axios请求:

设置请求方式,请求头,请求路径 (常用get/post请求)

后端路由接收请求:

获取axios请求所传递的参数,

根据所需的数据对数据库进行查询数据,

将所查找的数据响应给axios接收

axios接收到数据,将数据渲染到页面中

4、get和post请求不同的传参方式,post请求头有哪几种类型?有什么区别?分别表示所传递数据的类型

post请求头常见的类型:

  1. application/json(JSON数据格式)(常用)

    用于告诉服务器数据主体是序列化后的json字符串

  2. application/x-www-form-urlencoded (常用)

    用于提交原生的form表单的数据,如果是文件的话只能上传文本格式的文件,form 的 enctyped 属性默认为这个值

  3. multipart/form-data (常用)

    也是用于提交原生的form表单的数据,不过必须让 form 的 enctyped 等于这个值;

    这种方式可以将文件以二进制的形式上传,达到上传多种类型文件的功能

  4. text/xml

    它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范

get传参:

  1. 路由传参

5、ES6模块化

定义:

·每个js 文件都是一个独立的模块

·导入其他模块成员使用 import 关键字

·export 命令用于规定模块的对外接口

基本语法:

·默认导入和导出

·按需导入和导出

·直接导入并执行模块中的代码

①默认语法

默认导出格式: export default 默认导出的成员

注意: 每个模块中,只允许使用唯一的一次 export default,否则会报错。

要用到 export default 命令,为模块指定默认输出。

模块默认输出, 其他模块加载该模块时,import 命令可以为该匿名函数指定任意名字。

默认导入的语法:

import 任意名称 from ‘模块标识符’

②按需导入导出

按需导出: export 按需导出的成员

按需导入: import { … } from ‘模块标识符’

③直接执行

如果只想单纯的执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码,语法:import ‘模块’

6、less文件转css文件(脱离编译器)

·使用 antd-tools 中的 less 转换函数进行转换

·lessc 命令直接打包,node 安装 less 后,可以使用 lessc 命令进行转换:

npm install less --dev

npm lessc ./index.less ./index.css

需要注意的是,安装的包是 less,但是转换使用的命令是 lessc

7、轮播图的实现原理

①先让图片轮播起来,写一个函数里面加定时器,每次使图片的index对象加一,当index大于最大值时,设置index等于第一张图片

②图片下面的小圆点,就是使下面的按钮与上面的图片一一对应。然后点击下面的按钮显示对应的图片。

③左右的上一张和下一张按钮。点击左边的上一张按钮图片向前显示,实现原理就是使 index 对象减一。点击左边的下一张按钮图片向后显示,实现原理就是使 index 对象加一。

④对应上一张和下一张连续点击的问题就是给这两个按钮加上延时器。

⑤当鼠标放在轮播图区域时停止轮播,实现原理就是清除定时器,离开开始轮播就是加上定时器。

8、防抖节流

防抖指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖的应用场景:

用户在输入框连续输入一串字符时,可以通过防抖策略,只在输入完后,才执行查询的请求,这样可以有效减少请求次数,节约请求资源.

节流就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率

节流策略的应用场景:

鼠标不断触发某事件时,如点击,只在单位事件内触发一次.

懒加载时要监听计算滚动条的位置,但不必要每次滑动都触发,可以降低计算频率,而不必要浪费CPU资源.

9、Token和Session

Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库。

如何使用Token?

1、用设备号地址作为Token(推荐)

客户端:客户端在登录的时候获取设备的设备号,并将其作为参数传递到服务端。

服务端:服务端接收到该参数后,便用一个变量来接收同时将其作为Token保存在数据库,并将该Token设置到session中,客户端每次请求的时候都要统一拦截,并将客户端传递的token和服务器端session中的token进行对比,如果相同则放行,不同则拒绝。

2、用session值作为Token

客户端:客户端只需携带用户名和密码登陆即可。

服务端:服务端接收到用户名和密码后并判断,如果正确了就将本地获取sessionID作为Token返回给客户端,客户端以后只需带上请求数据即可。

token和session的区别?

\1) token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;

\2) session在服务器端会保存一份,可能保存到缓存、文件或数据库;

\3) session和token都是有过期时间一说,都需要去管理过期时间;

\4) 其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。

\5) 虽然确实都是“客户端记录,每次访问携带”,但token很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证

10、map和foreach的区别

1、相同点

1) 都是循环遍历数组中的每一项。

2) 每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组)。

3) 匿名函数中的this都是指向window。

4)只能遍历数组。

2、不同点

1) map()会分配内存空间存储新数组并返回,forEach()不会返回数据。

2) forEach()允许callback更改原始数组的元素,map()返回新的数组,map() 不会对空数组进行检测。

11、防抖和节流

防抖(deounce)

我们先说防抖吧,这里有个小笑话,看完你应该就秒懂了:

小明军训,教官发令:向左转!向右转!向后转!大家都照着做,唯有小明坐下来休息,教官火的一批,大声斥问他为啥不听指挥?小明说,我准备等你想好到底往哪个方向转,我再转。

虽然是个笑话,却很好地说明了防抖的定义:给一个固定时间,如果你开始触发动作,并且在这个固定时间内不再有任何动作,我就执行一次,否则我每次都会重新开始计时。我们可以用极端情况理解它:如果给定时间间隔足够大,并且期间一直有动作触发,那么回调就永远不会执行。放在笑话的语境里就是,只有教官最后一次发号后,小明才会转。

节流(throttle)

如果你理解了防抖,关于节流,就更好理解了

学生上自习课,班主任五分钟过来巡视一次,五分钟内随便你怎么皮,房顶掀了都没事,只要你别在五分钟的时间间隔点上被班主任逮到,逮不到就当没发生,逮到她就要弄你了。

在这里,班主任就是节流器;你搞事,就是用户触发的事件;你被班主任逮住弄,就是执行回调函数。

节流的定义:用户会反复触发一些操作,比如鼠标移动事件,此时只需要指定一个“巡视”的间隔时间,不管用户期间触发多少次,只会在间隔点上执行给定的回调函数。,我们同样可以用极端情况来理解:如果给定的间隔时间是 240毫秒,用户永不间断地在屏幕上疯狂移动鼠标,那么你的回调函数会分别在 240毫秒、 480毫秒、 720毫秒… 就这么一直执行下去

12、闭包

理解:主要是为了设计私有的方法和变量。
优点:可以避免全局变量造成污染。
缺点:闭包会常驻内存,增加内存使用量,使用不当会造成内存泄漏。
特征:(1)函数嵌套函数。(2)在函数内部可以引用外部的参数和变量。(3)参数和变量不会以垃圾回收机制回收。

13、src和href的区别

  1. href
    href:是指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,用于超链接。

标识超文本引用,用在link和a等元素上,href是引用和页面关联,是在当前元素和引用资源之间建立联系

****当浏览器解析到这一句的时候会识别该文档为css文件,会下载并且不会停止对当前文档的处理,这也是为什么建议使用link方式来加载css而不是使用@import。

  1. src
    src:是指向外部资源的位置,指向的内部会迁入到文档中当前标签所在的位置;在请求src资源时会将其指向的资源下载并应用到当前文档中,例如js脚本,img图片和frame等元素。

表示引用资源,表示替换当前元素,用在img,script,iframe上,src是页面内容不可缺少的一部分。

当浏览器解析到这一句的时候会暂停其他资源的下载和处理,直至将该资源加载,编译,执行完毕,图片和框架等元素也是如此,类似于该元素所指向的资源嵌套如当前标签内,这也是为什么要把js饭再底部而不是头部。
  1. src和href的区别:
  2. 当浏览器遇到href会并行下载资源并且不会停止对当前文档的处理。(同时也是为什么建议使用 link 方式加载 CSS,而不是使用 @import 方式)
  3. 当浏览器解析到src ,会暂停其他资源的下载和处理,直到将该资源加载或执行完毕。(这也是script标签为什么放在底部而不是头部的原因)

14、跨页面通信

1、路由传参

2、浏览器缓存

3、数据代理,proxy

4、bus总线程

5、vuex

16.new操作符具体干了什么

  • 创建一个空对象
  • 将对象的原型链挂载在函数的原型上
  • 改变this指向
  • 返回实例化对象

17、常见的请求方式

1、get:

请求指定页面信息,并返回实体主体。
2、head:

类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。
3、post:

向指定资源提交数据进行处理请求(例如提交表单或上传文件),数据包含在请求体中。
post请求可能会导致新的资源的建立或已有资源的修改。
4、put:

从客户端向服务器传送的数据取代指定的文档的内容。
5、delete:

请求服务器删除指定的页面。
6、connect:

HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
7、options:

允许客户端查看服务器的性能。
8、trace:

回显服务器收到的请求,主要用于测试或诊断。

18.JS有几种方法判断变量的类型?⭐⭐⭐

typeof
判断基本数据类型,对于引用数据类型除了function返回’function‘,其余全部返回’object’。
instanceof
区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。
constructor
检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
Object.prototype.toString.call()
适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。(举例:字符串返回的是[object String])

19、vue中的修饰符

1、表单修饰符

<1> .lazy
默认情况下,v-model同步输入框的值和数据。可以通过这个修饰符,转变为在change事件再同步。

2、事件修饰符

.stop 阻止事件继续传播
.prevent 阻止标签默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件将只会触发一次
.passive 告诉浏览器你不想阻止事件的默认行为

使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用v-on:click.prevent.self会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。

3、鼠标按键修饰符
<button @click.left="shout(1)">ok</button> //左键
<button @click.right="shout(1)">ok</button> //右键
<button @click.middle="shout(1)">ok</button> //中键
4、键值修饰符

put @keyup.enter=“submit”>
1
全部的按键别名:

.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
修饰键:

.ctrl
.alt
.shift
.meta

5、v-bind修饰符

sync
实现子组件props的双向绑定

//父组件

//父亲组件
<comp :myMessage=“bar” @update:myMessage=“func”>
func(e){
this.bar = e;
}
//子组件js
func2(){
this.$emit(‘update:myMessage’,params);
}
使用sync的时候,子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中声明的名称完全一致

注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用

将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的

props
设置自定义标签属性,避免暴露数据,防止污染HTML结构

20、Vuex的五个核心概念:

state、getters、mutations、actions、modules

1、state: vuex的基本数据,用来存储变量;
2、getters: 从基本数据(state)派生的数据,相当于state的计算属性;
3、mutations: 提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mutation 都有一个字符串的事件类型(type)和一个回调函数(handler)。
回调函数就是我们实际进行状态更改的地方,并且它会接受 state作为第一个参数,提交载荷作为第二个参数。
4、action: 和mutation的功能大致相同,不同之处在于 ①Action提交的是mutation,而不是直接变更状态,②Action可以包含任意异步操作。
5、modules: 模块化vuex,可以让每一个模块拥有自己的 state、mutation、action、 getters,使得结构非常清晰,方便管理。

21、V8引擎的垃圾回收采用标记清除法与分代回收法

​ 1.引用计数算法:记录每个变量值被使用的次数,如果为0,就销毁,回收其所占用的内存空间

  2.标记清除:标记清除,标记整理(解决内存碎片,合理分配内存),

  3.分代回收:针对不同的对象(新生代---存活时间短和老生代)采用不同的算法

      新生代:复制算法(Scavenge 算法)加标记整理算法:对象区域和空闲区域无限重复使用

JavaScript 引擎采用了对象晋升策略,也就是经过两次垃圾回收依然还存活的对象,会被移动到老生区中。

       老生代:标记清除,标记整理(解决内存碎片,合理分配内存)

22、es6函数新增

  1. 新增参数默认值

    function add(n1=0,n2=100){
            return n1+n2;
        }
        console.log(add())   // 输出100
    
  2. 新增args参数

    args参数可代替arguements,arguements是一个类数组对象,args是纯数组

    function add(...args){  //将数组或类数组转成以逗号隔开的列表
            console.log(args);
            let sum = 0;
            args.forEach(function(item){
                sum += item;
            })
            return sum;
        }
        console.log(add(1,2,3,4,5));
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pRvcg0l-1669815959115)(C:\Users\wzwde\AppData\Roaming\Typora\typora-user-images\image-20221128121650779.png)]

  3. 新增箭头函数

23、类数组

什么是类数组

长的像数组的对象,类数组又叫伪数组

类数组和数组的区别

1.都有length属性

2.都可以for循环遍历,有的类数组还可以通过for of遍历

3.类数组不具备数组的原型方法,不可以调用相关数组方法(如,push、slice、concat…)

类数组转换为真正的数组

  1. 遍历类数组,依次将元素放入一个空数组。

  2. 用扩展运算符或者Array.from()方法转换

    es6新增了扩展运算符(…)以及Array.from()方法,可以直接将类数组转换为真正的数组。

    关于扩展运算符以及Array.from()的详细用法,可参考阮一峰老师的《es6标准入门》一书。

  3. 利用apply展开

    apply方法的第二个参数是数组,也可以是类数组,在调用的时候会将第二个参数依次展开。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJjc3wDj-1669815959115)(C:\Users\wzwde\AppData\Roaming\Typora\typora-user-images\image-20221128122602116.png)]

24、什么是严格模式

  1. 在严格的条件下允许 JS 代码。
  2. 消除了 JavaScript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
  3. 提高编译器效率,增加运行速度。
  4. 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 JavaScript 做好铺垫。比如一些保留字,如:class、enum、export、extends、import、super不能做变量名。

25、map和forEach有什么区别

  1. forEach()方法没有返回值,而map()方法有返回值。

  2. forEach遍历通常都是直接引入当前遍历数组的内存地址,生成的数组的值发生变化,当前遍历的数组对应的值也会发生变化。

  3. map遍历的后的数组通常都是生成一个新的数组,新的数组的值发生变化,当前遍历的数组值不会变化。

  4. 总的来说 map 的速度大于forEach。

26、如何给一个DOM元素绑定两个点击事件

通过事件监听进行绑定:addEventListener

27、一个冒泡一个捕获在同一个绑定对象先执行哪个

绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。

27、JQuery中的$(document).ready与window.onload有什么区别

  1. 执行时间

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYlUsHjx-1669815959115)(C:\Users\wzwde\AppData\Roaming\Typora\typora-user-images\image-20221128123524242.png)]

    window.onload必须等到页面内包括图片的所有元素和资源加载完毕后才能执行,也就是上述图片的时间点2;
    $(document).ready()是DOM加载完毕后就执行,不必等到整个网页资源加载完毕,也就是在上述图片的时间点1。
    所以,使用document.ready()方法的执行速度比window.onload的方法要快。

  2. 编写个数不同

    $(document).ready()可以同时编写多个,并且都可以得到执行
    window.onload不能同时编写多个,如果有多个window.onload方法,只会其中执行一个

  3. 简化写法

    window.onload没有简化写法
    $ (document).ready(function(){ })可以简写成$(function(){方法体 });又因为JQuery的默认参数是document,则还可以写成$().ready(function{ })

28、描述一下原型和原型链

一切对象都是继承来自Object对象,Object对象直接继承根源对象null

一切的函数对象(包括 Object对象)都是继承自Function对象

Object对象直接继承自Function对象

Function对象的__proto__会指向自己的原型对象,最终还是继承Object对象

29、ES6怎么实现继承

  1. 在定义类的时候使用extends继承父类把类方法和实例方法继承下来
  2. 在constructor中使用super调用父类调用父类

30、async/await和Promise有什么关系

await必须在async内使用,并装饰一个Promise对象,async返回的也是一个Promise对象。

async封装Promise,await相当then,try…catch相当于catch

31、bind、call、apply有什么区别

  • 三者都可以改变函数的this对象指向
  • 三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefinednull,则默认指向全局window
  • 三者都可以传参,但是apply是数组,而call是参数列表,且applycall是一次性传入参数,而bind可以分为多次传入
  • bind 是返回绑定this之后的函数,apply call 则是立即执行

call apply bind 作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向

32、computer和watch区别

计算属性computed :

  1. 支持缓存,只有依赖数据发生改变,才会重新进行计算

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

​ 3.computed 属性值会默认走缓存,计算属性是基于它们的响应 式依赖进行缓存的,也就是基于data中声明过的数据通过计算得 到的

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

​ 5.如果computed属性属性值是函数,那么默认会走get方法;函 数的返回值就是属性的属性值;在computed中的,属性都有一 个get和一个set方法,当数 据变化时,调用set方法。

监听属性watch:

  1. 不支持缓存,数据变,直接会触发相应的操作;

​ 2.watch支持异步;

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

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

33、vuex5个方法以及如何同步异步

1.state:vuex的基本数据,用来存储变量

2. geeter:从基本数据(state)派生的数据,相当于state的计算属性,具有返回值的方法

3. mutation:提交更新数据的方法,必须是同步的

4. action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作

5. modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理

 同步:组件中使用mapMutations方法或者this.$store.commit('JIAN',this.n)

 异步:this.$store.dispatch('aaddper',count)

34、说说对$nextTick的理解

我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新

Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数

35、说说对vue中keep-alive的理解

在平常开发中,有部分组件没有必要多次初始化,这时,我们需要将组件进行持久化,使组件的状态维持不变,在下一次展示时,也不会进行重新初始化组件。

也就是说,keepalive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染,也就是所谓的组件缓存。

是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。

使用了keep-alive的组件以后,组件上就会自动加上了activated钩子和deactivated钩子。

activated 当组件被激活(使用)的时候触发 可以简单理解为进入这个页面的时候触发

deactivated 当组件不被使用(inactive状态)的时候触发 可以简单理解为离开这个页面的时候触发

36、v-model原理

v-model只不过是一个语法糖而已,真正的实现靠的还是

v-bind:绑定响应式数据
触发oninput 事件并传递数据

37、谈谈this的理解,指向对象有哪些

this 关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象。
this 的情况:
1、以函数形式调用时,this 永远都是 window
2、以方法的形式调用时,this 是调用方法的对象
3、以构造函数的形式调用时,this 是新创建的那个对象
4、使用 call 和 apply 调用时,this 是指定的那个对象
5、箭头函数:箭头函数的 this 看外层是否有函数 如果有,外层函数的 this 就是内部箭头函数的 this 如果没有,就是 window
6、特殊情况:通常意义上 this 指针指向为最后调用它的对象。这里需要注意的一点就是 如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例

38、for in 和for of的区别

 for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
 for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
 对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
总结: for…in 循环主要是为了遍历对象而生,不适用于遍历数组;for…of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。

40、instanceof

instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
区别:
typeof与instanceof都是判断数据类型的方法
• typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
• instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
• 而 typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断
如果需要通用检测数据类型,可以采用Object.prototype.toString,调用该方法,统一返回格式“[object Xxx]” 的字符串。

41、堆和栈的区别

1.申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;
2.申请大小的不同。栈获得的空间较小,而堆获得的空间较大;
3.申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;
4.存储内容的不同。栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;
5.底层不同。栈是连续的空间,而堆是不连续的空间。
概述
栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。基本类型:String,Number,Boolean,Null,Undefined。
堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。引用类型:Function,Array,Object

42、forEach中return有效果吗?如何中断forEach循环?

在forEach中用return不会返回,函数会继续执行。
中断方法:
• 使用try监视代码块,在需要中断的地方抛出异常。
• 官方推荐方法(替换方法):用every和some替代forEach函数。
o every在碰到return false的时候,中止循环。
o some在碰到return true的时候,中止循环。

43、什么是内存泄漏,什么原因会导致

​ ①.程序中动态分配的堆内存由于某种原因未释放或无法释放
​ 什么原因:全局变量过多、事件监听未移除、缓存

44、Ajax原理

​ 通过xmlhttprequest对象来向服务器发送异步请求,从服务器获得数据,然后用js来操作dom而更新页面

1、ajax步骤:

1、创建XMLHttpRequest异步对象
2、设置回调函数
3、使用open方法与服务器建立连接
4、向服务器发送数据
5、在回调函数中针对不同的响应状态进行处理

2、ajax 的5个状态值

  • 0:(未初始化) 还没有调用open()方法。
  • 1:(启动) 已经调用open()方法,但还没有调用send()方法。
  • 2:(发送) 已经调用send()方法,但还没有接收到响应。
  • 3:(接收) 已经接收到部分响应数据。
  • 4:(完成) 已经接收到全部的响应数据,且可以在客户端使用了。

3、ajax的状态码

5大类:

100+:表示连接继续
200+:操作成功收到,分析、接受
300+:完成此请求必须进一步处理,重定向
400+:表示各种客户端错误
500+:表示各种服务器错误

45、vue的组件通信

​ 子传父:
​ 1. e m i t 自 定 义 事 件 , 第 二 个 值 为 数 据 , 父 组 件 绑 定 监 听 器 获 取 参 数 的 数 据 ​ 2. r e f 父 组 件 在 子 组 件 时 设 置 r e f 通 过 t h i s . emit自定义事件,第二个值为数据,父组件绑定监听器获取参数的数据 ​ 2.ref 父组件在子组件时设置ref 通过this. emit2.refrefthis.refs.xxx获取数据
​ 父传子:
​ 1.子组件设置props属性,定义接收父组件传递来的参数 父组件通过字面量传递值
​ 2.$attrs$listeners 可传不被prop识别的特性绑定 可通过v-bind传入内部组件
​ 非父子:
​ 1.EventBus总线程
​ 2. p a r e n t 、 parent、 parentroot 需要共同祖辈组件
​ 3.provide和inject 祖先组件定义provide属性返回传递的值 后代组件通过inject接收

46、 r o u t e 和 route和 routerouter区别

​ 1、 r o u t e r 是 用 来 操 作 路 由 , router 是用来操作路由, routerroute是用来获取路由信息
​ 2、 r o u t e r 是 V u e R o u t e r 的 一 个 实 例 , 他 包 含 了 所 有 的 路 由 , 包 括 路 由 的 跳 转 方 法 , 钩 子 函 数 等 , 也 包 含 一 些 子 对 象 ( 例 如 h i s t o r y ) ​ 3 、 router 是VueRouter的一个实例,他包含了所有的路由,包括路由的跳转方法,钩子函数等,也包含一些子对象(例如history) ​ 3、 routerVueRouterhistory3route 是一个跳转的路由对象(路由信息对象),每一个路由都会有一个 r o u t e 对 象 , 是 一 个 局 部 的 对 象 。 ​ 4 、 route对象,是一个局部的对象。 ​ 4、 route4router是访问路由器而$route是访问当前路由

47、vue中hash和history区别

​ 兼容性hash(ie8)比较与history(ie10)更好
​ 外观history比较好看
​ history每次刷新会重新像后端请求整个网址,也就是重新请求服务器,如果后端没有及时响应,就会报错404
​ history的好处是可以进行修改历史记录,并且不会立刻像后端发起请求

48、arguments转化为数组的方法

1、Array.prototype.slice.apply(arguments)

2、使用ES6的新语法 Array.from() 来转换

3、使用 for 循环挨个将 arguments 对象中的内容复制给新数组中

49、同步和异步操作

同步:代码按照顺序一行一行执行。

**异步:**将用户请求放入消息队列,等同步执行完成以后再执行异步。

异步的操作有:axios,定时器,promise,事件监听,回调函数

50、e.target和e.currentTarget的区别

**e.target:****触发事件的元素

**e.currentTarget:**绑定事件的元素

51、闭包的垃圾回收机制?

垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行

53、cookie、sessionStorage、localStorage的区别⭐⭐⭐

保存方式
cookie存放在客户的浏览器上。
session都在客户端中保存,不参与服务器通讯。

生命周期
cookie可设置失效时间
localStorage除非手动清除否则永久保存
sessionStorage关闭当前页面或浏览器后失效

存储的大小
cookie 4kb左右
session 5M

易用性
cookie需自己封装
session可以接受原生接口

因为cookie每次请求都会携带在http请求中,所以它的主要用来识别用户登录,localStorage可以用来跨页面传参,sessionStorage可以用来保留一些临时数据。

54、数组的方法?

Push()、pop()、shift()、unshift()、slice()、splice()、jion()、concat、

IndexOf()、includes()、sort()reverse()、map()、filter()lastIndexO()

55、解决跨域问题

什么是跨域?

浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
常见的:

1、JSONP跨域

原理:利用script标签可以跨域请求资源,将回调函数作为参数拼接在url中。后端收到请求,调用该回调函数,并将数据作为参数返回去,注意设置响应头返回文档类型,应该设置成javascript。

2、跨域资源共享(CORS)

目前最常用的一种解决办法,通过设置后端允许跨域实现。

3、nginx反向代理

跨域限制的时候浏览器不能跨域访问服务器,node中间件和nginx反向代理,都是让请求发给代理服务器,静态页面面和代理服务器是同源的,然后代理服务器再向后端服务器发请求,服务器和服务器之间不存在同源限制。

4、WebSocket协议跨域

5、proxy

前端配置一个代理服务器(proxy)代替浏览器去发送请求:因为服务器与服务器之间是可以通信的不受同源策略的影响。
详细可看九种常见的前端跨域解决办法

56、下面代码的输出是什么?

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('timer')
    resolve('success')
  }, 1000)
})
const start = Date.now();
promise.then(res => {
  console.log(res, Date.now() - start)
})
promise.then(res => {
  console.log(res, Date.now() - start)
})

这里 Promise 构造函数只执行一次

结果:'timer ';‘success’ 1001 ; ‘success’ 1002

57、什么是Polyfill

polyfill(polyfiller),指的是一个代码块。这个代码块向开发者提供了一种技术, 这种技术可以让浏览器提供原生支持,抹平不同浏览器对API兼容性的差异。

(Polly 指的是用于实现浏览器并不支持的原生 API 的代码。)

58、Promise中的值穿透

解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。

.then :当then中传入的不是函数,则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。而且每一个无效的then所返回的promise的状态都为resolved

.catch :当使用.catch时,会默认为没有指定失败状态回调函数的.then添加一个失败回调函数(上文中有具体函数代码)。 .catch所谓的异常穿透并不是一次失败状态就触发catch,而是一层一层的传递下来的。 异常穿透的前提条件是所有的.then都没有指定失败状态的回调函数。 如果.catch前的所有.then都指定了失败状态的回调函数,.catch就失去了意义。

59、vue2响应式和vue3响应式的区别

  • Vue2.0

  1. 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
  2. Object.defineProperty 无法检测到对象属性的添加和删除 。
  3. 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
  4. 深度监听需要一次性递归,对性能影响比较大。
  • Vue3.0

  1. 基于Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除。

  2. 不需要一次性遍历data的属性,可以显著提高性能。

  3. 因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11 。

  4. 通过new Proxy()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

    4.1发布者-订阅者模式是什么

    JavaScript设计模式(5)——发布者-订阅模式_闲人王昱珩的博客-CSDN博客

    [观察者模式和发布订阅模式的区别 - 简书 (jianshu.com)](

60、箭头函数与普通函数的区别

1、外形不同:箭头函数使用箭头定义,普通函数中没有。
2、 箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
3、箭头函数不能用于构造函数:普通函数可以用于构造函数,以此创建对象实例。
4、箭头函数中 this 的指向不同:在普通函数中,this 总是指向调用它的对象,如果用作构造函数,它指向创建的对象实例。

5、箭头函数没有自己的this,会捕获其所在的上下文的this值,并且不能通过call()和apply()来改变其this

6、箭头函数不具有 arguments 对象:每一个普通函数调用后都具有一个arguments 对象,用来存储实际传递的参数。但是箭头函数并没有此对象。
7、其他区别:箭头函数不具有 prototype 原型对象。箭头函数不具有 super。
箭头函数不具有 new.target

61、jQuery和vue各自的优势

答:
jQuery:简化dom操作,对dom操作变得更简单方便
vue:减少dom操作、双向数据绑定,把操作dom转变为操作数据,通过数据渲染dom;组件化

62、对aysnc、await的理解

答:
async用来声明一个函数是异步的,而await是等待这个异步方法执行完毕
async函数会返回一个promise对象
async与await的优势在于处理多条promise链式调用

63、深拷贝和浅拷贝的区别,如何实现

浅拷贝:

创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

1、Object.assign()
2、函数库lodash的 _.clone 方法
3、es6的展开运算符 …
4、Array.prototype.concat()
5、Array.prototype.slice()

深拷贝:

将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

总而言之,浅拷贝改动拷贝的数组原数组也会变(慎用!项目中很多地方共用的数组都会变)。深拷贝修改新数组不会改到原数组。
实现方法

  1. JSON.parse(JSON.stringify())
  2. 函数库lodash的 _.cloneDeep 方法
  3. **jQuery.extend()**方法
  4. 手写递归方法(转)

64、$.ready和window.onload的区别

答:$.ready,在Dom解构加载完毕后就执行;而window.onload是在,页面加载完毕(包括图片)才会执行。
. r e a d y 执 行 效 率 比 w i n d o w . o n l o a d 更 高 。 w i n d o w . o n l o a d 会 覆 盖 , 只 执 行 一 个 , .ready执行效率比window.onload更高。 window.onload会覆盖,只执行一个, .readywindow.onloadwindow.onloadready不会覆盖,会根据代码执行顺序依次执行。

65、Promise中断

把pending状态的promise给reject

网络请求设置超时时间,一旦超时就中断

66、图片懒加载

通过判断每张图片的offsetTop是否小于视口的高度和滚动条的scrollTop的和,判断是否进入可视区域,如果进入可视区域就进行显示

67、字符串的常用方法:

拼接 concat()

切割 slince() substr() substring() split()

​ slince() 起始下标和结束下标 可正可负

​ substr() 起始下标和截取长度

​ substring() 起始下标和结束下标

​ split() 按照指定分隔符,拆分成数组中的每一项

复制 repeat() 接收一个整数参数

替换 replace()

删除前后空格符 trim()

查找 charAt() indexOf()

68、什么是扩展运算符?

  1. 对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中

     let bar = { a: 1, b: 2 };
     let baz = { ...bar }; // { a: 1, b: 2 }
    

69、什么是箭头函数?

箭头函数不会创建自己的this

箭头函数继承而来的this指向永远不变

.call()/.apply()/.bind()无法改变箭头函数中this的指向

箭头函数不能作为构造函数使用

箭头函数没有自己的arguments

箭头函数没有原型prototype

70、4钟字符串的切割方法

1、slice(start, end)

2、substr(start, length)

3、substring(start, stop)

4、split(separator, length)

5、join(separator)

6、splice(start, length, …args)

71、怎么在setTimerout实现setIterval的功能

setInterval是指定时间间隔后重复执行指定回调,所以可以使用递归方式重复调用setTimeout

function intervalFn(fn, ms) {
   function inner() {
        fn();
        setTimeout(inner, ms);
    }
    setTimeout(inner, ms);  // 第二个setTimeout
}

intervalFn(fn, 1000);

function fn() {
	console.log(666)
}

72、什么是原型链

主要是还是实现继承与扩展对象。
每个函数对象都有一个 prototype 属性,这个属性就是函数的原型对象。
原型链是JavaScript实现继承的重要方式,原型链的形成是真正是靠__proto__ 而非prototype。

所有的引用类型(包括数组,对象,函数)都有隐性原型属性(proto), 值也是一个普通的对象。
所有的引用类型的 proto 属性值都指向构造函数的 prototype 属性值。
构造函数 new 出来一个对象,而每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数。
实例对象通过__proto__或者 object.getPrototype 的方法获取原型。
原型链其实就是有限的实例对象和原型之间组成有限链,就是用来实现共享属性和继承的。

73、bus总线程与vuex的区别

74、async/await的原理

当调用一个 async 函数时,会返回一个 Promise 对象 (关键)
async 函数中可能会有 await 表达式,await表达式 会使 async 函数暂停执行,直到表达式中的Promise解析完成后继续执行 async 中 await 后面的代码并返回解决结果。
既然返回的是Promise 对象,所在最外层不能直接获取其返回值,那么肯定可以用原来的方式:then() 链来处理这个 Promise 对象
原理:
async/await 函数其实就是一种语法糖
async/await 是基于promise实现的,async 函数其实就是把 promise 做了一个包装
await 返回值是一个 Promise 对象,它只是把 await 后面的代码放到了 Promise.then()

  • promise 使用 .then 链式调用,但也是基于回调函数
  • async/await 更加优雅的异步编程的写法
    1.它是消灭异步回调的终极武器
    2.它是同步语法,也就是用同步的写法写异步的代码

75、插槽的理解

  1. 作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件

  2. 分类:默认插槽、具名插槽、作用域插槽

76、判断一个对象是否是空对象?

方法一:将对象转换成字符串,再判断是否等于“{}”

let obj={};
console.log(JSON.stringify(obj)==="{}");

方法二:for in循环

let result=function(obj){
    for(let key in obj){
        return false;//若不为空,可遍历,返回false
    }
    return true;
}
console.log(result(obj));//返回true

方法三:Object.keys()方法,返回对象的属性名组成的一个数组,若长度为0,则为空对象(ES6的写法)

console.log(Object.keys(obj).length==0);//返回true

方法四:Object.getOwnPropertyNames方法获取对象的属性名,存到数组中,若长度为0,则为空对象

console.log(Object.getOwnPropertyNames(obj).length==0);//返回true

78、es5和es6中class的区别

一、class类必须new调用,不能直接执行。

二、class类不存在变量提升

三、class类无法遍历它实例原型链上的属性和方法

四、new.target属性

es6为new命令引入了一个new.target属性,它会返回new命令作用于的那个构造函数。如果不是通过new调用或Reflect.construct()调用的,new.target会返回undefined

五、class类有static静态方法

static静态方法只能通过类调用,不会出现在实例上;另外如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例。static声明的静态属性和方法都可以被子类继承。

79、es6新增的数组方法

1、Array.from()

Array.from 方法用于将两类对象转为真正的数组:

  • 类似数组的对象(array-like object)
  • 可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)

2、Array.of()

Array.of 方法用于将一组值,转换为数组。弥补数组构造函数 Array()的不足。因为参数个数的不同,会导致 Array()的行为有差异

3数组实例的 find() 和 findIndex()

find()

返回第一个符合条件的数组成员,它的参数是一个回调函数,所有数组成员依次执行该函数,直到找出第一个满足条件的成员,然后返回该成员,如果没有符合条件的成员,则返回undefined

该方法的回调函数接收三个参数: 当前的值 , 当前位置, 原数组

findIndex()

写法用法基本与find()方法相同,只是返回第一个符合条件的数组成员的位置,如果没有则返回-1

4、数组实例的 fill()

  • fill()方法使用给定值, 填充一个数组
  • fill 方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置

5、数组实例的 entries(),keys() 和 values()

三个方法都用来遍历数组,都返回一个遍历器对象,可用for…of循环遍历

不同的是:

  • keys()是对键名的遍历
  • values()是对键值的遍历
  • entries()是对键值对的遍历

6、includes()方法返回一个布尔值

该方法返回一个布尔值,表示某个数组中是否包含给定的值

[1, 2, 3].includes(2) // true[(1, 2, 3)].includes(4) // false

7、数组实例的 flat(),flatMap()

  • flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。传参数代表拉平几层默认是一层
  • flatMap()只能展开一层数组。方法对原数组的每个成员执行一个函数(相当于执行 Array.prototype.map()),然后对返回值组成的数组执行 flat()方法。该方法返回一个新数组,不改变原数组

8、数组实例的copywithin()

在当前数组内部将指定位置的成员复制到其他位置,然后返回当前数组,会改变原数组

接收三个参数:

1、target(必需) 从该位置开始替换数据

2、start(可选) 从该位置开始读取数据,默认为0,如果为负数,则表示到数

3、end(可选) 到该位置前停止读取数据,默认等于数组长度。如果是负数,表示到数

三个参数都应该是数字,如果不是,会自动转为数值

80、性能优化

举例:

  1. 减少http请求次数
  2. 减少DNS查找
  3. 避免重定向
  4. 使用Ajax缓存
  5. 少用全局变量、减少DOM操作的使用
  6. 优化图片大小,通过CSS Sprites(精灵图)优化图片,
  7. 将css放在顶部,将js放在底部

81、首屏加载优化方案

1、vue-router 路由[懒加载]

2、使用CDN加速,将通用库从vendor进行抽离

3、Nginx开启Gzip功能

4、异步组件

5、服务端渲染SSR

6、按需加载UI库

7、webpack开启gzip压缩

8、图片懒加载减少占用带宽

9、页面使用骨架屏

10、js脚本使用异步和延迟

82、事件与事件流

JavaScript 中每个元素都可以产生触发JavaScript 函数的事件。我们可以认为事件可以被JavaScript侦测到的一种行为。

而接收事件的次序称为事件流。 事件流总共有两种形式:事件冒泡事件捕获

事件冒泡流:具体触发事件的元素先接收事件,然后向上传导

事件捕获流:与冒泡流相反,最外层先接收到事件

事件流有三个阶段:事件捕获阶段---->处于目标阶段---->事件冒泡阶段

83、事件模型

事件模型可以分为三种:

  • 原始事件模型(DOM0级)
  • 标准事件模型(DOM2级)
  • IE事件模型(基本不用)

原始事件模型

  • HTML代码中直接绑定
<input type="button" onclick="fun()">
  • 通过JS代码绑定
var btn = document.getElementById('.btn'); btn.onclick = fun;
特性
  • 绑定速度快

DOM0级事件具有很好的跨浏览器优势,会以最快的速度绑定,但由于绑定速度太快,可能页面还未完全加载出来,以至于事件可能无法正常运行

  • 只支持冒泡,不支持捕获
  • 同一个类型的事件只能绑定一次

2、标准事件模型

在该事件模型中,一次事件共有三个过程:

  • 事件捕获阶段:事件从document一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
  • 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数
  • 事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

事件绑定监听函数的方式如下:

addEventListener(eventType, handler, useCapture)

事件移除监听函数的方式如下:

removeEventListener(eventType, handler, useCapture)

参数如下:

  • eventType指定事件类型(不要加on)
  • handler是事件处理函数
  • useCapture是一个boolean用于指定是否在捕获阶段进行处理,一般设置为false与IE浏览器保持一致

举个例子:

var btn = document.getElementById('.btn'); 
btn.addEventListener(‘click’, showMessage, false); 
btn.removeEventListener(‘click’, showMessage, false);
特性
  • 可以在一个DOM元素上绑定多个事件处理器,各自并不会冲突

3、IE事件模型

IE事件模型共有两个过程:

  • 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
  • 事件冒泡阶段:事件从目标元素冒泡到document, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行

事件绑定监听函数的方式如下:

attachEvent(eventType, handler)

事件移除监听函数的方式如下:

detachEvent(eventType, handler)

84、尾递归

尾递归,即在函数尾位置调用自身(或是一个尾调用本身的其他函数等等)。尾递归也是递归的一种特殊情形。尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数

尾递归在普通尾调用的基础上,多出了2个特征:

  • 在尾部调用的是函数自身
  • 可通过优化,使得计算仅占用常量栈空间

在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出

这时候,我们就可以使用尾递归,即一个函数中所有递归形式的调用都出现在函数的末尾,对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误

实现一下阶乘,如果用普通的递归,如下:

function factorial(n) {  if (n === 1) return 1;  return n * factorial(n - 1); } factorial(5) // 120

如果n等于5,这个方法要执行5次,才返回最终的计算表达式,这样每次都要保存这个方法,就容易造成栈溢出,复杂度为O(n)

如果我们使用尾递归,则如下:

function factorial(n, total) {  if (n === 1) return total;  return factorial(n - 1, n * total); } factorial(5) // 120

可以看到,每一次返回的就是一个新的函数,不带上一个函数的参数,也就不需要储存上一个函数了。尾递归只需要保存一个调用栈,复杂度 O(1)

85、js本地储存的方法

与服务器交互
cookie:是网站为了标识用户的身份而存储在用户本地终端上的数据(通常需要加密),通常会在 同源 的 HTTP请求中携带(即使不需要),在浏览器和服务器之间来回传递。cookie还有路径的概念,可以控制存储在某个路径下面。

localStorage & sessionStorage: 不会自动把数据发给服务器,仅在本地存储

存储大小
cookie:根据不同浏览器的限制,大小一般不能超过4k,因为每次 HTTP 请求 都会携带

localStorage& sessionStorage 虽然也有存储大小的限制,但比 cookie大得多,可以达到5M或更大

存储时间
cookie:在设置 cookie 的有效期之前,是一直生效的,与浏览器页面是否关闭无关

localStorage:储存持久数据,浏览器页面关闭数据也不会丢失,除非手动清除数据

sessionStorage:数据在浏览器关闭之后自动清空

作用域不同
cookie:在所有的同源窗口内,数据是共享的

localStorage:也是在所有的同源窗口内,数据是共享的

sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面

86、对promise的理解

含义:异步编程的一种解决方案,用来解决回调地狱。
三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败) (Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。)

resolved函数作用:将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)。
reject函数的作用:将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then: Promise 实例添加状态改变时的回调函数。可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。

缺点:无法取消Promise,一旦新建它就会立即执行,无法中途取消。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

87、你是怎么理解ES6中Proxy的?

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”

88、Vue3与Vue2的区别

1、响应式数据

2、vue2和vue3的响应式原理

3、计算属性Computed

4、监听函数Watch

​ vue3中watch函数对ref的响应式数据的监听

​ vue3中watch函数对reactive的响应式数据的监听

5、vue3的生命周期

6、hooks函数

7、toRef和toRefs

8、readonly与shallowReadonly

9、toRaw与MarkRaw

10、组合式API的优势
https://blog.csdn.net/qq_38329392/article/details/126278088移动端点击穿透问题

89、点击穿透

原因:

点击蒙层上的关闭按钮,蒙层消失后触发了按钮下面元素的click事件
如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转
这次没有蒙层,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了.
解决方法:
最优解决方法——只用touch
最简单的解决方案,完美解决点击穿透问题
把页面内所有click全部换成touch事件( touchstart、touchmove、touchend)

90、ssr是什么

Server-Side Rendering 我们称其为SSR,意为服务端渲染

指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程

91、对es6新增的reflect对象的了解

92、null与undefined的区别

1、首先是数据类型不一样

2、null和undefined两者相等,但是当两者做全等比较时,两者又不等。(因为他们的数据类型不一样)

3、转化成数字不同

4、null代表“空”,代表空指针;undefined是定义了没有赋值

93、this指向的理解

  1. this总是指向函数的直接调用者(而非间接调用者)

  2. 如果有new关键字,this指向new出来的那个对象

  3. 在事件中,this指向目标元素,特殊的是IE的attachEvent中的this总是指向全局对象window

函数类型this的指向
普通函数指向window
匿名函数指向window
立即执行函数window
回调函数window
箭头函数函数定义位置的上下文this
对象下的函数谁调用,指向谁
dom回调绑定事件的对象

94、什么是递归函数

一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。

一句话总结递归:自我调用且有完成状态

95、什么是模板字符串

模板字符串只是在JS 中创建字符串的一种新方法。我们可以通过使用反引号使模板字符串化

96、在es5中怎么实现继承

1、原型链继承

2、借用构造函数继承

3、组合继承

4、寄生式组合继承

97、es5与es6的区别

1.es6与es5的类不同

es6引入class,写起来更符合面向对象的编程思维,实际上底层实现的逻辑是一样的。但会极大的简化代码,看起来更加清晰。es6的类有暂时性死区,不可变量提升,因为es6的类必须声明之后才能使用。而es5中不是如此

另外继承类也不同,es6中继承的写法更符合java等面向对象的语言,使用extends关键字继承。而es5中继承写起来比较复杂,不够简洁。

补充点:

es6的类中必须有构造函数,构造函数中可以给予实例属性,另外还有原型方法。在es5中需这样实现:

var Animal=function(属性1,属性2){}

Animal.prototype = {method1:function(){},method2:function(){}}//原型方法

Animal.method3 = function(){}//静态方法

在es6中这样实现

class Animal = {

constructor(){}

method1(){}//原型方法

method2(){}//原型方法

}

2.es6中有promise,以及同步异步async await,可将多层嵌套的回调函数 改为 链式执行,看起来清晰明白

3.增加let,const数据类型,在块级作用域中有效,即必须包含在大括号里,且不可变量提升,即必须声明之后再使用 (var声明的变量与函数声明会被提升到顶部,因此函数与var的变量可在声明之前使用)

什么是块级作用域?就是在大括号里的就是块级作用域,{},比如for循环里。let非常适合for循环,for循环中使用var声明变量,容易受for之外的影响,而影响到数据的正确性。而在for中声明的let变量,只在for循环中有效,且每次for循环的let的变量都是一个新的变量 ,所以可保证数据不受其他影响

作用域:js中原来只有两种作用域,函数作用域和全局作用域, ES6中增加了块级作用域,适用于let和const

4.箭头函数,es6中函数不用写function关键字,用()=>{}就行。

es5中函数写法为

function a(){}

或者

var a = function(){}

es6中可改为箭头写法

var a = (属性1,属性2)=>{代码块} //若代码块中只有一行代码,则大括号{}可以省去

5.es6中可一次性取出数组或object的多个值

var student= {age:18,sex:‘男’,name:‘name1’}

let {age,sex,name} = student

6.函数有默认参数的值,es5中无法设置默认值

function f(p1,p2,p3=false,p4=‘none’){}

7.模版字符串,用反引号标识、、,最主要可以加入变量或表达式,不用再一直用++++来连接字符串了

模板字符串相当于加强版的字符串,用反引号 ,除了作为普通字符串,还可以在字符串中加入变量和表达式。 let info = my name is ${name}`

98、bus是什么以及如何实现通讯

bus是一种通过事件实现组件交互的通信模式,它借助一个额外的Vue实例作为事件管理中心。任何引入了该Vue实例的组件都处于同一个事件环路内,可以相互注册和触发事件12.什么是事件代理。

  • 通过$emit触发自定义事件,$emit第二个参数为传递的数值
  • 另一个组件通过$on监听自定义事件

99、什么是严格模式

严格模式对正常的 JavaScript 语义做了一些更改:

1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。

2.消除代码运行的一些不安全之处,保证代码运行的安全。

3.提高编译器效率,增加运行速度。

4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class,enum,export, extends, import, super 不能做变量名1.js中数组是如何在内存中存储的?

数组不是以一组连续的区域存储在内存中,而是一种哈希映射的形式。它可以通过多种数据结构来实现,其中一种是链表。

js分为基本类型和引用类型:

  • 基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问;
  • 引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用

100、map和filter有什么区别?

相同点:filter 和 map 都是对数组的操作,均返回一个新的数组
不同点:filter是满足条件的留下,是对原数组的过滤;map则是对原数组的加工,映射成一对一映射的新数组

101、vue模板是如何编译的?

102、如何实现页面跨组件通信?

1、父组件向子组件传递数据 props

2、子组件向父组件传递数据

父: @自定义事件名=“父methods函数”

子: this.$emit(“自定义事件名”, 传值) - 执行父methods里函数代码

$emit绑定一个自定义事件(subprice), 当这个语句被执行时, 就会将参数(index与价格)传递给父组件,父组件通过v-on监听并接收数

3、provide/inject

4、ref/refs

**ref:**如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据

5、eventBus 事件中心管理组件间的通信

6、localStorage/sessinStorage

7、 a t t r s / attrs/ attrs/listeners

8、Vuex

10.bus是什么以及如何实现通讯

12.什么是事件代理
13.什么是严格模式

103、堆栈的区别

堆(heap)是不连续的内存区域,即数据可以任意存放, 主要存放的是对象等。

栈(stack)是一块连续的内存区域,每个区块按照一定次序存放(后进先出),栈中主要存放的是基本类型的变量的值以及指向堆中的

数组或者对象的地址。

104、js为什么存在数据精度丢失问题

由于计算机的底层是由二进制实现的,有些运算的数字无法全部显示出来。就像一些无理数不能完全显示出来一样,如圆周率 3.1415926…,0.3333… 等。JavaScript遵循IEEE754规范,采用双精度存储(double precision),占用64bit。

1位用来表示符号位,11位用来表示指数,52位表示尾数。

因为在计算机最底层,数值的运算和操作都是采用二进制实现的,所以计算机没有办法精确表示浮点数,而只能用二进制近似相等的去表示浮点数的小数部分

105、如何实现异步传值

106、三次握手,四次挥手

含义:从建立连接到断开连接一共七个步骤,就是三次招手四次挥手

  1. TCP 建立连接
  2. 浏览器发送请求命令
  3. 浏览器发送请求头
  4. 服务器应答
  5. 服务器回应信息
  6. 服务器发送数据
  7. 断开TCP连接

107、路由懒加载

我们在路由中通常会定义很多不同的页面。如果不应用懒加载的话,很多页面都会打包到同一个js文件中,文件将会异常的大。造成进入首页时,需要加载的内容过多,时间过长,在浏览器中可能会出现短暂的空白页,从而降低用户体验,而运用路由懒加载是将各个模块分开打包,用户查看的时候再加载对应的模块,减少加载用时。

vue实现路由懒加载的三种方式:

  • Vue异步组件

    vue-router 配置路由,使用vue的异步组件技术,可以实现懒加载,但是这种情况是一个组件会生成一个js文件。

    component:resolve => require([‘需要加载的组件地址’],resolve)

  • ES6的import()

    const 组件名 = () => import(‘组件路径’)

  • webpack的require.ensure()

    vue-router 配置路由,使用webpack的require.ensure技术,可以实现懒加载,这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。

    require.ensure可实现按需加载资源,包括js,css等,会给里面require的文件单独打包,不会合主文件打包在一起。
    const list = r => require.ensure([],()=>r(require(‘组件地址’)),‘list’)

108、前端状态码

100+:表示连接继续
200+:操作成功收到,分析、接受
300+:完成此请求必须进一步处理,重定向
400+:表示各种客户端错误
500+:表示各种服务器错误es6新增加

109、promise.all和promise.race的理解

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值async/await的理解。

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

1、all( )

将多个Promise对象 进行整合成一个全新的Promise对象

  • 方法接受一个数组作为参数
  • 接收的参数中 只有全部都为成功的状态,新Promise对象的状态才为成功
  • (逻辑与)其中一个为失败,则新Promise对象的状态为失败,结果返回第一个抛出失败后的值
const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));

2、race( )

将多个Promise对象 进行整合成一个全新的Promise对象

  • 接收的参数中 第一个发生状态改变后的值,则为当前新的Promise对象的结果
  • 方法接受一个数组作为参数
const p1 = new Promise((res,rej)=>{setTimeout(()=>{
 res(1)
})})
const p2 = new Promise((res,rej)=>{res(2)})
const p3 = new Promise((res,rej)=>{rej(3)})

const p = Promise.race([p1,p2,p3])
console.log(p)		// 状态为 resolve

110、路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。

  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。

  3. hash模式:

    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:

    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

async怎么执行同步哇

你常用的vue指令有哪些
vue的生命周期
在哪个周期可以访问数据
在哪个周期可以开始调用方法
v-model的原理
vue与react的区别
vue与uniapp的区别

聊聊vue
ajax和axios,以及他们的区别

ajax实现步骤

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值