当前位置: 首页 > Web前端 > HTML

美团前端面试高频题合集

时间:2023-03-28 02:08:22 HTML

手写bind,apply,call//callFunction.prototype.call=function(context,...args){context=context||窗户;constfnSymbol=Symbol("fn");上下文[fnSymbol]=这个;上下文[fnSymbol](...args);deletecontext[fnSymbol];}//applyFunction.prototype.apply=function(context,argsArr){context=context||}窗户;constfnSymbol=Symbol("fn");上下文[fnSymbol]=这个;上下文[fnSymbol](...argsArr);deletecontext[fnSymbol];}//bindFunction.prototype.bind=function(context,...args){context=context||}窗户;constfnSymbol=Symbol("fn");上下文[fnSymbol]=这个;返回函数(..._args){args=args。连接(_args);上下文[fnSymbol](...args);删除上下文[fnSymbol];}}前端高级面试题详解Throttle:触发高频事件,N秒内只执行一次。这就像公交车,每10分钟一班,我不管10分钟内有多少人在公交车站等,10分钟一到我就走!类似于QQ飞车的重置按钮。核心思想:使用timestamp或者flag来实现,立即执行一次,然后每N秒执行一次。如果在N秒内触发,则直接返回。应用:Throttling常应用于鼠标点击触发和监听滚动事件。Implementation://version1:flag实现函数throttle(fn,wait){letflag=true;//设置标志returnfunction(...args){if(!flag)return;标志=假;setTimeout(()=>{fn.call(this,...args);flag=true;},wait);}}//版本2:时间戳实现函数throttle(fn,wait){letpre=0;returnfunction(...args){letnow=newDate();}如果(现在-预<等待)返回;之前=现在;fn.call(this,...args);}}代码输出问题functionfun(n,o){console.log(o)return{fun:function(m){returnfun(m,n);}};}vara=fun(0);a.乐趣(1);a.乐趣(2);a.乐趣(3);变种b=乐趣(0).乐趣(1).乐趣(2).乐趣(3);变种c=乐趣(0).乐趣(1);c.乐趣(2);c.乐趣(3);输出:undefined000undefined012undefined011这是一个关于闭包的问题。对于fun方法,调用后返回一个对象。我们知道,在调用函数时,传入的实参个数少于函数声明中指定的形参个数,剩下的形参会被设置为未定义的值。所以console.log(o);将输出未定义。而a是fun(0)返回的对象。也就是说,函数fun中参数n的值为0,而在返回的对象中,需要一个参数n,而这个对象的作用域内没有n,则继续沿作用域到nextlevelscope寻找n,最后在函数fun中找到n,n的值为0。知道了这些,其他的操作就简单了,以此类推。告诉我JSON.stringify的缺点是什么?1.如果obj中有时间对象,那么JSON.stringify后JSON.parse的结果,时间只会是字符串的形式,不会是对象的形式。2、如果有RegExp(正则表达式的缩写),Errorinobj对象,序列化的结果只会得到一个空对象;3.如果obj中有一个函数,undefined,序列化的结果会丢失这个函数或者undefined;4.如果obj中存在NaN、Infinity和-Infinity,则序列转换的结果会变成null5。JSON.stringify()只能序列化对象的可枚举自身属性。比如obj中的对象是由构造函数生成的,使用JSON.parse(JSON.stringify(obj))深拷贝后,对象的构造函数会被丢弃;6.如果对象中存在循环引用,则无法正确实现深拷贝;使用vw实现具有自适应宽度和高度的正方形:.square{width:10%;身高:10vw;background:tomato;}利用元素相对于父元素宽度的margin/padding百分比来实现:.square{width:20%;高度:0;顶部填充:20%;背景:橙色;}利用子元素的margin-top值实现:.square{width:30%;溢出:隐藏;背景:黄色;}.square::after{内容:'';显示:块;margin-top:100%;}剩余参数的理解当展开运算符用在函数参数上时,也可以将一个分离的参数序列整合到一个数组中:functionmultiple(...args){letresult=1;for(varvalofargs){结果*=val;}returnresult;}mutiple(1,2,3,4)//24这里给mutiple传入了四个独立的参数,但是如果你尝试在mutiple函数值中输出args,会发现是一个数组:functionmultiple(...args){console.log(args)}multiple(1,2,3,4)//[1,2,3,4]这是...rest的另一层operator功能强大,可以将函数的多个输入参数收敛到一个数组中。这个常用于获取函数的冗余参数,或者像上面这样处理函数参数个数不确定的情况。原型构造函数是一种特殊方法,主要用于在对象创建时对其进行初始化。每个构造函数都有一个原型(prototype)(箭头函数而Function.prototype.bind()没有)属性,这个原型(prototype)属性是一个指向对象的指针,这个对象的目的是包含一个特定对象的所有实例typeSharedpropertiesandmethods,即这个原型对象用于共享实例对象的属性和方法。每个实例对象的__proto__指向这个构造函数/类的原型属性。面向对象三大特性:继承/多态/封装关于new操作符:1.new执行的函数,函数内部默认生成一个对象2.函数内部的this指向这个new生成的对象默认情况下3.新执行函数生成的对象是函数的默认返回值ES5例子:functionPerson(obj){this.name=obj.namethis.age=obj.age}//prototypemethodPerson.prototype.say=function(){console.log('Hello,',this.name)}//p为实例化对象,newPerson()的操作调用构造函数的实例化letp=newPerson({name:'Tomato',age:'27'})console.log(p.name,p.age)p.say()ES6例子:classPerson{constructor(obj){this.name=obj.namethis.age=obj.age}say(){console.log(this.name)}}letp=newPerson({name:'ES6-Tomato',age:'27'})console.log(p.name,p.age)p.say()src和href的区别src和href用来引用外部资源,它们的区别如下:src:表示对资源的引用,它指向的内容会嵌入到当前标签的位置。src会把它指向的资源下载并应用到文档中,比如请求js脚本。浏览器在解析该元素时,会暂停其他资源的下载和处理,直到该资源加载、编译、执行完毕,所以一般js脚本都会放在页面底部。href:表示超文本引用,指向一些网络资源,与当前元素或本文档建立链接关系。当浏览器识别出它指向的文件时,它会在不停止处理当前文档的情况下并行下载资源。常用于a和link等标签。第一种数组去重:通过ES6新特性Set()例如:vararr=[1,2,3,1,2];varnewArr=[...newSet(arr)]第二种:封装函数使用{}和[]functionuniqueEasy(arr){if(!arrinstanceofArray){throwError('当前输入不是数组')}letlist=[]letobj={}arr.forEach(item=>{if(!obj[item]){list.push(item)obj[item]=true}})returnlist}当然还有其他的方法,但是我的项目中一般都是用上面这两种方法基本满足map和foreach的要求有什么区别呢?foreach()方法将为每个元素执行提供的函数。该方法没有返回值。是否会改变原数组取决于数组元素的类型是基本类型还是引用类型。map()方法不会改变原始数组的值。,返回一个新数组,新数组中的值是调用原数组函数后的值:什么是闭包,闭包的作用是什么当调用内部函数时,会形成一个闭包,并且闭包可以读取在其他函数中获取变量的函数。闭包功能:局部变量不能长期共享保存,而全局变量可能会造成变量污染,所以我们希望有一种机制可以长期保存变量,不会造成全局污染。什么是内联元素?什么是块级元素?什么是空(void)元素?行内元素包括:abspanimginputselectstrong;块级元素包括:divulollidldtddh1h2h3h4h5h6p;空元素,即没有内容的HTML元素。空元素在开始标签中关闭,即空元素没有结束标签:常见的有:


;罕见的是:、、、。px、em、rem的区别以及三种使用场景的区别:px是一个固定的像素,一旦设置,就不能改变以适应页面大小。Em和rem比px更灵活。它们是相对长度单位,长度不固定,更适合响应式布局。em设置了相对于其父元素的字体大小,所以有问题。对于任何元素设置,可能需要知道其父元素的大小。而rem是相对于根元素的,也就是说只需要在根元素上确定一个参考值即可。使用场景:对于只需要适配少量移动设备,分辨率对页面影响不大的,使用px。对于适配各种移动设备的需求,使用rem,比如需要适配iPhone、iPad等分辨率差异比较大的设备。说说slicesplicesplit的区别?//slice(start,[end])//slice(start,[end])方法:该方法是对数组进行部分截取,该方法返回一个新数组//参数start是该数组的起始索引截取,end参数等于要提取的最后一个字符的位置值加1(可选)。//包含源函数指定的从头到尾的元素,但不包括结束元素,如a.slice(0,3);//如果有负数,把负数加到长度上再除。//如果切片中负数的绝对值大于数组长度,则显示所有数组//如果只有一个参数,且参数大于长度,则为空。//如果结束位置小于开始位置,则返回一个空数组//返回的数为结束-开始的个数//原数组不变vararr=[1,2,3,4,5,6]/*console.log(arr.slice(3))//[4,5,6]从下标0到3,截取3之后的数console.log(arr.slice(0,3))//[1,2,3]截取下标0到下标3之前的数console.log(arr.slice(0,-2))//[1,2,3,4]console.log(arr.slice(-4,4))//[3,4]console.log(arr.slice(-7))//[1,2,3,4,5,6]console.log(arr.slice(-3,-3))//[]console.log(arr.slice(8))//[]*///个人总结:如果slice的参数是正数,从左往右数,如果是负数,从右往左数。//截取的数组与数字方向一致。如果有两个参数,截取的就是数字的交集。如果没有交集,将返回一个空数组。//ps:slice也可以切String,用法同array,但注意空格也算字符//splice(start,deletecount,item)//start:起始位置//deletecount:位数todelete//item:replaceditem//返回值为被删除的字符串//如果有额外的参数,item将被插入到被删除元素的位置。//splice:remove,splice方法从数组中移除一个或多个数组,并用新项替换它们。//举个简单的例子vara=['a','b','c'];varb=a.splice(1,1,'e','f');console.log(a)//['a','e','f','c']console.log(b)//['b']vara=[1,2,3,4,5,6];//console.log("删除:",a.splice(1,1,8,9));//删除:2//console.log("数组元素:",a);//1,8,9,3,4,5,6//console.log("删除:",a.splice(0,2));//Deleted:1,2//console.log("数组元素:",a)//3,4,5,6console.log("Deleted:",a.splice(1,0,2,2))//插入第二个数字为0,即删除0console.log("数组元素:",a)//1,2,2,2,3,4,5,6//split(string)//string.split(separator,limit):split方法将字符串分割成段,创建一个字符串数组。//可选参数limit可以限制分割的段数。//分隔符参数可以是字符串或正则表达式。//如果separator为空字符,则返回单字符数组,不改变原数组。vara="0123456";varb=a.split("",3);console.log(b);//b=["0","1","2"]//注:字符串。split()的作用与Array.join的作用相反。Vuex有哪些基本属性?为什么Vuex的mutation不能进行异步操作?一共有五种,分别是State、Getter、Mutation、Action、Module1、state=>basicdata(数据源存放位置)2、getters=>frombasicData派生数据3、mutations=>提交改变数据的方法,同步4.动作=>像装饰器一样,包装突变以使其异步。5.模块=>模块化Vuex1。在Vuex中更新所有状态的唯一方法是突变。异步操作是通过Actions提交mutation来实现的,可以很方便的跟踪每一个状态的变化,这样可以实现一些工具来帮助更好的了解我们的应用。2.每一次mutation执行后,都会对应一次新的状态变化,所以devtools可以抓拍保存,然后就可以实现时间旅行了。如果mutation支持异步操作,则无法知道状态何时更新,也无法很好地跟踪状态,这给调试带来了困难。告诉我为什么数据是函数而不是对象?JavaScript中的对象是引用类型数据。当多个实例引用同一个对象时,只要一个实例对这个对象进行操作,其他实例中的数据也会发生变化。.在Vue中,我们希望更多地复用组件,所以每个组件都需要有自己的数据,这样组件之间才不会相互干扰。所以组件的数据不能写成对象的形式,而要写成函数的形式。数据以函数返回值的形式定义,这样我们每复用一个组件,就会返回一个新的数据,也就是说每个组件都有自己私有的数据空间,各自维护自己的数据。会干扰其他部件的正常运行。说说前端性能优化方案的三个方面来说明前端性能优化1:webapck优化和启用gzip压缩1.babel-loader使用include或exclude帮助我们避免不必要的翻译,不要翻译node_moudules中的js文件缓存当前翻译的js文件,设置loader:'babel-loader?cacheDirectory=true'2.按需加载文件等3.具体方法很简单,只需要在你的请求头中加上这句话:accept-encoding:gzip4.图片优化,使用svg图片或字体图标5.浏览器缓存机制,分为强缓??存和协商缓存二:本地存储——从Cookie到WebStorage,IndexedDB详解SessionStorage和localStorage有还有cookies的区别和优缺点三:代码优化1.事件代理2.事件节流防抖3.页面回流和重绘4.EventLoop事件循环机制5.代码优化等