看完这篇文章保你面试稳操胜券 ——(必考题)javaScript 篇

textcolor{blue}{欢迎各位小伙伴:}

✨ 进大厂收藏这一系列就够了,全方位搜集总结,为大家归纳出这篇面试宝典,面试途中祝你一臂之力!,共分为四个系列

V

u

e

40

textcolor{green}{包含Vue40道经典面试题}

Vue40

r

e

a

c

t

12

textcolor{green}{包含react12道高并发面试题}

react12

34

textcolor{green}{包含微信小程序34道必问面试题}

34

j

a

v

a

S

c

r

i

p

t

80

textcolor{green}{包含javaScript80道扩展面试题}

javaScript80

A

P

P

10

textcolor{green}{包含APP10道装逼面试题}

APP10

H

T

M

L

/

C

S

S

30

textcolor{green}{包含HTML/CSS30道基础面试题}

HTML/CSS30

G

i

t

E

S

6

A

x

i

o

s

textcolor{green}{还包含Git、前端优化、ES6、Axios面试题}

GitES6Axios

textcolor{pink}{接下来让我们饱享这顿美味吧。一起来学习吧!!!}

j

a

v

a

S

c

r

i

p

t

textcolor{pink}{本篇为《看完这篇文章保你面试稳操胜券》第三篇(javaScript扩展篇)}

javaScript

javaScript

原生js

数组方法
  1. push()方法 可把参数指定的元素依次添加到数组的末尾,并返回添加元素后的数组长度
  2. unshift () 方法 可把参数指定的元素依次添加到数组的前面,并返回添加元素后的数组长度。该方法必须至少有一个参数
  3. pop() 方法可弹出(删除)数组最后一个元素,并返回弹出的元素。
  4. shift() 方法可删除数组第一个元素,并返回删除的元素。
  5. splic() 方法功能比较强,它可以实现删除指定数量的元素、替换指定元素以及在指定位置添加元素。
  6. slice() 方法返回包含从数组对象中的第 index1~index2-1 之间的元素的数组。index2 参数可以省略,省略时表示返回从 index1 位置开始一直到最后位置的元素。需要注意的是,该方法只是读取指定的元素,并不会对原数组作任何修改。
  7. sort() 方法用于按某种规则排序数组:当方法的参数为空时,按字典序(即元素的 Unicode 编码从小到大排序顺序)排序数组元素;当参数为一个匿名函数时,将按匿名函数指定的规则排序数组元素。
  8. concat() 将参数指定的数组和当前数组连成一个新数组。
  9. reverse() 方法可返回当前数组倒序排序形式。
  10. join() 方法可将数组内各个元素按参数指定的分隔符连接成一个字符串。参数可以省略,省略参数时,分隔符默认为“逗号”。
  11. forEach() 方法用于对数组的每个元素执行一次回调函数。
  12. filter() 方法用于创建一个新的数组,其中的元素是指定数组中所有符合指定函数要求的元素。
  13. map() 方法用于创建一个新的数组,其中的每个元素是指定数组的对应元素调用指定函数处理后的值。
  14. reduce() 用于使用回调函数对数组中的每个元素进行处理,并将处理进行汇总返回。
  15. find() 用于获取使回调函数值为 true 的第一个数组元素。如果没有符合条件的元素,将返回 undefined。
JavaScript 有几种类型

基本数据类型:undefined、null、boolean、number、string、symbol(es6的新数据类型)
引用数据类型:object、array、function(统称为object)

数据类型检测

typeof
instanceof
Object.prototype.toString.call()

深浅拷贝

浅拷贝
Object.assign()
深拷贝
JSON.parse(JSON.stringify(obj))
浅拷贝只复制指向某个对象的指针而不复制对象本身,新旧对象还是共享同一块内存。
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

闭包

闭包的原理就是作用域链,比如 函数F内部有一个函数G,函数 G可以访问到函数F中的变量,那么函数G就是闭包

JS 如何实现一个类

构造函数法
ES6 语法糖 class
一句话解析什么是原型链 遍历一个实列的属性时,先遍历实列对象上的属性,再遍历它的原型对象,一直遍历到Object

Js如何实现继承?

构造函数绑定:使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上

new 操作符具体干了什么?

首先是创建实例对象{}
this 变量引用该对象,同时还继承了构造函数的原型
其次属性和方法被加入到 this 引用的对象中
并且新创建的对象由 this 所引用,最后隐式的返回 this

this 对象的理解

普通函数
this 总是指向函数的直接调用者
如果有 new 关键字,this 指向 new 出来的实例对象
在事件中,this 指向触发这个事件的对象
IE 下 attachEvent 中的 this 总是指向全局对象 Window
箭头函数中,函数体内的this对象,就是定义时所在作用域的对象,而不是使用时所在的作用域的对象。

apply、call、bind

call、apply和bind是Function对象自带的三个方法,都是为了改变函数体内部 this 的指向。
apply 、 call 、bind 三者第一个参数都是 this 要指向的对象,也就是想指定的上下文;
apply 、 call 、bind 三者都可以利用后续参数传参;
bind 是返回对应 函数,便于稍后调用;apply 、call 则是立即调用 。

宏任务/微任务

macro-task(宏任务):当前调用栈中执行的任务称为宏任务。包括:script全部代码、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/O、UI Rendering。
.micro-task(微任务): 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务为微任务。包括:Process.nextTick(Node独有)、Promise、Object.observe(废弃)、MutationObserver
不同类型的任务会进入对应的Event Queue,宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。

typeof 返回哪些类型?

值类型: undefined / string / number / boolean /symbol
引用类型 : object (注意:typeof null === ‘object’
函数类型: function

列举强制类型转换和隐式类型转换

强制类型转换:parseInt parseFloat toString
隐式: if 逻辑运算 == +拼接字符串
[ ]中括号,表示一个数组,也可以理解为一个数组对象。
{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数

split() 和 join() 的区别

‘1-2-3’.split(’-’) // [1,2,3]
[1,2,3].join(’-’) // ‘1-2-3’

get 和push 的区别

get 一般用于查询操作,post一般用于提交操作
get 参数拼接在url上,post放在请求体内(数据体积可更大)
post 比较安全

foreach、for in、for of三者对比
  • forEach 专门用来循环数组,可以直接取到元素,同时也可以取到 index 值,不能 break 终止循环,没有返回值,不能 return
  • for in一般循环遍历的都是对象的属性,遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性 key 会变成字符串类型
  • for of 是 ES6 引入的标准,一般情况下用于遍历数组,可以遍历数组内的值,与索引无关
怎么设置cookie有效时间

Max-Age 指定从现在开始 Cookie 存在的秒数. 秒数过完则 cookie 过期

防抖,节流

节流和防抖都是性能优化的方法

  • 防抖
    原理:事件触发时,不要马上执行动作,而是设定一个延迟时间(这个时间很短,比如 500ms),在延迟时间内,再次触发事件,则重新计时
    适用场景:搜索联想,保证在输入完毕后才发送请求
    在这里插入图片描述

    上面会在等待一定时间后才会执行,虽然这个等待时间以毫秒为单位
    如果希望事件触发后马上就执行,可以修改代码如下(有时希望立刻执行函数,然后等到停止触发 n 秒后,才可以重新触发执行)

在这里插入图片描述

  • 节流
    原理:事件触发后,规定在一个单位时间内,只能执行一次要执行的代码,如果在这个单位时间内多次触发函数,只有一次生效
    适用场景:如拖拽浏览器、上拉加载更多(web app 中),因为都是高频触发,就需要节流进行控制。另外对于用户频繁点击登录按钮的情况,其实使用防抖和节流都是可以的。
    下面的代码使用定时器实现节流函数
    在这里插入图片描述

另外,要想到对方可能问,某些使用节流函数的场景为什么不能使用防抖实现,比如上拉加载更多时为什么不能使用防抖。这个要结合具体业务来说,比如防抖是最后一次的操作才生效,而我们上拉加载更多是一个平滑的不间断的过程,我们希望在上拉过程中就加载数据,而不是停止滑动才加载

什么是跨域?如何处理跨域?

1、jsonp跨域
利用了 script 不受同源策略的限制
2、CORS(Cross-Origin Resource Sharing),跨域资源共享 当使用XMLHttpRequest发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头 origin; 后端在接受到请求后确定响应后会在 Response Headers 中加入一个属性 Access-Control-AllowOrigin;
3、nginx反向代理
4、PHP端修改header
5、document.domain
6、window.name
7、postMessage

重绘和回流

1)首先可以先说一下重绘和回流的概念
回流:当 Render Tree 中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流
仔细分析的话,就是上面的这些改变会影响到布局的改变,就会造成回流
重绘:当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
2)在说明基本概念后,可以根据实际情况决定是否聊一下关于性能的问题,以及浏览器是如何优化的
回流比重绘的代价要更高
有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流
现代浏览器会对频繁的回流或重绘操作进行优化:
浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。
下面不用说,但还是建议记几个,以防万一
在这里插入图片描述

3)可以继续说一下,代码编写代码时,如何尽量避免重绘或者回流
CSS
• 避免使用table布局
• 尽可能在DOM树的最末端改变class
• 避免设置多层内联样式
• 将动画效果应用到position属性为absolute或fixed的元素上,因为这两种定位的元素脱离了文档流,其改变不会影响到文档
• 避免使用CSS表达式(例如:calc())
JavaScript
• 避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性
• 避免频繁操作DOM创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
• 也可以先为元素设置 display: none ,操作结束后再把它显示出来。因为在display属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘
• 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
• 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流
4. 附一些常用且会导致会回流的属性和方法,尽量记几个,万一面试中问到,就可以说一下
在这里插入图片描述

commit message 规范

在多人协作项目中,如果代码风格统一、代码提交信息的说明准确,那么在后期协作以及Bug处理时会 更加方便。Git 每次提交代码,都要写 Commit message(提交说明),否则就不允许提交。一般来 说,commit message 应该清晰明了,说明本次提交的目的。 Commit message 的作用
● 提供更多的历史信息,方便快速浏览
● 过滤某些commit(比如文档改动),便于快速查找信息
● 直接从commit生成Change log
● 可读性好,清晰,不必深入看代码即可了解当前commit的作用。
● 为 Code Reviewing(代码审查)做准备
● 方便跟踪工程历史
● 提高项目的整体质量,提高个人工程素质

git flow 工作流

Git Flow定义了一个项目发布的分支模型,为管理具有预定发布周期的大型项目提供了一个健壮的框 架。 流程

  1. master分支存放所有正式发布的版本,可以作为项目历史版本记录分支,不直接提交代码。仅用 于保持一个对应线上运行代码的 code base。
  2. develop分支为主开发分支,一般不直接提交代码
  3. feature分支为新功能分支,feature分支都是基于develop创建的,开发完成后会合并到develop 分支上。同时存在多个
  4. release分支基于最新develop分支创建,当新功能足够发布一个新版本(或者接近新版本发布的截 止日期),从develop分支创建一个release分支作为新版本的起点,用于测试,所有的测试bug在这 个分支改。测试完成后合并到master并打上版本号,同时也合并到develop,更新最新开发分支。 (一旦打了release分支之后不要从develop分支上合并新的改动到release分支),同一时间只有1 个,生命周期很短,只是为了发布。
  5. hotfix分支基于master分支创建,对线上版本的bug进行修复,完成后直接合并到master分支和 develop分支,如果当前还有新功能release分支,也同步到release分支上。同一时间只有1个,生 命周期较短
Js延迟加载

这个题目首先要问情对方到底问的是什么,是想知道 js 延迟加载,还是想知道懒加载
如果是前者的话,我觉得可以从一下几方面聊聊

  1. 首先要明白浏览器渲染页面的过程,也就是为什么需要 js 延迟加载
    浏览器渲染页面经过了构建 dom tree、构建 cssdom,构建 render tree 的过程,而且 JS 是单线程语言,所以其在 构建 dom 树的过程中,如果遇到了 script 标签,就会停止 dom 树的构建,先执行 js 代码,当 js 代码执行完毕后,才会从中断的地方继续构建 dom 树
  2. js 延迟加载就是尽量先完成 dom 树的构建,然后再执行 js
    根据上面所描述的过程,如果 js 执行花费的时间较长,拖慢了 dom 树的构建过程,就会导致页面的渲染变慢,也就是咱们平常所说的页面打开较慢。所以尽量确保 dom 树构建完毕后,在执行 script 代码
  3. 具体措施有哪些
    • 将 script 标签往后放,别放在 header 中,我们以前就推荐放到 body 的结束标签前面,因为这时已经确保 dom 树渲染完毕了
    • 引入外部 js 文件时,向 script 标签中加入 async ,这样html 的解析(dom渲染)和 js 脚本下载和执行就会同时进行
    • 引入外部 js 文件时,向 script 标签中加入 defer,这样,无论在页面的任何地方引入外部 js 文件,都会等到 html 解析完毕后,才会下载和执行 js 脚本
    4)最后可以分析一下这几种措施各自的特点以及应用场景
    将 js 脚本引入放在 body 结束标签前的问题在于只有在所有 HTML DOM 加载完成后才开始脚本的加载/解析过程。对于有大量 JavaScript 代码的大型网站,可能会带来显著的性能损耗
    async 和 defer 就是用于解决这个问题的
    浏览器遇到 async 脚本时不会阻塞页面渲染,而是直接下载然后运行
    但如果脚本中某些代码依赖的某些 dom 元素还没有解析,就会出现问题,所以必须确保脚本无需等待页面解析,那么就可以使用 async
    另外,async 的另外一个缺点就是无法控制执行顺序,比如下面同时引入三个 js 文件,后面的 js 文件依赖前面的 jquery,使用 async 无法确保三个的执行顺序,可能就会出现问题
    在这里插入图片描述

所以还要确保加载的多个脚本之间没有互相依赖,才可以使用 async

如果脚本之间有依赖,希望按照顺序加载执行,可以将 async 改成 defer

总结:
• 如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 async
• 如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用 defer,将关联的脚本按所需顺序置于 HTML 中。

上面就是对于 js 延迟加载的分析,面试时要根据实际情况整理成自己的语言回答

虚拟dom

关于虚拟dom:首先先从我们的真是dom来描述,知道浏览器如何解析的真是dom 首先是html分析器去解析html元素 然后形成dom树,然后css分析器分析css样式 生成一个样式表 接着会将dom树和样式表关联起来 构建一颗render树 因为每个dom节点上都有attach方法来接受样式信息 结合后 返回一个render对象 这些对象 最终会被构建成一个render树 然后有个render树 浏览器来时找每个render的对应显示的精准坐标 然后会调用每个节点上的paint方法去绘制到页面中
真实dom的弊端,因为原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。因为我们一般情况下 有时候我们改变一哥数据仅仅是影响的一个dom或者是一部分dom的变化 而真实dom会进行完整的更新和构建,所以就出现了虚拟dom,虚拟dom也就是一个js对象 就是他会将更新dom的的diff保存到js对象中 几次的dom操作变成了将操作转换成操作js对象 然后一次性的将js对象去attch到dom树上 交给浏览器去绘制

自己实现一个new方法

在这里插入图片描述

自己写一个深浅克隆

在这里插入图片描述

微任务和宏任务(js 运行机制)

其实,无论对方问 微任务、宏任务、还是事件循环、栈、堆、队列等,都是对于 js 运行机制的考察
所以,我们在这里对 js 运行机制做一个大概的梳理

有几个点,给大家梳理一下
• 为什么 js 被设计为单线程语言?如果两个线程同时对一个节点进行操作,一个删除某个子节点,一个修改这个子节点,那么结果应该是什么呢?所以 js 被设计为单线程
• js 任务为什么分为同步和异步任务?有些 js 代码耗时较长,但后面的代码又不依赖于上面代码的运行结果,为了提升效率,提出了同步和异步的解决方案
• 浏览器是多进程的。js 虽然是单线程语言,但是运行宿主之一的浏览器被设计为多进程的(一个进程可以包含多个线程)。为什么?试想,我们打开一个 tab 选项卡听歌,再打开一个写博客,可能还打开一个挂着某个网页游戏,如果单线程,搞得定吗
• 浏览器有一个进程叫做 渲染进程,负责页面的渲染,js 执行,事件的循环。渲染进程中有多个线程。其中 GUI 渲染线程负责渲染浏览器界面,JS 引擎线程,负责 js 脚本执行,事件触发线程用来控制事件循环,管理者一个事件队列
• 宏任务和微任务: js 中的任务分为同步和异步任务,异步任务分为宏任务和微任务(整体 script 代码执行也是一个宏任务,却并不是异步的),宏任务和微任务不再一个事件队列。一个宏任务执行完毕后,判断当前执行栈上是否有微任务,有的话执行当前的所有微任务,然后渲染,没有微任务的话,直接渲染,渲染完毕后再次执行下一个宏任务
• 事件循环:js 代码执行时,分为同步任务和异步任务。同步任务直接进入主线程开始执行,异步任务分为宏任务和微任务。一个宏任务执行完毕后,判断当前执行栈上是否有微任务,有的话执行当前的所有微任务,然后渲染,没有微任务的话,直接渲染,渲染完毕后再次执行下一个宏任务。这个过程就是事件循环
在这里插入图片描述

• 常见宏任务
o 主代码块
o setTimeout
o setInterval
o setImmediate ()-Node
o requestAnimationFrame ()-浏览器

• 常见微任务
o process.nextTick ()-Node
o Promise.then()
o catch
o finally
o Object.observe
o MutationObserver

Http的几种请求方式

对于一般人来说,能记住的无非就是前5个,所以大家回答时,能打出前5个就好了,后面几个可以说隐约记得,还有个啥方法,但是干嘛用的想不起来了,这样显得真实
• get
• post
• put
• delete
• OPTIONS:OPTIONS方法用来描述了目标资源的通信选项,会返回服务器支持预定义URL的HTTP策略
• TRACE:TRACE方法用于沿着目标资源的路径执行消息环回测试;它回应收到的请求,以便客户可以看到中间服务器进行了哪些(假设任何)进度或增量
• CONNECT:CONNECT方法用来建立到给定URI标识的服务器的隧道;它通过简单的TCP / IP隧道更改请求连接,通常实使用解码的HTTP代理来进行SSL编码的通信(HTTPS)
• HEAD:HEAD方法与GET方法相同,但没有响应体,仅传输状态行和标题部分。这对于恢复相应头部编写的元数据非常有用,而无需传输整个内容

Es6箭头函数和普通函数区别

• 箭头函数是匿名函数,不能作为构造函数,不能使用new
在这里插入图片描述

• 箭头函数内部不能使用 arguments

let B = (b) => {
        console.log(arguments);
      };
      B(2, 92, 32, 32); // Uncaught ReferenceError: arguments is not defined

• 箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
• 箭头函数没有原型属性
在这里插入图片描述

Es6相较于es5的优势,为什么喜欢使用es6

也可能会问, ES6 有哪些新特性,等等
这个可以参考 ES6 入门教程 - ECMAScript 6入门 (ruanyifeng.com)
找一些用的多的,说一说即可
当然,这里的 ES6 就是 ES2015
可以装逼的告诉面试官,自己认为的 ES6 是广义的 ES6,包含了 ES2015、2016、2017、2018、2019、2020、2021
当然 ES2015 是变化最大的,后面的变化稍微小一些
基于上面的认知,至少 模块化、解构赋值、Promise、async 和 await、新的创建类的方法、set 和 map 数据结构应该说出来

使用vue/react 自己编写一个 页码组件

要求:

  1. 能实现上一页 下一页
  2. 选中是数字高亮
  3. 可以输入页码进行跳转
  4. 展示总页数
  5. 可以设置每一页的条数

这个说白了,就是仿一下 elementui 的分页组件,考察的是你的编写组件的能力
其实在聊项目的时候,也可以说你觉得组件库(如 elementui) 的分页组件不好用,所以自己封装了一个分页组件,当然你要实际写写,想清楚这个组件的特点,有哪些功能是 elementui 的分页组件不具有的,或者比人家好的,这些事情要想清楚,吹牛之前要做足充分的准备

前端开发的大体流程

通过这个问题,可以看出平时开发是否规范,而且也能看出你实际的开发经验
对于这种问题,没有固定的回答,根据项目的规模、团队的配置(比如是否有测试、运维人员等)、技术负责人的能力,开发流程肯定是不一样的
回答这类问题的时候,不要跟背书似的,而要老神在在,大概的回答可以这样:
我们公司项目的开发流程大概是这样的
首先大家坐在一起,研讨项目需求,这个一般前后端做到一起讨论,项目经历也参与(如果有的话)
需求定下来之后,产品经理出原型(如果有产品经理的话,很多小企业都是后端或者前端兼了)
UI 同学出设计稿
前端同学和 UI 同学开始撕逼,不好实现或者有兼容问题的地方让 UI 去改
设计稿定下来后,前端同学出静态页面
前端同学编写前端逻辑,调用后台接口,完成功能
注:从需求定下来后,前端同学就需要跟后端同学不断的沟通接口的问题,这是一个持续的过程
前后端联调,这基本要等到前后端功能,开发的都差不多的时候
联调有问题,赶紧修改,没问题,推给测试人员测试,然后就是测试=》改bug=》测试=》改bug 这样循环,直到没有 bug 为止。(当然很多小企业没有专门的测试人员,所以这个步骤就是开发人员自己简单做一点功能测试即可)
上线(谁上线?运维人员,前提是有运维的话,大多数小企业,都是后端或前端人员搞定了)
后边项目有问题了就继续修改行了
注意:公司规模不一样,人员配置不一样,上面的步骤就不一样,所以大家要先想清楚,自己原来公司的规模等等,没有意外的话,都是小公司,毕竟小地方来的嘛
记住一点 前后端开发 后端先行

发布订阅模式

发布订阅模式是设计模式中的一种
发布-订阅模式其实是一种对象间一对多的依赖关系,当一个对象的状态发送改变时,所有依赖于它的对象都将得到状态改变的通知
举个生活中的栗子,就是公众号,我们订阅了公众号后,公众号只要发布新的内容,我们就可以收到,这就是典型的发布订阅模式(一个公众号对应多个用户),公众号是发布者,我们是订阅者
需要注意的是,在发布者和订阅者之间,还有一个调度中心,就是腾讯的服务,其负责将公众号发布的内容,通知给订阅者
所以,当我们聊到发布订阅模式的时候,其实是由三个主体的:发布者、调度中心、订阅者,这与观察者模式稍有不同
具体到代码层面,看下面代码,就是典型的发布订阅模式,body 的单击事件一旦触发,就相当于发布了内容,绑定的事件处理函数就会执行(相当于订阅者),调度中心很显然就是 js 引擎,负责将 body 的单击事件通知给事件处理函数
而且我们为 body 单击事件添加了多个事件处理函数,就好比是多个订阅者
在这里插入图片描述
理解了上面的内容后,面试的时候可能会让你手写代码,演示发布订阅模式
在这里插入图片描述
最后总是难免要聊一下 vue 中 发布订阅模式的应用
在 vue 实例初始化时,Object.defineProperty 方法,为 data 函数中的对象中属性添加了订阅,同时会初始化一个 watcher(调度中心),当对象属性发生改变时,向订阅者发送一个通知,然后订阅者执行 dom 操作,根据属性最新的只刷新视图

聊聊 promise

像这种问题,无非聊聊一下几点
● Promise 是什么
● Promise 解决了什么问题
● Promise 有什么不足之处
● 如果能手写一个 Promise 实现就更完美了

其实,能说出前两个问题基本就及格了,如果能够说一说不足之处,就代表了你是仔细使用了的。如果人家有要求,你再能够手写一个 Promise 的实现,基本半小时就过去了,差不多也就过了

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败,这种书法太书面了,通俗点的说法如下
一般我们都会在函数中使用 Promise ,更确切的说是返回一个 Promise,可以在这个 Promise 上绑定回调函数,常见的绑定回调函数的方式就是将成功的回调函数作为 resolve 方法的参数传入,将失败的回调函数作为 reject 方法的参数传入
在这里插入图片描述

所以,第二个问题的答案就是,Promise 可以解决回调地狱的问题,当然并不是只有这一个作用,更详细的解答可以自行搜索

至于缺点和手写 promise ,自行百度搜索

你是如何封装 axios 的

这是一个老生常谈的问题,对于 axios 的封装,大概如下几个方面
● 编写请求拦截器,拦截去中主要工作是配置统一的后台请求基准地址;设置超时时间;向 header 中添加 token 等
● 编写响应拦截器:拦截器中主要工作是配置统一的错误处理,比如 token 过期自动续命,统一显示错误信息等
● 对于重复请求的处理,这个单说一下
参考这篇文章 https://juejin.cn/post/6968630178163458084
这里简单说下,取消重复请求,主要是为了减轻后端接口的压力,造成不必要的结果,比如,用户重复点击下单页面,如果不做重复请求的判断和处理,可能会造成重复下单问题,当然这个问题,后端一定也会判读处理,但是我们最好还是在前端处理一下
处理的大概思路
在相应拦截器中,拿到每次请求的接口信息,并将其存储到队列中,

object.definedproperty缺点

无法检测到对象属性的新增或删除
由于js的动态性,可以为对象追加新的属性或者删除其中某个属性,这点对经过Object.defineProperty方法建立的响应式对象来说,只能追踪对象已有数据是否被修改,无法追踪新增属性和删除属性,这就需要另外处理。
不能监听数组的变化
vue在实现数组的响应式时,它使用了一些hack,把无法监听数组的情况通过重写数组的部分方法来实现响应式,这也只限制在数组的push/pop/shift/unshift/splice/sort/reverse七个方法,其他数组方法及数组的使用则无法检测到,

import和require区别

遵循规范 require 是 AMD规范引入方式 import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法 调用时间 require是运行时调用,所以require理论上可以运用在代码的任何地方 import是编译时调用,所以必须放在文件开头 本质 require是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量 import是解构过程,但是目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require

构造函数、proptype和__proto__的区别

所有构造函数的__proto__都指向Function.prototype,它是一个空函数(Empty function)

还有些内置对象比如Math,JSON是以对象形式存在的,无需new。它们的__proto__是Object.prototype
所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了Function.prototype的属性及方法,如length、call、apply、bind(ES5)。
.prototype是一个对象的原型对象,而.__proto__则是对原型对象的引用!

Vue 如何清除浏览器缓存?

项目打包的时候给每个打包文件加上 hash 值,一般是在文件后面加上时间戳;
在 html 文件中加入 meta 标签,content 属性设置为no-cache;
在后端服务器中进行禁止缓存设置。

Vue-router 路由有哪些模式?

一般有两种模式:

hash 模式:后面的 hash 值的变化,浏览器既不会向服务器发出请求,浏览器也不会刷新,每次 hash 值的变化会触发 hashchange 事件。
history 模式:利用了 HTML5 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。
Vue-cli 项目中 assets 和 static 文件夹有什么区别?
两者都是用于存放项目中所使用的静态资源文件的文件夹。其区别在于:
** assets 中的文件在运行 npm run build 的时候会打包**,简单来说就是会被压缩体积,代码格式化之类的。打包之后也会放到 static 中。static 中的文件则不会被打包。

v-model的原理
v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性。

xml和json的区别

1、读取的方式不一样。XML是使用XML DOM来循环遍历文档,JSON可以通过JSON.parse方法将json字符串转化为JavaScript可解析的格式。
2、格式不一样,XML的格式是对便签类似于HTML便签一样,而JSON是key/value的格式。

描述创建ajax的过程

step1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象;
step2. 创建一个新的HTTP请求,并指定改HTTP请求的方法、URL以及验证信息;
step3. 设置响应HTTP状态变化的函数;
step4. 发送HTTP请求;
step5. 获取异步调用返回的数据;
step6. 使用javascript和DOM实现局部刷新;

说说你对闭包的理解

闭包是指有权访问另一个函数作用域中的变量的函数。

  1. 可以在函数的外部访问到函数内部的局部变量。
  2. 让这些变量始终保存在内存中,不会随着函数的结束而自动销毁。
分别描述标签
defer和async的作用

defer:用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
async:用于异步下载脚本文件,下载完毕立即解释执行代码。

cookie的过期时间
设置一分钟过期
const nextTime = Date.now()+60*1000  //Date.now()是得到计算机开始的时间到现在的毫秒数,一毛1000毫秒
 
const expiresTime = new Date(nextTime).toUTCString() //转成UTC格式的字符串
 
res.writeHeader(200,{
 
"Content-Type":"text/html;charset=utf-8";
"Set-cookie":[isvisit=yes;expires="+expiresTime ",name=zs]
 
 
})
 
 
isvisit=yes
name=zs
 
给哪一个设置到期时间哪个就起作用,不然的的话不起作用,这个写的只有isvisit=yes起作用,name=zs还是默认的,关闭浏览器消失
LocalStorage 和 sessionStorage 之间的区别

LocalStorage 使用 localStorage 存储数据,即使关闭浏览器,也不会让数据消失,除非主动的去删除数据
使用 sessionStorage 存储数据,整个浏览器未关闭前,其数据一直都是存在的,若浏览器关闭数据消失
数组的方法

js数组递归求和

递归

function sum(arr) {
    var len = arr.length;
    if(len == 0){
        return 0;
    } else if (len == 1){
        return arr[0];
    } else {
        return arr[0] + sum(arr.slice(1));
    }
}

常用循环

function sum(arr) {
    var s = 0;
    for (var i=arr.length-1; i>=0; i--) {
        s += arr[i];
    }
    return s;
}
GET和POST区别

(1) GET的参数携带在URL地址中,POST请求的参数在request body中。
(2) GET在浏览器回退时不会再次提交请求,POST在浏览器回退时,会再次提交。
(3) GET请求会被浏览器主动cache(缓存),而POST不会。
(4) GET请求传递参数长度有限制,POST没有。
(5) GET比POST更不安全,因为参数暴露在URL中,POST不会。

在客户端是如何进行加密的?

哈希“加密”
结合验证码进行前端加密
非对称加密

如何改变 this 指向

call()
apply()
bind()

JS获取dom方法:
1.通过ID获取(getElementById)
  1. 通过类名(getElementsByClassName)
    3.通过name属性(getElementsByName)
    4.通过标签名(getElementsByTagName)
    5.获取html的方法(document.documentElement)
    6.获取body的方法(document.body)
    7.通过css选择器获取一个元素(querySelector)
    8.通过选择器获取一组元素(querySelectorAll)
怎样把录音传递给后端 客户端实现录音之后,

将压缩后的音频对象Blob传给服务器:
在该页面会创建下载连接,并以录音日期为文件名,可以选择下载,同时也会将音频对象传到服务器。
因为前端通过音频流文件上传到后台后,不再是保存为wav格式的音频,而是处理流的形式转为二进制数组,直接调用百度语音识别SDK方法,即可返回识别结果,不必编码后发给后端,后端然后再解码。
将结果使用websocket传给前端,显示在标签内就可以了:

http

概念:超文本传输协议(HTTP)是一个用于传输超媒体文档(例如 HTML)的应用层协议。它是为 Web 浏览器与 Web 服务器之间的通信而设计的,但也可以用于其他目的。HTTP 遵循经典的客户端-服务端模型,客户端打开一个连接以发出请求,然后等待它收到服务器端响应。HTTP 是无状态协议,这意味着服务器不会在两个请求之间保留任何数据(状态)。该协议虽然通常基于 TCP/IP 层,但可以在任何可靠的传输层上使用;也就是说,不像 UDP,它是一个不会静默丢失消息的协议。RUDP——作为 UDP 的可靠化升级版本——是一种合适的替代选择
http特性:
1.HTTP 是无连接无状态的

2.HTTP 一般构建于 TCP/IP 协议之上,默认端口号是 80

3.HTTP 可以分为两个部分,即请求和响应。

http请求:
HTTP 定义了在与服务器交互的不同方式,最常用的方法有 4 种 分别是 GET,POST,PUT, DELETE。URL 全称为资源描述符,可以这么认为:一个 URL 地址 对应着一个网络上的资源,而 HTTP 中的 GET,POST,PUT,DELETE 就对应着对这个资源的查询,修改,增添,删除4个操作。
HTTP 请求由 3 个部分构成,分别是:状态行,请求头(Request Header),请求正文。
HTTP 响应由 3 个部分构成,分别是:状态行,响应头(Response Header),响应正文。
HTTP 响应中包含一个状态码,用来表示服务器对客户端响应的结果。 状态码一般由3位构成:
1xx : 表示请求已经接受了,继续处理。 2xx : 表示请求已经处理掉了。 3xx : 重定向。 4xx : 一般表示客户端有错误,请求无法实现。 5xx : 一般为服务器端的错误。
常见的状态码:
200 OK 客户端请求成功。
301 Moved Permanently 请求永久重定向。
302 Moved Temporarily 请求临时重定向。
304 Not Modified 文件未修改,可以直接使用缓存的文件。
400 Bad Request 由于客户端请求有语法错误,不能被服务器所理解。
401 Unauthorized 请求未经授权,无法访问。
403 Forbidden 服务器收到请求,但是拒绝提供服务。服务器通常会在响应正文中给出不提供服务的原因。
404 Not Found 请求的资源不存在,比如输入了错误的URL。
500 Internal Server Error 服务器发生不可预期的错误,导致无法完成客户端的请求。
502 Bad Gateway 是一种HTTP协议的服务器端错误状态代码,它表示作为网关或代理角色的服务器,从上游服务器(如tomcat、php-fpm)中接收到的响应是无效的
503 Service Unavailable 服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常。

js计算数组重复出现的次数
let sumObj = arr.reduce((obj, name) => { 
if (name in obj) {
obj[name]++
} else {
obj[name]=1
}
return obj
}, {})

for循环

function getEleNums(data) {
 var map = {}
 for (i = 0; i < data.length; i++) {
     var key = data[i]
     if (map[key]) {
         map[key] += 1
     } else {
         map[key] = 1
     }
 }
 return map
}

Axios

ajax原理

AJAX 的核心是 XMLHttpRequest 对象。
Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器。像—些数据验证和数据处理等都交给Ajax引擎自己来做,,只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。

XMLHttpRequest 对象的三个常用的属性
  1. onreadystatechange 属性
    onreadystatechange 属性存有处理服务器响应的函数。
  2. readyState 属性
    readyState 属性存有服务器响应的状态信息。每当 readyState 改变时,
    onreadystatechange 函数就会被执行
  3. responseText 属性
    可以通过 responseText 属性来取回由服务器返回的数据。
xmlhttprequst的方法

open() 方法
send() 方法

Ajax编程步骤

创建XMLHttpRequest对象。
设置请求方式。
调用回调函数。
发送请求。

接口回调的方法

原生ajax
基于jQuery的ajax
fetch
axios

前后端交互的方式
  1. HTML赋值
  2. JS赋值
  3. script填充JSON
  4. AJAX获取JSON
  5. WebSocket实时传输数据

ES6

let&const

特性:
let 不能重复声明变量,var 可以重复声明变量;
块级作用域,es5中存在全局作用域、函数作用域、eval作用域;es6中引入了块级作用域,let声明的变量在块级作用域{}内有效
let声明的变量不存在var的变量提升问题

解构赋值

ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值

模板字符串
特性:

` `(反引号)内容中可以直接出现换行符,’ '和" "中则不可以,出现会报错
可以直接进行变量拼接
简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法
(在属性名和变量名相同的情况下),这样的书写更加简洁

箭头函数

与function声明的区别:

  1. 箭头函数this是静态的。
    箭头函数内的this指向上层对象;始终指向函数声明时所在作用域下的this的值,无法被call改变
    普通函数内的this指向调用其函数的对象
  2. 箭头函数不能作为构造函数实例化对象
  3. 箭头函数不能使用arguments变量,但是可以使用…rest
    箭头函数的简写
    ① 当形参有且只有一个的时候,可以省略()
    ② 当代码体只有一条语句的时候,可以省略{},此时return必须省略,而且语句的执行结果就是函数的返回值
rest参数

ES6引入rest参数,用于获取函数的实参,用来代替arguments

扩展运算符

…能将「数组」转为逗号分隔的「参数序列」
注:虽然形式与rest参数类似,但是rest参数是用在函数定义时的形参位置,扩展运算符是用在函数实际调用时的实参位置
应用场景:
1.数组的合并
2.数组的克隆
3.将伪数组转为真正的数组

symbol

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。
它是JavaScript语言的第7种数据类型,是一个类似字符串的数据类型
Symbol特点:
Symbol的值是唯一的,用来解决命名冲突的问题
Symbol值不能与其他数据进行运算,也不能与自己进行运算,譬如+、-、*、/、比较运算
Symbol定义的对象属性不能使用for…in遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名

symbol的使用场景

给对象添加属性和方法。
由于Symbol值具有唯一性,所以可以很安全地把属性和方法加入对象中

迭代器

迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。
任何数据结构只要部署iterator接口,就可以完成遍历操作

生成器

生成器本身是一个特殊的函数,
生成器函数是ES6提供的一种异步编程解决方案,
语法行为与传统函数不同
执行生成器函数,返回的是一个迭代器对象,通过iterator.next()调用执行函数内语句
yield是函数代码的分隔符,结合调用iterator.next()方法,实现函数gen1的语句的分段执行
使用for…of遍历函数执行后返回的迭代器对象,每一次遍历的item为yield后的表达式或者自变量的值

set集合

ES6提供了新的数据结构set(集合),本质上是一个对象。
它类似于数组,但成员的值都是唯一的,集合实现了iterator接口
,所以可以使用「扩展运算符」和for…of进行遍历
集合的属性和方法:
size,返回集合的元素个数
add,增加一个新元素,返回当前集合
delete,删除元素,返回Boolean值
has,检测集合中是否包含某个元素,返回Boolean值

set的应用
数组去重
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
        // 数组去重
        let result = [...new Set(arr)]
        console.log(result) // [1, 2, 3, 4, 5]

求交集   求并集    求差集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
        let arr2 = [4, 5, 6, 5, 6]

        // 求交集
        let result = [...new Set(arr)].filter(item => { // 对arr去重并进行遍历
            let s2 = new Set(arr2) // 将arr2变为元素不重复的集合
            if(s2.has(item)){ // 如果元素存在s2中
                return true
            }else{
                return false
            }

        })
        console.log(result) // [4, 5]

        // 简化写法
        let result2 = [...new Set(arr)].filter(item => new Set(arr2).has(item))
        console.log(result2)



map

ES6提供了Map数据结构。它类似于对象,也是键值对的集合,
但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。
Map也实现了iterator接口,所以可以使用「扩展运算符」和for…of进行遍历

Map的属性和方法:

size,返回Map的元素个数
set,增加一个新元素,返回当前Map
get,返回键名对象的键值
has,检测Map中是否包含某个元素,返回Boolean值
clear,清空集合,返回undefined

async await

不使用它请求数据 则是同步,会出现堵塞。
async/await是什么
async/await 是ES7提出的基于Promise的解决异步的最终方案。

async
async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。因此对async函数可以直接then,返回值就是then方法传入的函数。
await
await 也是一个修饰符,只能放在async定义的函数内。可以理解为等待。

await 修饰的如果是Promise对象:可以获取Promise中返回的内容(resolve或reject的参数),且取到值后语句才会往下执行;

如果不是Promise对象:把这个非promise的东西当做await表达式的结果

怎么不用循环输出1到50 var filled = new Array(100);

filled.fill(1)
var a = filled.map(function(val, index){
return index+1;
});
console.log(a);

写在最后

? 周周有福利,周周有惊喜 风火轮计划

? 更多前端硬核技术 总结 分享 交流 前端技术交流社区诚邀各位加入讨论 前端技术交流社区

textcolor{blue}{原创不易,还希望各位大佬支持一下}

?

textcolor{green}{点赞,你的认可是我创作的动力!}

⭐️

textcolor{green}{收藏,你的青睐是我努力的方向!}

✏️

textcolor{green}{评论,你的意见是我进步的财富!}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>