前端工程师面试题

一、 请说出至少3种减少页面加载的方法?

1、减少http请求(合并文件、合并图片)

2、优化图片文件,减小其尺寸,特别是缩略图,一定要按尺寸生成缩略图然后调用,不要在网页中用resize方法实现,虽然这样看到的图片外形小了,但是其加载的数据量一点也没减少。曾经见过有人在网页中加载的缩略图,其真实尺寸有10M之巨…普通图像、icon也要尽可能压缩后,可以采用web图像保存、减少颜色数等等方法实现。

3、图像格式的选择(GIF:提供的颜色较少,可用在一些对颜色要求不高的地方)

4、压缩JavaScript、CSS代码:一般js、css文件中存在大量的空格、换行、注释,这些利于阅读,如果能够压缩掉,将会很有利于网络传输。这方面的工具也有很多,可以在百度里搜索一下关键字“css代码压缩”,或者“js代码压缩”将会发现有很多网站都提供这样的功能,当然了你也可以自己写程序来做这个工作,如果你会的话。就拿我们这个网站来说吧。刚开始上传这个网站的时候,我的很多Css代码都没有压缩,后面发现了这个问题,我就上网找了相关的网站的压缩代码的功能,最后就把很多 CSS文件都压缩了。这个压缩比率还是比较高的,一般都有百分五十左右。这个代码压缩对于网页的加载还是很有用的。

5、服务器启用gzip压缩功能:将要传输的文件压缩后传输到客户端再解压,在网络传输数据量会大幅减小。在服务器上的Apache、Nginx可直接启用,也可用代码直接设置传输文件头,增加gzip的设置,也可从负载均衡设备直接设置。不过需要留意的是,这个设置会略微增加服务器的负担。服务器性能不是很好的网站,要慎重考虑。

6、标明高度和宽度(如果浏览器没有找到这两个参数,它需要一边下载图片一边计算大小,如果图片很多,浏览器需要不断地调整页面。这不但影响速度,也影响浏览体验。当浏览器知道了高度和宽度参数后,即使图片暂时无法显示,页面上也会腾出图片的空位,然后继续加载后面的内容。从而加载时间快了,浏览体验也更好了。)

7、网址后面加上“/”:对服务器而言,不加斜杠服务器会多一次判断的过程,加斜杠就会直接返回网站设置的存放在网站根目录下的默认页面。

二、 new操作符具体干了啥?

先看代码
var Func=function(){};
var func=new Func ();

new共经过了4几个阶段
1、创建一个空对象
var obj=new Object();
2、设置原型链
obj.proto= Func.prototype;
3、让Func中的this指向obj,并执行Func的函数体。
var result =Func.call(obj);
4、判断Func的返回值类型:
如果是值类型,返回obj;
如果是引用类型,就返回这个引用类型的对象。
if (typeof(result) == “object”){
func=result;
}
else{
func=obj;;
}

三、 什么是闭包?闭包的用途以及缺点?

定义:闭包当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数内部的变量,且返回的这个函数在外部被执行就产生了闭包.闭包是一个环境,具体指的就是外部函数–高阶函数。
说白了就是一个环境,能够读取其他函数内部的变量。
本质上,闭包是将函数内部和函数外部连接起来的桥梁。
用处:
1.读取函数内部的变量;
2.这些变量的值始终保持在内存中,不会在外层函数调用后被自动清除。
优点:
1:变量长期驻扎在内存中;
2:避免全局变量的污染;
3:私有成员的存在 ;
特性:
1:函数套函数;
2:内部函数可以直接使用外部函数的局部变量或参数;
3:变量或参数不会被垃圾回收机制回收 GC;
缺点:
常驻内存 会增大内存的使用量 使用不当会造成内存泄露,详解:
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

四、 一个页面从输入url到页面加载显示完成,这个过程发生了什么?

js html是基于http在浏览器和服务器之间进行传输(是响应的第四部分的字符串)
http协议基于TCP/IP协议
过程:
浏览器输入域名->浏览器查询dns是否有缓存->DNS查询到域名->TCP/IP链接(三次握手)->建立连接->浏览器发出请求->服务器响应(1.2.3.4)->浏览器会先获得响应头然后在获得相应体(因为响应体有时候会很大).
1、浏览器地址栏输入url
2、浏览器会先查看浏览器缓存–系统缓存–路由缓存,如有存在缓存,就直接显示。如果没有,接着第三步
3、域名解析(DNS)获取相应的ip
4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手
5、握手成功,浏览器向服务器发送http请求,请求数据包
6、服务器请求数据,将数据返回到浏览器
7、浏览器接收响应,读取页面内容,解析html源码,生成DOM树
8、解析css样式、浏览器渲染,js交互绑定多个域名,数量不限;
三次握手
知道了服务器的 IP 地址,下面便开始与服务器建立连接了。
通俗地讲,通信连接的建立需要经历以下三个过程:
主机向服务器发送一个建立连接的请求(您好,我想认识您);
服务器接到请求后发送同意连接的信号(好的,很高兴认识您);
主机接到同意连接的信号后,再次向服务器发送了确认信号(我也很高兴认识您),自此,主机与服务器两者建立了连接。
*补充说明
TCP 协议:三次握手的过程采用 TCP 协议,其可以保证信息传输的可靠性,三次握手过程中,若一方收不到确认信号,协议会要求重新发送信号。
网页请求与显示
当服务器与主机建立了连接之后,下面主机便与服务器进行通信。网页请求是一个单向请求的过程,即是一个主机向服务器请求数据,服务器返回相应的数据的过程。
浏览器根据 URL 内容生成 HTTP 请求,请求中包含请求文件的位置、请求文件的方式等等;
服务器接到请求后,会根据 HTTP 请求中的内容来决定如何获取相应的 HTML 文件;
服务器将得到的 HTML 文件发送给浏览器;
在浏览器还没有完全接收 HTML 文件时便开始渲染、显示网页;
在执行 HTML 中代码时,根据需要,浏览器会继续请求图片、CSS、JavsScript等文件,过程同请求 HTML ;

四次挥手
主机向服务器发送一个断开连接的请求(不早了,我该走了);
服务器接到请求后发送确认收到请求的信号(知道了);
服务器向主机发送断开通知(我也该走了);
主机接到断开通知后断开连接并反馈一个确认信号(嗯,好的),服务器收到确认信号后断开连接;
*补充说明
为什么服务器在接到断开请求时不立即同意断开:当服务器收到断开连接的请求时,可能仍然有数据未发送完毕,所以服务器先发送确认信号,等所有数据发送完毕后再同意断开。
第四次握手后,主机发送确认信号后并没有立即断开连接,而是等待了 2 个报文传送周期,原因是:如果第四次握手的确认信息丢失,服务器将会重新发送第三次握手的断开连接的信号,而服务器发觉丢包与重新发送的断开连接到达主机的时间正好为 2 个报文传输周期。

五、 简单介绍下Vue实现数据双向绑定的原理?

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,那么vue是如果进行数据劫持的,我们可以先来看一下通过控制台输出一个定义在vue初始化数据上的对象是个什么东西。
代码:
var vm = new Vue({
data: {
obj: {
a: 1
}
},
created: function () {
console.log(this.obj);
}
});
结果:
我们可以看到属性a有两个相对应的get和set方法,为什么会多出这两个方法呢?因为vue是通过Object.defineProperty()来实现数据劫持的。
Object.defineProperty( )是用来做什么的?它可以来控制一个对象属性的一些特有操作,比如读写权、是否可以枚举,这里我们主要先来研究下它对应的两个描述属性get和set,如果还不熟悉其用法,请点击这里阅读更多用法。
在平常,我们很容易就可以打印出一个对象的属性数据:
var Book = {
name: ‘vue权威指南’
};
console.log(Book.name); // vue权威指南
如果想要在执行console.log(book.name)的同时,直接给书名加个书名号,那要怎么处理呢?或者说要通过什么监听对象 Book 的属性值。这时候Object.defineProperty( )就派上用场了,代码如下:
var Book = {}
var name = ‘’;
Object.defineProperty(Book, ‘name’, {
set: function (value) {
name = value;
console.log(‘你取了一个书名叫做’ + value);
},
get: function () {
return ‘《’ + name + ‘》’
}
})

Book.name = ‘vue权威指南’; // 你取了一个书名叫做vue权威指南
console.log(Book.name); // 《vue权威指南》

我们通过Object.defineProperty( )设置了对象Book的name属性,对其get和set进行重写操作,顾名思义,get就是在读取name属性这个值触发的函数,set就是在设置name属性这个值触发的函数,所以当执行 Book.name = ‘vue权威指南’ 这个语句时,控制台会打印出 “你取了一个书名叫做vue权威指南”,紧接着,当读取这个属性时,就会输出 “《vue权威指南》”,因为我们在get函数里面对该值做了加工了。如果这个时候我们执行下下面的语句,控制台会输出什么?

乍一看,是不是跟我们在上面打印vue数据长得有点类似,说明vue确实是通过这种方法来进行数据劫持的。

六、 Vue单页面中有时更改第三方UI框架样式不生效,为什么?此时如何更改?需要注意什么?

这时候想到了用深度选择器解决这个问题;元素名前加/deep/
/deep/.van-cell {
padding-left: 10px;
.fontSize(16);
color: @fontColor33;
}

深度选择器一般用于组件开发,修改组件内的某些样式
深度选择器表示种类:
“/deep/”
“>>>”
“::deep”
“:v-deep”

七、 写出Vue、React项目循环时为什么要在组件中写key,作用是什么?

首先了解一下虚拟dom和diff算法
虚拟dom:用一个简单的对象去替代复杂的dom对象,该对象存储了对应dom的一些重要参数,在改变dom之前,会先比较相应的虚拟dom,如果需要改变,才会将相应的改变应用到真实dom上
diff算法(差异算法):用新渲染的对象树和旧的树进行对比,记录这两颗树的差异,记录下来的不同就是我们需要对页面正真的dom操作,然后把他们应用到正真的dom操作中,进行页面的变更;虽然每次视图结构都是整个全新的渲染,但是最后操作的只是dom不同的地方

写key的作用相当于给每一个vnode绑定一个id,可以根据这个key,更准确,更快的拿到oldVNode对应的VNode(diff算法中比较的对象)
更准确:带key就不是就地复用,在sameNode函数 a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。
更快:利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。

其实我们在正真写循环的时候,有时候不写key也不会出现什么问题,页面也会正常的渲染,我们的一些操作也能正确的得到相应,只是会跳出警告让你加上key,有时候我们为了省事直接让key = index,其实这些做法都是有一定的弊端的;
在一些情况下,不加key的速度会比加上key的速度更快,但是这并不是不加key的作用,而实没有key的情况下对节点的就地复用,可能提高性能;所谓的就地复用是不加key的一种渲染机制,因为没有key,就没有一个对象的一个标识,如果对里面内容进行调换位置,只是用需要修改的内容去替换已有的内容,增加也是相当于在最后多添加了一个对象;但是如果拥有了key,那么已经渲染的内容就相当于拥有了一个id,如果是调换位置,那么不回去修改内容,而是实现真正的去改变对应的位置
为了更好理解就地复用,可以这样想象,相当于每个人都有一个个座位,但是这些座位是没有名字的,谁都可以使用,今天可以我坐一号,明天别人也可以坐一号;而加上key,就相当于给这些座位写了每个人的名字,每个人每次都是坐自己的,如果某个人走了,顺带着自己的位置也走了,不会应该又来了一个人把这个位置分给他。

不加key的副作用:可能不会产生过度效果,或者在某些绑定节点数据(表单)状态,会出现错位,Vue的文档也声明了,”这个默认的模式是搞笑的,但是只适用于不依赖子组件状态或临时dom状态(如:表单输入值)的列表渲染输出“
有时习惯使用key = index,这样做虽然给了一个key,但是如果删除某个内容,重新渲染之后,删除中间的任意一项(假如由三项,删除2),index会从123 ----> 12,而不是期待的由123 ----> 13,如果只为渲染可以使用
所以尽量在列表组中写key,不仅可以避免一些莫名的bug,也可以提高代码规范

八、 请写出下面的代码执行机制?(非输出值)为什么会这样?

for(var i = 0; i <= 5; i++ ){
setTimeout(function() {
console.log(i);
}, 1000);
}
setTimeout是异步执行的,1000毫秒后向任务队列里添加一个任务,只有主线上的全部执行完才会执行任务队列里的任务,所以当主线程for循环执行完之后 i 的值为5,这个时候再去任务队列中执行任务,i全部为5;
每次for循环的时候setTimeout都会执行,但是里面的function则不会执行被放入任务队列,因此放了5次;for循环的5次执行完之后不到1000毫秒;
1000毫秒后全部执行任务队列中的函数,所以就是输出五个5啦

假如把var换成let,那么输出结果为0,1,2,3,4;
因为let i 的是区块变量,每个i只能存活到大括号结束,并不会把后面的for循环的 i 值赋给前面的setTimeout中的i;而var i 则是局部变量,这个 i 的生命周期不受for循环的大括号限制。

九、 什么是跨域?怎么解决跨域问题?

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制。
由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一个与当前页面地址不同即为跨域。存在跨域的情况:
网络协议不同,如http协议访问https协议。
端口不同,如80端口访问8080端口。
域名不同,如qianduanblog.com访问baidu.com。
子域名不同,如abc.qianduanblog.com访问def.qianduanblog.com。
域名和域名对应ip,如www.a.com访问20.205.28.90.

所谓同源是指,域名,协议,端口均相同,不明白没关系,举个栗子:
http://www.123.com/index.html 调用 http://www.123.com/server.php (非跨域)
http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)
http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)
http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)

请注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域。
浏览器执行javascript脚本时,会检查这个脚本属于哪个页面,如果不是同源页面,就不会被执行。
解决办法:
1、JSONP:
使用方式就不赘述了,但是要注意JSONP只支持GET请求,不支持POST请求。
2、代理:
例如www.123.com/index.html需要调用www.456.com/server.php,可以写一个接口www.123.com/server.php,由这个接口在后端去调用www.456.com/server.php并拿到返回值,然后再返回给index.html,这就是一个代理的模式。相当于绕过了浏览器端,自然就不存在跨域问题。
3、PHP端修改header(XHR2方式)
在php接口脚本中加入以下两句即可:
header(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
header(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式

十、 你如何优化SPA应用的首屏加载速度慢的问题?

1、安装动态懒加载所需插件
babel-plugin-syntax-dynamic-import
配置/laravel/.babelrc
{
“plugins”: [“syntax-dynamic-import”]
}
2、去掉.extract()
它包含需要编译的文件类型,现在vue等静态资源从CDN加速
3、配置output输出形式
chunkFilename: ‘js/chunk/[name].js?v=[chunkHash]’
4、配置externals
它所包含的文件类型将被排除,避免不必要的静态资源包含进来,导致编译出的文件过大
5、在首页HTML挂载点之前引入静态资源CDN
6、配置vue路由
由require()方式修改为() => import()方式
{
path: ‘/home/wx/auth’,
component: () => import (’./views/home/wx/Auth’)
}

十一、 闭包需要注意什么?

不要滥用闭包,因为闭包会使得函数中的变量一直保存在内存中,内存的消耗很大。
在IE下,使用闭包有可能导致内存泄漏,所以我们在使用闭包的时候要注意,在退出函数之前,将不使用的闭包全部删除
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
讲完闭包我么再了解一下和闭包有关的js中的垃圾回收机制,以及内存泄漏的相关知识
什么是垃圾回收机制?
Js具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行。
JS中最常见的垃圾回收方式是标记清除。
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
1.垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
2.去掉环境中的变量以及被环境中的变量引用的变量的标记。
3.再被加上标记的会被视为准备删除的变量。
4.垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
引用计数 方式

工作原理:跟踪记录每个值被引用的次数。
工作流程:
1.声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值的引用次数就是1。
2.同一个值又被赋值给另一个变量,这个引用类型值的引用次数加1.
3.当包含这个引用类型值的变量又被赋值成另一个值了,那么这个引用类型值的引用次数减1.
4.当引用次数变成0时,说明没办法访问这个值了。
5.当垃圾收集器下一次运行时,它就会释放引用次数是0的值所占的内存。
但是循环引用的时候就会释放不掉内存。循环引用就是对象A中包含另一个指向对象B的指针,B中也包含一个指向A的引用。
因为IE中的BOM、DOM的实现使用了COM,而COM对象使用的垃圾收集机制是引用计数策略。所以会存在循环引用的问题。
解决:手工断开js对象和DOM之间的链接。赋值为null。IE9把DOM和BOM转换成真正的JS对象了,所以避免了这个问题。
什么是内存泄漏?
简单点说,不再用到的内存,没有及时释放,就叫内存泄漏
当页面跳转的时候,变量不会释放,一直存在于内存当中,然后使你的CPU在累加,在提高,只有当你关闭浏览器的时候,内存才会被释放。
什么情况会引起内存泄漏?
虽然有垃圾回收机制但是我们编写代码操作不当还是会造成内存泄漏。
1.意外的全局变量引起的内存泄漏。
原因:全局变量,不会被回收。
解决:使用严格模式避免。
2.闭包引起的内存泄漏
原因:闭包可以维持函数内局部变量,使其得不到释放。
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中,删除对dom的引用。
3.没有清理的DOM元素引用
原因:虽然别的地方删除了,但是对象中还存在对dom的引用
解决:手动删除。
4.被遗忘的定时器或者回调
原因:定时器中有dom的引用,即使dom删除了,但是定时器还在,所以内存中还是有这个dom。
解决:手动删除定时器和dom。
5.子元素存在引用引起的内存泄漏
原因:div中的ul li 得到这个div,会间接引用某个得到的li,那么此时因为div间接引用li,即使li被清空,也还是在内存中,并且只要li不被删除,他的父元素都不会被删除。
解决:手动删除清空。

前端如何做优化

答案:
了解了浏览器渲染界面的过程其实对于前端的优化就有思路了
1、css 放在文件头部,javascript 放在底部;
2、尽量减少 http 请求次数;
3、善用缓存不要重复加载相同的资源,比如用户登录之后的用户信息等;
4、图片优化,采用图片懒加载,在页面开始加载的时候,不请求真实图片地址,而是用默认图占位,当前页面加载完成后,在根据相关的条件依次加载真实图片;
5、降低css选择器的复杂性,尽量使用 id 和 class。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值