整理不易如果觉得本文有帮助记得点赞三连哦十分感谢!
这个问题属于老生常谈的经典问题了下面给出面试简单版作答
所以推荐大家可以看看这篇详细作答史上最详细的经典面试题从输入URL到看到页面发生了什么?
事件委托的原理:不给每个子节点单独设置事件监听器,而是设置在其父节点上,然后利用冒泡原理设置每个子节点。
优点:
默认是冒泡
addEventListener第三个参数默认为false代表执行事件冒泡行为。
当为true时执行事件捕获行为。
在DOM树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于相同的坐标空间(z轴空间)时,就会形成一个RenderLayers,也就是渲染层。渲染层将保证页面元素以正确的顺序堆叠,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。
浏览器如何创建新的渲染层
注意!很多同学可能会忽略bind绑定的函数作为构造函数进行new实例化的情况
比如常见的防抖节流
//防抖functiondebounce(fn,delay=300){lettimer;//闭包引用的外界变量returnfunction(){constargs=arguments;if(timer){clearTimeout(timer);}timer=setTimeout(()=>{fn.apply(this,args);},delay);};}使用闭包可以在JavaScript中模拟块级作用域
注意!我在最后一个Promise埋了个坑我没有调用resolve方法这个是在面试美团的时候遇到了当时自己没看清楚以为都是一样的套路最后面试官说不对找了半天才发现是这个坑哈哈
Http3.0相对于Http2.0是一种脱胎换骨的改变!
这道题基本也是css经典题目但是网上已经有太多千篇一律的答案了如果大家想在这道题加分
可以针对定宽高和不定宽高的实现多种不同的方案
建议大家直接看面试官:你能实现多少种水平垂直居中的布局(定宽高和不定宽高)
整体思路是数据劫持+观察者模式
对象内部通过defineReactive方法,使用Object.defineProperty将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的dep属性,存放他所依赖的watcher(依赖收集),当属性变化后会通知自己对应的watcher去更新(派发更新)。
nextTick中的回调是在下次DOM更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。主要思路就是采用微任务优先的方式调用异步方法去执行nextTick包装的方法
建议直接看diff算法详解传送门
hash模式
window.addEventListener("hashchange",funcRef,false);每一次改变hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用hash的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了
特点:兼容性好但是不美观
history模式
利用了HTML5HistoryInterface中新增的pushState()和replaceState()方法。
这两个方法应用于浏览器的历史记录站,在当前已有的back、forward、go的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前URL改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。
特点:虽然美观,但是刷新会出现404需要后端进行配置
//真正的渲染函数function_render(vnode){//如果是数字类型转化为字符串if(typeofvnode==="number"){vnode=String(vnode);}//字符串类型直接就是文本节点if(typeofvnode==="string"){returndocument.createTextNode(vnode);}//普通DOMconstdom=document.createElement(vnode.tag);if(vnode.attrs){//遍历属性Object.keys(vnode.attrs).forEach((key)=>{constvalue=vnode.attrs[key];dom.setAttribute(key,value);});}//子数组进行递归操作这一步是关键vnode.children.forEach((child)=>dom.appendChild(_render(child)));returndom;}26手写-实现一个对象的flatten方法(阿里)题目描述
constobj={a:{b:1,c:2,d:{e:5}},b:[1,3,{a:2,b:3}],c:3}flatten(obj)结果返回如下//{//'a.b':1,//'a.c':2,//'a.d.e':5,//'b[0]':1,//'b[1]':3,//'b[2].a':2,//'b[2].b':3//c:3//}答案
给定一个只包括'(',')','{','}','[',']'的字符串s,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。示例1:输入:s="()"输出:true示例2:输入:s="()[]{}"输出:true示例3:输入:s="(]"输出:false答案
编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串""。示例1:输入:strs=["flower","flow","flight"]输出:"fl"示例2:输入:strs=["dog","racecar","car"]输出:""解释:输入不存在公共前缀。答案
给定一个字符串s,请你找出其中不含有重复字符的最长子串的长度。示例1:输入:s="abcabcbb"输出:3解释:因为无重复字符的最长子串是"abc",所以其长度为3。示例2:输入:s="bbbbb"输出:1解释:因为无重复字符的最长子串是"b",所以其长度为1。示例3:输入:s="pwwkew"输出:3解释:因为无重复字符的最长子串是"wke",所以其长度为3。请注意,你的答案必须是子串的长度,"pwke"是一个子序列,不是子串。示例4:输入:s=""输出:0答案
浏览器在处理下面的css的时候,会使用GPU渲染
采用transform:translateZ(0)采用transform:translate3d(0,0,0)使用CSS的will-change属性。will-change可以设置为opacity、transform、top、left、bottom、right。注意!层爆炸,由于某些原因可能导致产生大量不在预期内的合成层,虽然有浏览器的层压缩机制,但是也有很多无法进行压缩的情况,这就可能出现层爆炸的现象(简单理解就是,很多不需要提升为合成层的元素因为某些不当操作成为了合成层)。解决层爆炸的问题,最佳方案是打破overlap的条件,也就是说让其他元素不要和合成层元素重叠。简单直接的方式:使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰合成的排序,可以有效减少创建不必要的合成层,提升渲染性能,移动端优化效果尤为明显。
1.工厂模式-传入参数即可创建实例
虚拟DOM根据参数的不同返回基础标签的Vnode和组件Vnode
2.单例模式-整个程序有且仅有一个实例
vuex和vue-router的插件注册方法install判断如果系统存在实例就直接返回掉
3.发布-订阅模式(vue事件机制)
4.观察者模式(响应式数据原理)
5.装饰模式:(@装饰器的用法)
6.策略模式策略模式指对象有某个行为,但是在不同的场景中,该行为有不同的实现方案-比如选项的合并策略
...其他模式欢迎补充
使用了对称加密可非对称加密的混合方式
具体过程请看前端进阶高薪必看-HTTPS篇
flex实际上是flex-grow、flex-shrink和flex-basis三个属性的缩写。
flex-grow:定义项目的的放大比例;
默认为0,即即使存在剩余空间,也不会放大;所有项目的flex-grow为1:等分剩余空间(自动放大占位);flex-grow为n的项目,占据的空间(放大的比例)是flex-grow为1的n倍。flex-shrink:定义项目的缩小比例;
默认为1,即如果空间不足,该项目将缩小;所有项目的flex-shrink为1:当空间不足时,缩小的比例相同;flex-shrink为0:空间不足时,该项目不会缩小;flex-shrink为n的项目,空间不足时缩小的比例是flex-shrink为1的n倍。flex-basis:定义在分配多余空间之前,项目占据的主轴空间(mainsize),浏览器根据此属性计算主轴是否有多余空间
默认值为auto,即项目原本大小;设置后项目将占据固定空间。7304是什么意思一般什么场景出现,命中强缓存返回什么状态码协商缓存命中返回304
Last-Modified:Thu,20Jun201915:58:05GMT
当再次请求该资源时,浏览器需要再次向服务器确认,资源是否过期,其中的凭证就是请求头If-Modified-Since字段,值为上次请求中响应头Last-Modified字段的值:
If-Modified-Since:Thu,20Jun201915:58:05GMT
另外,浏览器在发送请求的时候服务器会检查请求头(requestheader)里面的if-none-match的值与当前文件的内容通过hash算法(例如nodejs:cryto.createHash('sha1'))生成的内容摘要字符对比,相同则直接返回304,否则给返回头(responseheader)添加etag属性为当前的内容摘要字符,并且返回内容。
综上总结为:
请求头last-modified的日期与响应头的last-modified一致请求头if-none-match的hash与响应头的etag一致这两种情况会返回StatusCode:304强缓存命中返回200200(fromcache)
//src/global-api/initExtend.jsimport{mergeOptions}from"../util/index";exportdefaultfunctioninitExtend(Vue){letcid=0;//组件的唯一标识//创建子类继承Vue父类便于属性扩展Vue.extend=function(extendOptions){//创建子类的构造函数并且调用初始化方法constSub=functionVueComponent(options){this._init(options);//调用Vue初始化方法};Sub.cid=cid++;Sub.prototype=Object.create(this.prototype);//子类原型指向父类Sub.prototype.constructor=Sub;//constructor指向自己Sub.options=mergeOptions(this.options,extendOptions);//合并自己的options和父类的optionsreturnSub;};}具体可以看看这篇手写Vue2.0源码(八)-组件原理
答案是:不能
pushState和replaceState
HTML5新接口,可以改变网址(存在跨域限制)而不刷新页面,这个强大的特性后来用到了单页面应用如:vue-router,react-router-dom中。
注意:仅改变网址,网页不会真的跳转,也不会获取到新的内容,本质上网页还停留在原页面
window.history.pushState(state,title,targetURL);@状态对象:传给目标路由的信息,可为空@页面标题:目前所有浏览器都不支持,填空字符串即可@可选url:目标url,不会检查url是否存在,且不能跨域。如不传该项,即给当前url添加datawindow.history.replaceState(state,title,targetURL);@类似于pushState,但是会直接替换掉当前url,而不会在history中留下记录popstate事件会在点击后退、前进按钮(或调用history.back()、history.forward()、history.go()方法)时触发
注意:用history.pushState()或者history.replaceState()不会触发popstate事件
Treeshaking是一种通过清除多余代码方式来优化项目打包体积的技术,专业术语叫Deadcodeelimination
treeshaking的原理是什么
ES6Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码扩展:common.js和es6中模块引入的区别?
CommonJS是一种模块规范,最初被应用于Nodejs,成为Nodejs的模块规范。运行在浏览器端的JavaScript由于也缺少类似的规范,在ES6出来之前,前端也实现了一套相同的模块规范(例如:AMD),用来对前端模块进行管理。自ES6起,引入了一套新的ES6Module规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对ES6Module兼容还不太好,我们平时在Webpack中使用的export和import,会经过Babel转换为CommonJS规范。在使用上的差别主要有:
1、CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用。
2、CommonJS模块是运行时加载,ES6模块是编译时输出接口(静态编译)。
3、CommonJs是单个值导出,ES6Module可以导出多个
4、CommonJs是动态语法可以写在判断里,ES6Module静态语法只能写在顶层
5、CommonJs的this是当前模块,ES6Module的this是undefined
Babel是一个JavaScript编译器。他把最新版的javascript编译成当下可以执行的版本,简言之,利用babel就可以让我们在当前的项目中随意的使用这些新最新的es6,甚至es7的语法。
Babel的三个主要处理步骤分别是:解析(parse),转换(transform),生成(generate)。
还想深入了解的可以看[实践系列]Babel原理
请写出下面的答案
requestAnimationFrame:告诉浏览器在下次重绘之前执行传入的回调函数(通常是操纵dom,更新动画的函数);由于是每帧执行一次,那结果就是每秒的执行次数与浏览器屏幕刷新次数一样,通常是每秒60次。
这个题目可以深入去问浏览器每一帧的渲染流程具体可以看看这篇requestIdleCallback和requestAnimationFrame详解
原始es6代码
这道题是字节终面的最后一个题目属于开放性问题没有固定答案我当时觉得题目概念太大了把我整懵了我只是回答了下浏览器渲染原理啥的貌似面试官不太满意哈哈如果叫你设计一个渲染引擎应该从哪些方面着手呢
require基本原理
require查找路径
require和module.exports干的事情并不复杂,我们先假设有一个全局对象{},初始情况下是空的,当你require某个文件时,就将这个文件拿出来执行,如果这个文件里面存在module.exports,当运行到这行代码时将module.exports的值加入这个对象,键为对应的文件名,最终这个对象就长这样:
{"a.js":"helloworld","b.js":functionadd(){},"c.js":2,"d.js":{num:2}}当你再次require某个文件时,如果这个对象里面有对应的值,就直接返回给你,如果没有就重复前面的步骤,执行目标文件,然后将它的module.exports加入这个全局对象,并返回给调用者。这个全局对象其实就是我们经常听说的缓存。所以require和module.exports并没有什么黑魔法,就只是运行并获取目标文件的值,然后加入缓存,用的时候拿出来用就行。
具体可以看看这篇深入Node.js的模块加载机制,手写require函数