看完这篇文章保你面试稳操胜券 ——(必考题)javaScript 篇
Posted 几何心凉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了看完这篇文章保你面试稳操胜券 ——(必考题)javaScript 篇相关的知识,希望对你有一定的参考价值。
✨ 欢 迎 各 位 小 伙 伴 : \\textcolorblue欢迎各位小伙伴: 欢迎各位小伙伴:
✨ 进大厂收藏这一系列就够了,全方位搜集总结,为大家归纳出这篇面试宝典,面试途中祝你一臂之力!,共分为四个系列
✨ 包 含 V u e 40 道 经 典 面 试 题 \\textcolorgreen包含Vue40道经典面试题 包含Vue40道经典面试题
✨ 包 含 r e a c t 12 道 高 并 发 面 试 题 \\textcolorgreen包含react12道高并发面试题 包含react12道高并发面试题
✨ 包 含 微 信 小 程 序 34 道 必 问 面 试 题 \\textcolorgreen包含微信小程序34道必问面试题 包含微信小程序34道必问面试题
✨ 包 含 j a v a S c r i p t 80 道 扩 展 面 试 题 \\textcolorgreen包含javascript80道扩展面试题 包含javaScript80道扩展面试题
✨ 包 含 A P P 10 道 装 逼 面 试 题 \\textcolorgreen包含APP10道装逼面试题 包含APP10道装逼面试题
✨ 包 含 H T M L / C S S 30 道 基 础 面 试 题 \\textcolorgreen包含html/CSS30道基础面试题 包含HTML/CSS30道基础面试题
✨ 还 包 含 G i t 、 前 端 优 化 、 E S 6 、 A x i o s 面 试 题 \\textcolorgreen还包含Git、前端优化、ES6、Axios面试题 还包含Git、前端优化、ES6、Axios面试题
✨ 接 下 来 让 我 们 饱 享 这 顿 美 味 吧 。 一 起 来 学 习 吧 ! ! ! \\textcolorpink接下来让我们饱享这顿美味吧。一起来学习吧!!! 接下来让我们饱享这顿美味吧。一起来学习吧!!!
✨ 本 篇 为 《 看 完 这 篇 文 章 保 你 面 试 稳 操 胜 券 》 第 三 篇 ( j a v a S c r i p t 扩 展 篇 ) \\textcolorpink本篇为《看完这篇文章保你面试稳操胜券》第三篇(javaScript扩展篇) 本篇为《看完这篇文章保你面试稳操胜券》第三篇(javaScript扩展篇)
javaScript
原生js
数组方法
- push()方法 可把参数指定的元素依次添加到数组的末尾,并返回添加元素后的数组长度
- unshift () 方法 可把参数指定的元素依次添加到数组的前面,并返回添加元素后的数组长度。该方法必须至少有一个参数
- pop() 方法可弹出(删除)数组最后一个元素,并返回弹出的元素。
- shift() 方法可删除数组第一个元素,并返回删除的元素。
- splic() 方法功能比较强,它可以实现删除指定数量的元素、替换指定元素以及在指定位置添加元素。
- slice() 方法返回包含从数组对象中的第 index1~index2-1 之间的元素的数组。index2 参数可以省略,省略时表示返回从 index1 位置开始一直到最后位置的元素。需要注意的是,该方法只是读取指定的元素,并不会对原数组作任何修改。
- sort() 方法用于按某种规则排序数组:当方法的参数为空时,按字典序(即元素的 Unicode 编码从小到大排序顺序)排序数组元素;当参数为一个匿名函数时,将按匿名函数指定的规则排序数组元素。
- concat() 将参数指定的数组和当前数组连成一个新数组。
- reverse() 方法可返回当前数组倒序排序形式。
- join() 方法可将数组内各个元素按参数指定的分隔符连接成一个字符串。参数可以省略,省略参数时,分隔符默认为“逗号”。
- forEach() 方法用于对数组的每个元素执行一次回调函数。
- filter() 方法用于创建一个新的数组,其中的元素是指定数组中所有符合指定函数要求的元素。
- map() 方法用于创建一个新的数组,其中的每个元素是指定数组的对应元素调用指定函数处理后的值。
- reduce() 用于使用回调函数对数组中的每个元素进行处理,并将处理进行汇总返回。
- 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定义了一个项目发布的分支模型,为管理具有预定发布周期的大型项目提供了一个健壮的框 架。 流程
- master分支存放所有正式发布的版本,可以作为项目历史版本记录分支,不直接提交代码。仅用 于保持一个对应线上运行代码的 code base。
- develop分支为主开发分支,一般不直接提交代码
- feature分支为新功能分支,feature分支都是基于develop创建的,开发完成后会合并到develop 分支上。同时存在多个
- release分支基于最新develop分支创建,当新功能足够发布一个新版本(或者接近新版本发布的截 止日期),从develop分支创建一个release分支作为新版本的起点,用于测试,所有的测试bug在这 个分支改。测试完成后合并到master并打上版本号,同时也合并到develop,更新最新开发分支。 (一旦打了release分支之后不要从develop分支上合并新的改动到release分支),同一时间只有1 个,生命周期很短,只是为了发布。
- hotfix分支基于master分支创建,对线上版本的bug进行修复,完成后直接合并到master分支和 develop分支,如果当前还有新功能release分支,也同步到release分支上。同一时间只有1个,生 命周期较短
Js延迟加载
这个题目首先要问情对方到底问的是什么,是想知道 js 延迟加载,还是想知道懒加载
如果是前者的话,我觉得可以从一下几方面聊聊
- 首先要明白浏览器渲染页面的过程,也就是为什么需要 js 延迟加载
浏览器渲染页面经过了构建 dom tree、构建 cssdom,构建 render tree 的过程,而且 JS 是单线程语言,所以其在 构建 dom 树的过程中,如果遇到了 script 标签,就会停止 dom 树的构建,先执行 js 代码,当 js 代码执行完毕后,才会从中断的地方继续构建 dom 树 - js 延迟加载就是尽量先完成 dom 树的构建,然后再执行 js
根据上面所描述的过程,如果 js 执行花费的时间较长,拖慢了 dom 树的构建过程,就会导致页面的渲染变慢,也就是咱们平常所说的页面打开较慢。所以尽量确保 dom 树构建完毕后,在执行 script 代码 - 具体措施有哪些
• 将 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 自己编写一个 页码组件
要求:
- 能实现上一页 下一页
- 选中是数字高亮
- 可以输入页码进行跳转
- 展示总页数
- 可以设置每一页的条数
这个说白了,就是仿一下 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实现局部刷新;
说说你对闭包的理解
闭包是指有权访问另一个函数作用域中的变量的函数。
- 可以在函数的外部访问到函数内部的局部变量。
- 让这些变量始终保存在内存中,不会随着函数的结束而自动销毁。
分别描述标签
defer和async的作用
defer:用于开启新的线程下载脚本文件,并使脚本在文档解析完成后执行。
async:用于异步下载脚本文件,下载完毕立即解释执行代码。
cookie的过期时间
设置一分钟过期
const nextTime = Date.now()看完这84道面试题,不信你找不到工作(java)
面试官:什么是 EventLoop。你:一脸蒙蔽。看完这篇文章就懂了