前端面试题笔记

题目参考来源:

1.http://blog.csdn.net/PRIMEFJT/article/details/78619080

2.个人笔试/面试

初步看了下阿利面试题的博客,觉得自己确实是非常的“新”,之前准备社招的时候觉得自己基础打得还算扎实,现在发现有些概念模糊了,有些文中提到的根本没了解过,所以决定温故知新,因为要准备三月的校招社招,所以除了以上博文内提到的题目,遇到别的好的题目也会跟大家一起深入去了解一下有关XXX的前世今生,希望大家有什么好的问题可以在下面留言,尽量保持这篇文章的更新,下面开始刨根问底。

1.CSS盒模型

标准盒模型:占用的宽度 = margin + border + padding + width

怪异盒模型:box-sizing:border-box 占用的宽度 = margin + width  其中 width = border + padding +content 实际使用中,个人觉得怪异盒模型的使用度更高,因为在UI的眼里,只有margin和width。

2.未知宽高元素怎么上下左右垂直居中

左右居中其实很简单:margin:auto;width:?? 注意width要设置,否则无法做到居中显示

上下居中比较麻烦,而且应用场景比较特殊,什么时候会上下居中呢?比如右侧悬浮的导航栏,或者说一些弹窗什么的,因此用绝对定位来解决这个居中问题看起来也没什么不妥

看一下上下左右居中的解决方案:position:absolute;top:50%;height:50%;tranform:translate(-50%,-50%)

3.float和absolute的区别

乍一看float和absolute的区别,简直就是天差地别,但我仅从脱离文档流的概念讲,首先两者都是脱离文档流进行定位的一种方式。但好像这么说,他们之间还差了点——“破坏性”。个人认为破坏性这个词很好的区分了两者,首先absolute元素会完全破坏dom结构,就好像其他元素(不管是否绝对定位还是如何定位)不存在一样,有种“悬浮”在父级里的感觉。而float则是“真实”存在的,比如相邻的float元素能够互相感知彼此的存在的,而相邻的绝对定位元素只看上下左右是多少。当然要让父级真实的感知float的存在,不要忘记给父级清浮动。

4.absolute和relative的区别

乍一看absolute和relative还是天差地别,但我仅从他们是以什么元素定位进行分析,首先绝对定位,是基于最近的具有定位的父级元素进行定位,注意前面划重点部分不能漏,如果没有具有定位特性的父级,则知道找到body为止,很多人以为是父级,切记!,absolute请配合relative使用。relative则是相对于相邻的同级元素进行定位。

5.行内元素和行间元素

说实话这个问题,先说个人看法,首先h5新增了很多语义化标签,如常用的header、section、footer、nav等,当然现在也支持你自己自定义标签名字,如<test></test>,你可以给这个自定义标签附上你喜欢的属性,他在生成的时候是一个空样式的inline标签。实际做项目的过程中,个人在布局的时候喜欢用浮动之类的布局方式,浮动会自动给标签添加inline-block特性。

关于两者的区别,首先块级元素自占一行,宽高,padding,margin都可以,内部可以放块级/行内元素

行内元素则是占同一行,注意换行被解析2px可能会影响布局,不支持宽高设置,margin不支持上下,padding支持,内部可以放行内元素。试了放块级元素,布局完全看不懂,不要轻易尝试了。

常用的行内元素:input,a,img,label,span

常用的块级元素:div,h,p,ul,table

6.JSON.parse()和JSON.stringify()

关于这两个函数,之前只知道通过JSON.stringify() 将obj --> string,再通过JSON.parse()将string --> obj,总结来说就是json对象和string间的相互转化。

关于使用场景:如下面第七题中,cookie和localstrage的存储格式一般都是键值对,因此需要将json数据转化成string类型存入,然后取出的时候再转化成obj类型方便使用。

关于细节:事实上这两个函数还有可选项,以及为什么要有可选项。

先来定义一个json

var obj = {
		name:"dkr",
		sex:"nan",
		age:undefined,
		fn:function(){
			console.log("fn");
		}
	}
	var str = JSON.stringify(obj);
	console.log(typeof(str)) 
	console.log(str);
//string
//{"name":"dkr","sex":"nan"}

可以发现,undefined和function都被忽略了,那么如何解决这个问题呢,其实stringify方法有第二个参数replace,你可以写函数,也可以写数组,如果是函数的话,则通过该函数重新计算输出,如果是数组,则输出数组内想输出的键值对,具体参考https://www.cnblogs.com/xiao2013/p/7278766.html,里面说的非常详细,这里不多介绍了。

7.简述一下cookie、session、localstorage和sessionStorage的区别

事实上要搞清这个问题还得有一定的后端知识,我先来说前端可以使用的cookie、localstrage和sessionStorage

首先cookie、localstorage和sessionStorage的数据都存储在本地浏览器中,其中sessionStorage的概念比较特别,需要引入一个"浏览器窗口"的概念,sessionStorage是在同源的窗口中始终存在的数据,当窗口未关闭,刷新页面或进入另一个同源的页面,数据仍然存在,关闭窗口后sessionStorage会自动销毁。如果用不同窗口打开同一网站,那么生成的sessionStorage对象也不同,个人对sessionStorage和sesstion应用的比较少。注意不要混淆一个概念,sessionStorage并不是指存在服务器中的一块特殊地址,他是存放在本地浏览器的。

个人常用的本地存储一般是localstorage和cookie,这两者的区别是localstorage的存储量更大,cookie的存储是受限的,而且cookie在每次发送请求的时候都会放在request header里发送到服务端,无形中浪费了带宽,另外cookie还需指定作用域,不可跨域使用。但是cookie是好处也很重要,就是cookie可以承担与服务器之间的数据通讯,作为http规范的一部分而存在,而localstorage中的内容是服务器访问不到的。关于关闭浏览器是否自动清空cookie,答案是,不一定。在设置cookie的时候可以设置cookie的保存时间,如果不设置,则默认为浏览器关闭cookie失效。而localstorage的数据则可以永久保存在本地,这可以用于页面间通讯,当然,这也会造成本地存储的无效数据较多,占用内存较多。(好在各种杀毒软件每天提示你各种清理)

关于session,不想说的太详细,只需要知道数据存在服务器,在大量用户访问的时候会给服务器造成很大压力。session和cookie的区别是,客户端无法访问session存储的内容。参考博文http://blog.csdn.net/ruby_xc/article/details/65939988

8.跨域问题的产生和解决方案

首先,什么是跨域问题?举个例子,https://www.baidu.com/ 要访问 https://www.dkr.com/的某一个文件就会产生跨域问题。详细的说,协议http/https,域名www.baidu.com/www.dkr.com,端口:80/70不同都会导致跨域问题。但是这里我有个小不懂的地方,既然域名在通讯的时候被解析为DNS,为什么www.baidu.com不能访问对应的DNS比如192.158.11.0呢?待解决ing....

其次,为什么要有跨域?举个例子,某用户打开支付宝访问了付钱接口,且客户端啊和服务端通过cookie携带信息进行验证,验证成功后服务端告诉你付款成功,这时候,用户又点开了恶意程序,跟支付宝界面一模一样,然后用户又点了类似付款的操作,这时候浏览器的cookie信息会在request header里被携带发送给恶意程序的服务器,这时候如果恶意程序想要访问支付宝的接口,即使信息正确,但也会因为跨域问题被拦截。

介绍了跨域问题的产生和原理,再来介绍解决方案。

关于解决方案,这里只讲一种,网上有很多解决方案,在面试的过程中写一种基本能填满了,而且实际项目中也基本不会涉及到跨域问题,你自己的产品,域名一般都是一样的(最多有几个子域名,可以上网查有关这个跨域问题的解决方案)。下面来说最常用的解决方案JSONP。

首先我们知道script标签是可以访问网络文件的,比如你在引入jquery的包的时候既可以下载到本地引入,也可以引入官方提供的http...地址。因此可以利用这个特性,从a网站访问b网站的某一个接口,b网站会把数据存放到指定名字的回调函数里传回来。在a网站就可以利用这个函数进行数据接收。

<script type="text/javascript">  
    function jsonpCallback(result) {  
        //alert(result);  
        for(var i in result) {  
            alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
        }  
    }  
    var JSONP=document.createElement("script");  
    JSONP.type="text/javascript";  
    JSONP.src="http://crossdomain.com/services.php?callback=jsonpCallback";  
    document.getElementsByTagName("head")[0].appendChild(JSONP);  
</script> 

jquery提供的简易版本

//Jq 客户端 方案二
<script type="text/javascript" src="jquery.js"></script>  
<script type="text/javascript">  
    $.ajax({  
        url:"http://crossdomain.com/services.php",  
        dataType:'jsonp',  
        data:'',  
        jsonp:'callback',  
        success:function(result) {  
            for(var i in result) {  
                alert(i+":"+result[i]);//循环输出a:1,b:2,etc.  
            }  
        },  
        timeout:3000  
    });  
</script>   

这种方式的坏处是显而易见的,首先他只支持get请求,对于现在主流网站的大部分post接口是无法使用的,其次,如果b网站的安全工作做得好,是无法通过这种方式获取数据的,也就是b网站可以通过一定方法拒绝这种攻击方式。

9.页面间通信

首先要了解一个概念,页面间通讯和窗口间通讯的关系。

页面间通讯:指的是window.open()一个新的页面,A页面和B页面是并列关系

窗口间通信:指的是A页面用iframe,包含一个B页面,A页面和B页面是嵌套关系。

关于窗口间通讯,可以参考网上的postMessage()方法。

这里简单介绍一下做项目中会遇到的打开关联窗口传值的问题。也就是所谓的页面间通信。

1.用localstorage或者cookie保存数据在浏览器上,A页面存储,B页面取出,达到A页面向B页面传值的效果。安全指数:3颗星,对于程序员来说,这太好破解了,只能欺骗用户。

2.用url附带参数的做法打开B页面,如A页面打开B页面的时候跟上?name=dkr.....,B页面从window.location里取值,达到传值效果。安全指数:0颗星,用户都能看到自己信息被暴露了。

3.和服务端协商,通过服务端改变数据,A页面不停监听接口的做法。安全指数:5颗星,操作在服务器上进行,但是也有个坏处,A页面需要不停地发送http请求去了解某个值是否被改变,增大了服务器的压力。

10.作用域和作用域链(预解析机制理解)

关于作用域和作用域链,经常会考查的是另外一个问题,闭包,关于闭包的概念后一题会详解。

先来看作用域是什么?首先作用域是指函数或者变量被声明后可被使用的区域。在讲作用域之前,来看一下浏览器对于JS代码是怎么解析的,这有助于作用域的理解。

alert(a)// function a(){alert(2)}
var a = 1;
alert(a); // 1
function a (){alert(2)};
a(); //报错

关于上面代码为什么会产生这样的结果呢?

第一步:"预解析"(这个名字非官方)

当浏览器遇到script标签,并且里面存放的是JS代码的时候,浏览器会由上至下进行一次预解析,解析内容为被var 和 function声明的内容。

首先解析到var a,这时候全局环境里就会存放一个a = undefined,注意预解析的过程中不会执行表达式(如 = + - * / 参数等,只要能改变值得都可以当做表达式),而是默认var声明的为undefined,然后一步步解析下来,遇到一个名为a的function函数,这时候在全局变量中已经存在一个值为undefined的a,浏览器会选择函数来覆盖a的值,如果后面还有其他函数声明,则由下往上进行覆盖,当然,如果还有var声明,是无法覆盖function的。

第一步得到的结果是:a最终被解析成function函数,存在全局变量中。

第二步:由上往下读取代码

在读到a = 1时,由于这是一个赋值的操作,因此typeof(a)由function被强转成string类型,所以第二个alert输出了1,而这个时候a已经 = 1了,因此执行1()这个操作的时候,浏览器自然会报错。

上面的讲解并没有提到作用域,因为预解析的过程还漏了一个点,就是JS会以function为域(!!!注意不是以双大括号会域,如if和for的双大括号),生成全局作用域下的子作用域。function内部的解析过程和上面两步相同。举个例子看一下

alert(a) //function a(){....}
var a = 1; 
function a(){
  alert(a); //undefined
  var a = 2
}
alert(a) // 1

上面的例子可以看到,a()函数内部定义了局部变量a,因此在函数内输出a的时候,先从函数内部开始找,找到后发现其还未被赋值,因此输出undefined。如果将函数内部的var去掉,那么函数内部打印a的时候发现内部没有a变量,那就往上一层找是否包含a变量,最终形成了一条由外而内的作用域链。

11.闭包的概念和使用场景

关于闭包的概念说起来很简单:函数嵌套函数。

那么函数嵌套函数的方式有什么用呢?先看一个demo吧

 
 
var fn = (function(){
        var a = 1;
        function fn2(){
            a++;
            alert(a);
        }
        return fn2;
    })();
    fn(); //2
    fn(); //3
之前讲作用域的时候讲到了两个步骤,那么我先根据这两个步骤走一下看,看一下闭包是怎么回事。
先从外往内:
第一步,最外层,全局作用域,检测到一个fn,注意虽然最终自执行函数返回了一个函数给fn,但事实上,他在预解析的时候是用var声明的,所以一开始,fn = "undefined”,可以自己输出来试试。

第二步,进入匿名函数,匿名函数也有一个自己的作用域,并在匿名函数中生成了一个a变量。

第三步:进入匿名函数内部的fn2函数,又生成了一个作用域,该作用域内没有声明任何东西。

再由内而外:(从内而外找比较绕,我特地分了段落,希望大家能跟住这个绕呀绕的思路)

最内层应该是fn2函数,执行的时候需要寻找一个变量a,然后打印一下,结果在本层作用域内没有找到a

于是往上一层找,也就是匿名函数,在上一层找到了a,并且已经赋值了1,于是fn2执行a++的时候改变了匿名函数中a变量的值,成功使他变成了2

而fn2作为返回值赋值给了全局变量fn,这样就导致,全局变量,可以找到匿名函数内的一个值,并且将这个值看似保存在了全局变量中。这导致很多人觉得闭包,打破了作用域和作用域链的规则,那我建议你重新看一下由内而外寻找的这个过程,确实有点绕,我很清楚他依旧符作用域的规则,但欠缺一些表达,实在看不懂的话,点下面这个链接 

http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html ,更加详细一些。

如果看懂了闭包是怎么回事,那请细细体会他的好处&&坏处:

好处:闭包避免了污染全局作用域 

好处&&坏处 :闭包可以是一个变量长期驻扎在内存中(一般函数的私有变量在函数执行结束后都会被内存释放掉,而闭包由于将私有成员赋值给了全局变量,导致他内部的值得以保存)

为什么说他是坏处呢?因为过多变量的值存储在内存中会导致浏览器占用的内存过多,导致性能下降等问题。

下面来看一个例子,来看一下闭包的使用

<body>
<button>1</button>
<button>2</button>
<button>3</button>
</body>

<script type="text/javascript">
   var bts = document.getElementsByTagName('button');
   for(var i = 0;i<bts.length;i++){
       (function(i){
           bts[i].onclick = function(){
               alert(i);
           }
       })(i)
   }
</script>

去掉闭包的执行结果,三个button都输出3,因为处罚click事件的时候,执行匿名函数function(){alert(i)},这时候这个函数内部没有声明i,因此要往外找,最终找到,i++不符合<lenght的结果跳出for循环,因此三个按钮都输出3.

再来看看,为什么闭包之后函数能执行出正确结果分别输出0,1,2。

补充一个概念,function a(n)  === function a(var n),因此函数的参数实际上声明了一个变量在自己的作用域内,这样不管是bts[i]还是alert(i),他们都会去找最近的作用域的i的值,而作为参数传进来的i的值是正确的,他不会随着全局变量的改变而改变。因此这样就能得到正确结果。

说实话,闭包的概念比较难懂,建议大家多上网看看demo,我自认为讲的不够清楚....

12.事件冒泡的各种坏处

什么是事件冒泡?举个例子,有助于理解

<BODY onclick="alert('aaa');">
<div onclick="alert('bbb');">
 <a href="#" class="cooltip" title="这是我的超链接提示1。" onclick="alert('ddd');">
   提示
  </a>
</div>
</BODY>

当我点击a标签,这个时候不但会输出ddd,还会依次输出bbb,和aaa。因为body包含了div,div包含了a标签,a标签的点击事件就一路像上级大佬通报,遇到也有click事件的就触发该元素的点击事件,没有的就略过,导致你明明想触发一个事件的,一不小心触发了不知道谁写的别的事件。所以这是冒泡的第一个坏处。

冒泡的第二个坏处是,原生JS的写法也太难记了!顺便来看一下如果阻止元素的默认事件

阻止事件冒泡: event.stopPropagation();

阻止默认事件:event.preventDefault();

关于事件冒泡,他还有个反面的兄弟叫事件捕获,有兴趣的童鞋百度一下,了解一下,感觉实际项目很少用。

            



 
 




猜你喜欢

转载自blog.csdn.net/dkr380205984/article/details/79218827
今日推荐