2021-09-29前端面试题的积累

1.URL和URI的区别?

URI: Uniform Resource Identifier 指的是统一资源标识符,用唯一的标识来确定一个资源,它是一种抽象的定义,也就是说,不管使用什么方法来定义,只要能唯一的标识一个资源,就可以称为 URI。

URL: Uniform Resource Location 指的是统一资源定位符

URN: Universal Resource Name 指的是统一资源名称

URL 和 URN 是 URI 的子集,URL 可以理解为使用地址来标识资源,URN 可以理解为使用名称来标识资源。

2.get和post请求在缓存方面的区别?

get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。post 不同,post 做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。

回答:缓存一般只适用于那些不会更新服务端数据的请求。一般get请求都是查找请求,不会对服务器资源数据造成修改,而 post 请求一般都会对服务器数据造成修改,所以,一般会对get请求进行缓存,很少会对 post 请求进行缓存。

3.图片的懒加载和预加载?

预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染。懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数。两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。 懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

懒加载也叫延迟加载,指的是在长网页中延迟加载图片的时机,当用户需要访问时,再去加载,这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。它适用于图片很多,页面很长的电商网站的场景。懒加载的实现原理是,将页面上的图片的 src 属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出真实路径赋值给图片的 src 属性,以此来实现图片的延迟加载。预加载指的是将所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。通过预加载能够减少用户的等待时间,提高用户的体验。我了解的预加载的最常用的方式是使用 js 中的 image 对象,通过为 image 对象来设置 scr 属性,来实现图片的预加载。这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。

4.mouseover和mouseenter的区别?

当鼠标移动到元素上时就会触发 mouseenter 事件,类似 mouseover,它们两者之间的差别是 mouseenter 不会冒泡。由于 mouseenter 不支持事件冒泡,导致在一个元素的子元素上进入或离开的时候会触发其 mouseover 和 mouseout 事件,但是却不会触发 mouseenter 和 mouseleave 事件。

5.js拖拽功能的实现?

首先是三个事件,分别是 mousedown,mousemove,mouseup当鼠标点击按下的时候,需要一个 tag 标识此时已经按下,可以执行 mousemove 里面的具体方法。clientX,clientY 标识的是鼠标的坐标,分别标识横坐标和纵坐标,并且我们用 offsetX 和 offsetY 来表示元素的元素的初始坐标,移动的举例应该是:鼠标移动时候的坐标-鼠标按下去时候的坐标。也就是说定位信息为:鼠标移动时候的坐标-鼠标按下去时候的坐标+元素初始情况下的 offetLeft.

回答:一个元素的拖拽过程,我们可以分为三个步骤,第一步是鼠标按下目标元素,第二步是鼠标保持按下的状态移动鼠标,第三步是鼠标抬起,拖拽过程结束。这三步分别对应了三个事件,mousedown 事件,mousemove 事件和 mouseup 事件。只有在鼠标按下的状态移动鼠标我们才会执行拖拽事件,因此我们需要在 mousedown 事件中设置一个状态来标识鼠标已经按下,然后在 mouseup 事件中再取消这个状态。在 mousedown 事件中我们首先应该判断,目标元素是否为拖拽元素,如果是拖拽元素,我们就设置状态并且保存这个时候鼠标的位置。然后在 mousemove 事件中,我们通过判断鼠标现在的位置和以前位置的相对移动,来确定拖拽元素在移动中的坐标。最后 mouseup 事件触发后,清除状态,结束拖拽事件。

6.为什么使用setTimeout实现setInterval?怎么模拟?

// 思路是使用递归函数,不断地去执行 setTimeout 从而达到 setInterval 的效果function mySetInterval(fn, timeout) { // 控制器,控制定时器是否继续执行 var timer = { flag: true }; // 设置递归函数,模拟定时器执行。 function interval() { if (timer.flag) { fn(); setTimeout(interval, timeout); } } // 启动定时器 setTimeout(interval, timeout); // 返回控制器 return timer;}

回答:setInterval 的作用是每隔一段指定时间执行一个函数,但是这个执行不是真的到了时间立即执行,它真正的作用是每隔一段时间将事件加入事件队列中去,只有当当前的执行栈为空的时候,才能去从事件队列中取出事件执行。所以可能会出现这样的情况,就是当前执行栈执行的时间很长,导致事件队列里边积累多个定时器加入的事件,当执行栈结束的时候,这些事件会依次执行,因此就不能到间隔一段时间执行的效果。针对 setInterval 的这个缺点,我们可以使用 setTimeout 递归调用来模拟 setInterval,这样我们就确保了只有一个事件结束了,我们才会触发下一个定时器事件,这样解决了 setInterval 的问题。

7.let和const的注意点?1.声明的变量只在声明时的代码块内有效,2.不存在声明提升,3.存在暂时性死区,如果在变量声明前使用,会报错

4.不允许重复声明,重复声明会报错

8.什么是rest参数? rest 参数(形式为...变量名),用于获取函数的多余参数。

9.什么是尾调用,使用尾调用有什么好处?

尾调用指的是函数的最后一步调用另一个函数。我们代码执行是基于执行栈的,所以当我们在一个函数里调用另一个函数时,我们会保留当前的执行上下文,然后再新建另外一个执行上下文加入栈中。使用尾调用的话,因为已经是函数的最后一步,所以这个时候我们可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

10.Symbol类型的注意点?

1.Symbol 函数前不能使用 new 命令,否则会报错。

2.Symbol 函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。

3.Symbol 作为属性名,该属性不会出现在 for...in、for...of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify() 返回。

4.Object.getOwnPropertySymbols 方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

5.Symbol.for 接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。

6.Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key。

11.set和weakset结构?

1.ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。

2.WeakSet 结构与 Set 类似,也是不重复的值的集合。但是 WeakSet 的成员只能是对象,而不能是其他类型的值。WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用

12.map和weakmap结构?

1.Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。

2.WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制

13.reflect对象创建目的?

1.将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty,放到 Reflect 对象上。

2.修改某些 Object 方法的返回结果,让其变得更合理。

3.让 Object 操作都变成函数行为。

4.Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。

14.什么是Promise对象,身, 是Promises/A+规范?

Promise 对象是异步编程的一种解决方案,最早由社区提出。Promises/A+规范是 JavaScript Promise 的标准,规定了一个 Promise 所必须具有的特性。Promise 是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。一个 Promise 实例有三种状态,分别是 pending、resolved 和 rejected,分别代表了进行中、已成功和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经改变,就凝固了,无法再被改变了。状态的改变是通过resolve()和reject()函数来实现的,我们可以在异步操作结束后调用这两个函数改变 Promise 实例的状态,它的原型上定义了一个 then 方法,使用这个 then 方法可以为两个状态的改变注册回调函数。这个回调函数属于微任务,会在本轮事件循环的末尾执行。

15.require模块引入的查找方式?

当 Node 遇到require(X)时,按下面的顺序处理。(1)如果X是内置模块(比如require('http')) a.返回该模块。 b.不再继续执行。(2)如果X以"./"或者"/"或者"../"开头 a.根据X所在的父模块,确定X的绝对路径。 b.将X当成文件,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。XX.jsX.jsonX.node c.将X当成目录,依次查找下面文件,只要其中有一个存在,就返回该文件,不再继续执行。X/package.json(main字段)X/index.jsX/index.jsonX/index.node(3)如果X不带路径 a.根据X所在的父模块,确定X可能的安装目录。 b.依次在每个目录中,将X当成文件名或目录名加载。(4)抛出"not found"

16.手写一个Promise?

const PENDING = "pending";const RESOLVED = "resolved";const REJECTED = "rejected";function MyPromise(fn) { // 保存初始化状态 var self = this; // 初始化状态 this.state = PENDING; // 用于保存 resolve 或者 rejected 传入的值 this.value = null; // 用于保存 resolve 的回调函数 this.resolvedCallbacks = []; // 用于保存 reject 的回调函数 this.rejectedCallbacks = []; // 状态转变为 resolved 方法 function resolve(value) { // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变 if (value instanceof MyPromise) { return value.then(resolve, reject); } // 保证代码的执行顺序为本轮事件循环的末尾 setTimeout(() => { // 只有状态为 pending 时才能转变, if (self.state === PENDING) { // 修改状态 self.state = RESOLVED; // 设置传入的值 self.value = value; // 执行回调函数 self.resolvedCallbacks.forEach(callback => { callback(value); }); } }, 0); } // 状态转变为 rejected 方法 function reject(value) { // 保证代码的执行顺序为本轮事件循环的末尾 setTimeout(() => { // 只有状态为 pending 时才能转变 if (self.state === PENDING) { // 修改状态 self.state = REJECTED; // 设置传入的值 self.value = value; // 执行回调函数 self.rejectedCallbacks.forEach(callback => { callback(value); }); } }, 0); } // 将两个方法传入函数执行 try { fn(resolve, reject); } catch (e) { // 遇到错误时,捕获错误,执行 reject 函数 reject(e); }}MyPromise.prototype.then = function(onResolved, onRejected) { // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数 onResolved = typeof onResolved === "function" ? onResolved : function(value) { return value; }; onRejected = typeof onRejected === "function" ? onRejected : function(error) { throw error; }; // 如果是等待状态,则将函数加入对应列表中 if (this.state === PENDING) { this.resolvedCallbacks.push(onResolved); this.rejectedCallbacks.push(onRejected); } // 如果状态已经凝固,则直接执行对应状态的函数 if (this.state === RESOLVED) { onResolved(this.value); } if (this.state === REJECTED) { onRejected(this.value); }};

17.如何检测浏览器所支持的最小字体大小? 用 JS 设置 DOM 的字体为某一个值,然后再取出来,如果值设置成功,就说明支持。

18.怎么做js代码Error统计? error 统计使用浏览器的 window.error 事件。

19.单例模式是什么? 单例模式保证了全局只有一个实例来被访问。比如说常用的如弹框组件的实现和全局状态的实现。

20.策略模式是什么? 策略模式主要是用来将方法的实现和方法的调用分离开,外部通过不同的参数可以调用不同的策略。我主要在 MVP 模式解耦的时候用来将视图层的方法定义和方法调用分离。

21.代理模式是什么? 代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。比如说常见的事件代理。

22.中介者模式是什么? 中介者模式指的是,多个对象通过一个中介者进行交流,而不是直接进行交流,这样能够将通信的各个对象解耦。

23.适配器模式是什么? 

适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。假如我们需要一种新的接口返回方式,但是老的接口由于在太多地方已经使用了,不能随意更改,这个时候就可以使用适配器模式。比如我们需要一种自定义的时间返回格式,但是我们又不能对 js 时间格式化的接口进行修改,这个时候就可以使用适配器模式。

24.观察者模式和发布订阅模式有什么不同?

发布订阅模式其实属于广义上的观察者模式在观察者模式中,观察者需要直接订阅目标事件。在目标发出内容改变的事件后,直接接收事件并作出响应。而在发布订阅模式中,发布者和订阅者之间多了一个调度中心。调度中心一方面从发布者接收事件,另一方面向订阅者发布事件,订阅者需要在调度中心中订阅事件。通过调度中心实现了发布者和订阅者关系的解耦。使用发布订阅者模式更利于我们代码的可维护性。

25.vuede生命周期是什么? Vue 的生命周期指的是组件从创建到销毁的一系列的过程,被称为 Vue 的生命周期。通过提供的 Vue 在生命周期各个阶段的钩子函数,我们可以很好的在 Vue 的各个生命阶段实现一些操作。

26.vue的各个生命阶段是什么?

Vue 一共有8个生命阶段,分别是创建前、创建后、加载前、加载后、更新前、更新后、销毁前和销毁后,每个阶段对应了一个生命周期的钩子函数。(1)beforeCreate 钩子函数,在实例初始化之后,在数据监听和事件配置之前触发。因此在这个事件中我们是获取不到data数据的。(2)created 钩子函数,在实例创建完成后触发,此时可以访问data、methods 等属性。但这个时候组件还没有被挂载到页面中去,所以这个时候访问不到 $el 属性。一般我们可以在这个函数中进行一些页面初始化的工作,比如通过 ajax 请求数据来对页面进行初始化。(3)beforeMount 钩子函数,在组件被挂载到页面之前触发。在 beforeMount 之前,会找到对应的 template,并编译成 render 函数。(4)mounted 钩子函数,在组件挂载到页面之后触发。此时可以通过 DOM API 获取到页面中的 DOM 元素。(5)beforeUpdate 钩子函数,在响应式数据更新时触发,发生在虚拟 DOM 重新渲染和打补丁之前,这个时候我们可以对可能会被移除的元素做一些操作,比如移除事件监听器。(6)updated 钩子函数,虚拟 DOM 重新渲染和打补丁之后调用。(7)beforeDestroy 钩子函数,在实例销毁之前调用。一般在这一步我们可以销毁定时器、解绑全局事件等。(8)destroyed 钩子函数,在实例销毁之后调用,调用后,Vue 实例中的所有东西都会解除绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。当我们使用 keep-alive 的时候,还有两个钩子函数,分别是 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 deactivated 钩子函数,命中缓存渲染后会执行 actived 钩子函数。

27.vue组件间的参数传递方式?

(1)父子组件间通信第一种方法是子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据。第二种是通过 ref 属性给子组件设置一个名字。父组件通过 $refs 组件名来获得子组件,子组件通过 $parent 获得父组件,这样也可以实现通信。第三种是使用 provider/inject,在父组件中通过 provider 提供变量,在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深,只要调用了 inject 那么就可以注入 provider 中的数据。(2)兄弟组件间通信第一种是使用 eventBus 的方法,它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递。第二种是通过 $parent.$refs 来获取到兄弟组件,也可以进行通信。(3)任意组件之间使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件。如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候采用上面这一些方法可能不利于项目的维护。这个时候可以使用 vuex ,vuex 的思想就是将这一些公共的数据抽离出来,将它作为一个全局的变量来管理,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。

28.computer和watch的差异?

(1)computed 是计算一个新的属性,并将该属性挂载到 Vue 实例上,而 watch 是监听已经存在且已挂载到 Vue 实例上的数据,所以用 watch 同样可以监听 computed 计算属性的变化。(2)computed 本质是一个惰性求值的观察者,具有缓存性,只有当依赖变化后,第一次访问 computed 属性,才会计算新的值。而 watch 则是当数据发生变化便会调用执行函数。(3)从使用场景上说,computed 适用一个数据被多个数据影响,而 watch 适用一个数据影响多个数据。

区别:computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容。watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。

29.vue-router中的导航钩子函数?

(1)全局的钩子函数 beforeEach 和 afterEachbeforeEach 有三个参数,to 代表要进入的路由对象,from代表离开的路由对象。next 是一个必须要执行的函数,如果不传参数,那就执行下一个钩子函数,如果传入false,则终止跳转,如果传入一个路径,则导航到对应的路由,如果传入 error ,则导航终止,error 传入错误的监听函数。(2)单个路由独享的钩子函数 beforeEnter,它是在路由配置上直接进行定义的。(3)组件内的导航钩子主要有这三种:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。它们是直接在路由组件内部直接进行定义的。

30.$router和$route 的区别?

$route 是“路由信息对象”,包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。而 $router 是“路由实例”对象包括了路由的跳转方法,钩子函数等。

31.vue的常用的修饰符?prevent:提交事件不再重载页面;.stop:阻止单击事件冒泡;.self:当事件发生在该元素本身而不是子元素的时候会触发;

32.vue中key值的作用?

vue 中 key 值的作用可以分为两种情况来考虑。第一种情况是 v-if 中使用 key。由于 Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。因此当我们使用 v-if 来实现元素切换的时候,如果切换前后含有相同类型的元素,那么这个元素就会被复用。如果是相同的 input 元素,那么切换前后用户的输入不会被清除掉,这样是不符合需求的。因此我们可以通过使用 key 来唯一的标识一个元素,这个情况下,使用 key 的元素不会被复用。这个时候 key 的作用是用来标识一个独立的元素。第二种情况是 v-for 中使用 key。用 v-for 更新已渲染过的元素列表时,它默认使用“就地复用”的策略。如果数据项的顺序发生了改变,Vue 不会移动 DOM 元素来匹配数据项的顺序,而是简单复用此处的每个元素。因此通过为每个列表项提供一个 key 值,来以便 Vue 跟踪元素的身份,从而高效的实现复用。这个时候 key 的作用是为了高效的更新渲染虚拟 DOM。

33.keep-alive组件有什么作用? 如果你需要在组件切换的时候,保存一些组件的状态防止多次渲染,就可以使用 keep-alive 组件包裹需要保存的组件。

34.vue中mixin和mixins区别?

mixin 用于全局混入,会影响到每个组件实例。mixins 应该是我们最常使用的扩展组件的方式了。如果多个组件中有相同的业务逻辑,就可以将这些逻辑剥离出来,通过 mixins 混入代码,比如上拉下拉加载数据这种逻辑等等。另外需要注意的是 mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并

35.开发中常用的几种Content-Type?

(1)application/x-www-form-urlencoded浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。该种方式提交的数据放在 body 里面,数据按照 key1=val1&key2=val2 的方式进行编码,key 和val都进行了 URL转码。(2)multipart/form-data该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式。(3)application/json告诉服务器消息主体是序列化后的 JSON 字符串。(4)text/xml该种方式主要用来提交 XML 格式的数据。

36.如何封装一个javascript的类型判断函数?

function getType(value) { 

 // 判断数据是 null 的情况

 if (value === null) { return value + ""; }

 // 判断数据是引用类型的情况

 if (typeof value === "object") { let valueClass = Object.prototype.toString.call(value), type = valueClass.split(" ")[1].split(""); type.pop(); return type.join("").toLowerCase(); } 

else { // 判断数据是基本数据类型的情况和函数的情况 return typeof value; }}

37.使用闭包实现每隔一秒打印1,2,3,4?

// 使用闭包实现for (var i = 0; i < 5; i++) { (function(i) { setTimeout(function() { console.log(i); }, i * 1000); })(i);}// 使用 let 块级作用域for (let i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000);}

38.手写一个jsonp?

function jsonp(url, params, callback) {

 // 判断是否含有参数 let queryString = url.indexOf("?") === "-1" ? "?" : "&"; 

 // 添加参数 for (var k in params) { if (params.hasOwnProperty(k)) { queryString += k + "=" + params[k] + "&"; } } 

 // 处理回调函数名 let random = Math.random() .toString() .replace(".", ""), callbackName = "myJsonp" + random; // 添加回调函数 queryString += "callback=" + callbackName; 

 // 构建请求 let scriptNode = document.createElement("script"); scriptNode.src = url + queryString; 

 window[callbackName] = function() { 

 // 调用回调函数 callback(...arguments); 

 // 删除这个引入的脚本 document.getElementsByTagName("head")[0].removeChild(scriptNode); }; 

 // 发起请求 document.getElementsByTagName("head")[0].appendChild(scriptNode);}

39.手写一个观察者模式?

var events = (function() {

 var topics = {}; return { 

 // 注册监听函数 subscribe: function(topic, handler) { if (!topics.hasOwnProperty(topic)) { topics[topic] = []; } topics[topic].push(handler); }, 

 // 发布事件,触发观察者回调事件 publish: function(topic, info) { if (topics.hasOwnProperty(topic)) { topics[topic].forEach(function(handler) { handler(info); }); } },

 // 移除主题的一个观察者的回调事件 remove: function(topic, handler) { if (!topics.hasOwnProperty(topic)) return; var handlerIndex = -1; topics[topic].forEach(function(item, index) { if (item === handler) { handlerIndex = index; } }); if (handlerIndex >= 0) { topics[topic].splice(handlerIndex, 1); } }, 

 // 移除主题的所有观察者的回调事件 removeAll: function(topic) { if (topics.hasOwnProperty(topic)) { topics[topic] = []; } } };})();

40.EventEmitter实现?

classEventEmitter{constructor(){this.events={};}

on(event,callback){letcallbacks=this.events[event]||[];callbacks.push(callback);this.events[event]=callbacks;returnthis;}

off(event,callback{letcallbacks=this.events[event];this.events[event]=callbacks&&callbacks.filter(fn=>fn!==callback);returnthis;}

emit(event,...args){letcallbacks=this.events[event];callbacks.forEach(fn=>{fn(...args);});returnthis;}

once(event,callback){letwrapFun=function(...args){callback(...args);

this.off(event,wrapFun);};

this.on(event,wrapFun);returnthis;}}

41.js中的命名规则?

(1)第一个字符必须是字母、下划线(_)或美元符号($)(2)余下的字符可以是下划线、美元符号或任何字母或数字字符一般我们推荐使用驼峰法来对变量名进行命名,因为这样可以与ECMAScript内置的函数和对象命名格式保持一致。

42.js语句末尾分号是否可以省略?

在 ECMAScript 规范中,语句结尾的分号并不是必需的。但是我们一般最好不要省略分号,因为加上分号一方面有利于我们代码的可维护性,另一方面也可以避免我们在对代码进行压缩时出现错误。

43.obiect.assign()? Object.assign()方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

44.Math.ceil和Math.floor的区别?

Math.ceil()===向上取整,函数返回一个大于或等于给定数字的最小整数。Math.floor()===向下取整,函数返回一个小于或等于给定数字的最大整数。

45.jsfor循坏的注意点?

for(vari=0,j=0;i<5,j<9;i++,j++){console.log(i,j);}

// 当判断语句含有多个语句时,以最后一个判断语句的值为准,因此上面的代码会执行 10 次。

// 当判断语句为空时,循环会一直进行。

46.js中倒计时的实现?

在前端实现中我们一般通过 setTimeout 和 setInterval 方法来实现一个倒计时效果。但是使用这些方法会存在时间偏差的问题,这是由于 js 的程序执行机制造成的,setTimeout 和 setInterval 的作用是隔一段时间将回调事件加入到事件队列中,因此事件并不是立即执行的,它会等到当前执行栈为空的时候再取出事件执行,因此事件等待执行的时间就是造成误差的原因。一般解决倒计时中的误差的有这样两种办法:(1)第一种是通过前端定时向服务器发送请求获取最新的时间差,以此来校准倒计时时间。(2)第二种方法是前端根据偏差时间来自动调整间隔时间的方式来实现的。这一种方式首先是以 setTimeout 递归的方式来实现倒计时,然后通过一个变量来记录已经倒计时的秒数。每一次函数调用的时候,首先将变量加一,然后根据这个变量和每次的间隔时间,我们就可以计算出此时无偏差时应该显示的时间。然后将当前的真实时间与这个时间相减,这样我们就可以得到时间的偏差大小,因此我们在设置下一个定时器的间隔大小的时候,我们就从间隔时间中减去这个偏差大小,以此来实现由于程序执行所造成的时间误差的纠正。

47.进程间通信的方式?1.管道通信,2.消息队列通信,3.信号量通信,4.信号通信,5.共享内存通信,6.套接字通信

HTML部分:

 1.了解meta viewport 吗?怎么写?(移动端开发)

[if !supportLists]第一、[endif]如果不设置meta viewport标签,那么移动设备上浏览器默认的宽度值为800px,980px,1024px等这些,是大于屏幕宽度的。这里的宽度所用的单位px都是指css中的px,它跟代表实际屏幕物理像素的px不同。[if !supportLists]第二、[endif]每个移动设备浏览器中都有一个理想的宽度,相当于css中的100%所代表的宽度。设置理想的宽度可以使得网站在尺寸近似但分辨率不同的设备上看起来是差不多的。市面上的手机品牌种类繁多,但是理想的viewport归纳起来只有320/360/384/400等

2.行内元素有哪些?块级元素有哪些?

行内元素有: span a b i img input select strong

块级元素有:div p h1-h6 ul table form ul ol li dl dt dd

3.a标签中如何禁用href 跳转页面?

1、e.preventDefault();

2、href="javascript:void(0);"

4.Doctype作用? 严格模式与混杂模式如何区分?

https://www.cnblogs.com/wuqiutong/p/5986191.html

用自己的话总结

5.HTML与XHTML 二者有什么区别?

(1)、在XHTML中标记名称必须小写

hello

(2)、在XHTML中属性名称必须小写 src必须小写

(3)、在XHTML中标记必须严格嵌套

hello

(4)、在XHTML中标记必须封闭

hello

(5)、在XHTML中即使是空元素的标记也必须封闭

(6)、在XHTML中属性值用双引号括起来

(7)、在XHTML中属性值必须使用完整形式

(8)、在XHTML中应该区分“内容标记” 和 “结构标记”

CSS部分:

1.边距重叠解决方案(BFC) BFC原理?

(1)、BFC的基本概念?

块级格式化上下文。与之并列的是IFC(内联元素的格式化上下文,此处先不讨论哈)。

(2)、说下BFC的原理?

BFC原理就是BFC的渲染规则,共四点:

a)在BFC这个元素垂直方向的边距会发生重叠。

b) BFC的区域不会与浮动元素box重叠,用来清除浮动的和布局的。

c) BFC在页面上是一个独立的容器,外面的元素不会影响里面的元素,里面的元素也不会影响外面的元素。

d)计算BFC元素的高度的时候,浮动元素也会参与计算。

2.CSS选择器列表优先级及权重?

2.1选择器类型

1、ID  #id

2、class  .class

3、标签  p

4、通用  *

5、属性  [type="text"]

6、伪类  :hover

7、伪元素  ::first-line

8、子选择器、相邻选择器

2.2权重计算规则

(1).第一等:代表内联样式,如: style=””,权值为1000。

(2).第二等:代表ID选择器,如:#content,权值为0100。

(3).第三等:代表类,伪类和属性选择器,如.content,权值为0010。

(4).第四等:代表类型选择器和伪元素选择器,如div p,权值为0001。

(5).通配符、子选择器、相邻选择器等的。如*、>、+,权值为0000。

(6).继承的样式没有权值。

2.3CSS优先级算法如何计算?

优先级就近原则,同权重情况下样式定义最近者为准;

载入样式以最后载入的定位为准;

优先级为:

同权重: 内联样式表(标签内部)> 嵌入样式表(当前文件中)> 外部样式表(外部文件中)。

   !important >  id > class > tag

important比 内联优先级高

JavaScript部分:

1.简单说浏览器本地存储是怎样的?

比较详细:https://blog.csdn.net/u011927449/article/details/107123151/

着重看常用的cookie sessionStorage localStorage 然后主动扩展到vuex与上述存储的区别

2.对象的拷贝?

2.1深拷贝浅拷贝

Js的数据类型有基本数据类型和引用类型,基本数据类型保存在栈内存,引用类型保存在堆内存。

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。

深拷贝就是能够实现真正意义上的数组和对象的拷贝。递归调用"浅拷贝"。

2.2具体深拷贝的方法

jquery有提供一个$.extend可以用来做 Deep Copy。

函数库lodash,有提供_.cloneDeep用来做 Deep Copy。

通过JSON对象来实现深拷贝先JSON.stringify再JSON.parse(无法实现对对象中方法的深拷贝)

手动循环遍历赋值

使用ES6的扩展运算符 实现深拷贝

3.防抖与节流?

首先明确应用场景:在一些持续触发的事件:resize, scroll, mousemove等 不希望频繁的执行函数。

防抖(debounce)触发事件后的n秒内函数只执行一次,重复触发,重新计算函数执行时间。

节流(throttle)连续触发事件,但是n秒内只执行一次。重复触发不重新计算时间。

4.函数柯里化?

函数的柯里化,是Javascript 中函数式编程的一个重要概念。它返回的,是一个函数的函数。其实现方式,需要依赖参数以及递归,通过拆分参数的方式,来调用一个多参数的函数方法,以达到减少代码冗余,增加可读性的目的。

框架VUE部分:

1.Vue的双向绑定的原理是什么?(重点记粗体其余用自己的话串联)

实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图4、mvvm入口函数,整合以上三者

2.Vue生命周期钩子函数有哪些?

从创建到销毁4个步骤 分别加before 和ed,Beforecreate created,Beforemount mounted,Beforeupdate updated,Beforedestory destoryed

3.Vue组件中 data 为什么必须是函数?

与js本身的特性相关

如果data是一个对象,那么重复使用相同的组件,每个组件都会从data对象中取值,也是取的同一个值,这就会对组件之间造成互相干扰。

如果是一个函数,在每次组件实例化的时候会return出不同地址的对象,不会干扰组件。

4.vuex的使用场景, 属性, 对应的作用?

场景:使用vuex 解决跨组件通信问题,或者是相隔多个层级

属性:vuex作为数据存储中心,主要存放:1、组件之间全局共享的数据2、通过后端异步请求的数据

作用:vuex的几个核心概念

Store:Vuex 使用一个 Store 对象管理应用的状态,一个 Store 包括 State, Getter, Mutation, Action 四个属性。

State:State 意为“状态”,是 vuex 状态管理的数据源

Getter:Getter 的作用与 filters 有一些相似,可以将 State 进行过滤后输出。

Mutation:Mutaion 是 vuex 中改变 State 的唯一途径(严格模式下),并且只能是同步操作。Mutaion 使得状态变得可追踪,配合一些 devtools 可以实现 time-travel 的调试体验。

Action:一些对 State 的异步操作可以放在Action 中,并通过在 Action 提交 Mutaion 变更状态。

Module:当 Store 对象过于庞大时,可根据具体的业务需求分为多个Module。

5:说说Vue Router的路由模式 hash 和 history 的实现原理?

5.1:hash路由模式的实现特性:

1.URL中hash值只是客户端的一种状态,当服务器端发送请求时,hasj部分不会被发送

2.Hash值的改变,都会在浏览器的访问历史中增加一个记录,因此我们能通过浏览器的回退,前进按钮控制hash的切换

3.可以通过a标签,并设置href属性,当用户点击这个标签后,URL的hash值会发生改变,4.或者使用JavaScript来对loaction.hash进行赋值,改变URL的hash值

我们可以使用hashchange事件来监听hash值的变化,从而对页面进行跳转(渲染)

5.2:history模式中的原理:

History中有俩个神器就是pushState和replaceState

pushState: 浏览器不会向服务器请求数据,直接改变URL地址,可以类似的理解为变相版的hash,但不像hash一样,浏览器会记录pushState的历史记录,可以使用浏览器的前进后退功能作用

relpaceState:不同于pushState,replaceState仅仅是修改了对应的历史记录

5.3:hash 模式:#后面 hash 值的变化,不会导致浏览器向服务器发出请求,浏览器不发出请求,就不会刷新页面

通过监听hashchange 事件可以知道 hash 发生了哪些变化,然后根据 hash 变化来实现更新页面部分内容的操作。

5.4:history 模式:history模式的实现,主要是 HTML5 标准发布的两个 API,pushState 和 replaceState,这两个 API 可以在改变 url,但是不会发送请求。这样就可以监听 url 变化来实现更新页面部分内容的操作

5.5:区别:url展示上,hash 模式有“#”,history 模式没有

刷新页面时,hash 模式可以正常加载到 hash 值对应的页面,而 history 没有处理的话,会返回 404,一般需要后端将所有页面都配置重定向到首页路由

5.6:兼容性,hash可以支持低版本浏览器和 IE。

174道JavaScript 面试知识点总结(上)

174道JavaSciprt面试知识点总结(中)

174道JavaScript 面试知识点总结(下)

JS语法 ES6、ES7、ES8、ES9、ES10、ES11、ES12新特性

19个JavaScript数组常用方法总结

JavaScript 代码整洁之道

面试官问 Vue 性能优化,我该怎么回答

深入浅出前端本地储存

你可能感兴趣的:(2021-09-29前端面试题的积累)