面试复盘(一)

134 阅读12分钟

1.margin坍塌问题怎么解决。

答:

  1. 为父盒子设置border:1px solid transparent。
  2. 为父盒子添加overflow:hidden。
  3. 为父盒子设定padding值。
  4. 为父盒子添加position:fixed/absolute;
  5. 为父盒子添加display:table/inline-block。
  6. 为父盒子添加float:left/right。

在标准文档流中,垂直方向的父子元素,当给子元素设置margin-top: 100px时,子元素不会相对父元素顶端距离100个像素,而是父子元素同时相对文档下移100px。或则同时给父子两个元素设置margin-top,但是呈现的效果是谁大,父子元素整体像下移动大的距离(此时子元素还是相对父元素不动)。这两种现象我们都称作margin塌陷。

  *{
      margin:0;
      padding:0;
  }
  .box1{
      width:400px;
      height:400px;
      background: pink;
  }
  .box2{
      width:200px;
      height:200px;
      background: lightblue;
      margin-top:10px;
  }

<div class="box1">
      <div class="box2"></div>
 </div>

什么的代码本来想让box2这个div距离box1这个元素margin-top为10px。但是呈现出来的确是父元素的margin-top为10px;子元素的margin-top为0px。

.box1{ margin-top:20px; }

按照我们现在代码的展现形式应该是box2节点距离文档顶部应该是30px。box1节点距离顶部20px。但是最终呈现出来的却是box1和box2节点都是距离顶部20px。

解决方法:

方法一:给父元素加上边框border:1px solid transparent;(改变了父元素的结构)

.box1{ border:1px solid transparent; }

但是加了这个之后,整个父元素可以存放的男人空间只有198个像素了。

方案二:给父元素加上绝对定位。position:absolute。

.box1{ position:absolute }

方案三:display:inline-block;让父级同时具有行级属性和块级属性。

.box1{ display:inline-block; }

方案四:float:left/rgight;让父级产生浮动流。

.box1{ float:left; }

方案五:overflow:hidden;溢出部分隐藏。

.box1{ overflow:hidden; }

参考:www.bilibili.com/read/cv5005…

2.简述事件委托。

事件委托又称事件代理,事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。通俗的讲,事件就是onclick,onmouseover,onmouseout,等就是事件,委托呢,就是让别人来做,这个事件本来就是加在某些元素上的,然而你却加到别人身上来做,完成这个事件。

事件委托的原理就是DOM元素的事件冒泡。利用事件冒泡的原理,把本应添加给某个元素上的事件委托给它的父级,触发执行效果。

事件委托的优点:

  • 可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件就非常棒。

  • 可以实现当新增子对象时无需再次对其绑定(动态绑定事件)

举个通俗的例子

比如一个宿舍的同学同时快递到了,一种方法就是他们一个个去领取,还有一种方法就是把这件事情委托给宿舍长,让一个人出去拿好所有快递,然后再根据收件人一 一分发给每个宿舍同学;

在这里,取快递就是一个事件,每个同学指的是需要响应事件的 DOM 元素,而出去统一领取快递的宿舍长就是代理的元素,所以真正绑定事件的是这个元素,按照收件人分发快递的过程就是在事件执行中,需要判断当前响应的事件应该匹配到被代理元素中的哪一个或者哪几个。

<ul id="ul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
</ul>

一般方法实现:

window.onload=function(){
   var oUl = document.getElementById("ul");
   var aLi = oUl.getElementsByTagName('li');
   for(var i=0;i<aLi.length;i++){
      aLi[i].onclick = function(){
         alert(123);
      };
      aLi[i].onmouseover = function(){
         this.style.background = "red";
      }
      aLi[i].onmouseout = function(){
         this.style.background = "";
      }
   }
}

首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li;但是如果说我们可能有很多个li用for循环的话就比较影响性能。

事件委托方式实现:

// 这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,
// 当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,
// 比如说只有点击li才会触发,不怕,我们有绝招:

// Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,
// 但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,
// 并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题):

 window.onload = function () {
    var oUl = document.getElementById("ul");
    oUl.onclick = function () {
       alert(123)
    }
    oUl.onclick = function (ev) {
       var ev = ev || window.event;
       var target = ev.target || ev.srcElement;
       if (target.nodeName.toLowerCase() == 'li') {
         alert(123);
         alert(target.innerHTML);
       }
     }
// 这里要用到事件源:event 对象,事件源,不管在哪个事件中,只要你操作的那个元素就是事件源。
// ie:window.event.srcElement
// 标准下:event.target
// nodeName:找到元素的标签名
    oUl.onmouseover = function (ev) {
      var ev = ev || window.event;
      var target = ev.target || ev.srcElement;
      console.log(target.innerHTML);
      if (target.nodeName.toLowerCase() == "li") {
        target.style.background = "red";
      }
    }
    oUl.onmouseout = function () {
      var ev = ev || window.event;
      var target = ev.target || ev.srcElement;
      console.log(target.innerHTML);
      if (target.nodeName.toLowerCase() == "li") {
        target.style.background = "";
      }
   }
}

这样改下就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!

上面的例子是说li操作的是同样的效果,要是每个li被点击的效果都不一样,那么用事件委托还有用吗?

<div id="box">
   <input type="button" id="add" value="添加" />
   <input type="button" id="remove" value="删除" />
   <input type="button" id="move" value="移动" />
   <input type="button" id="select" value="选择" />
</div>

一般实现的方式:

window.onload = function () {
    var Add=document.getElementById("add");
    var Remove=document.getElementById("remove");
    var Move=document.getElementById("move");
    var Select=document.getElementById("select");
    Add.onclick=function(){
       alert('添加')
    }
    Remove.onclick=function(){
       alert('删除')
    }
    Move.onclick=function(){
       alert('移动')
    }
    Select.onclick=function(){
       alert('选择')
    }
}

上面的实现效果,4个按钮,点击每一个做不同的操作,那么至少需要4次dom操作。

使用事件委托的实现方式:

window.onload = function () {
    var box=document.getElementById("box");
    box.onclick=function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if (target.nodeName.toLocaleLowerCase() == 'input') {
          switch(target.id){
             case 'add' :
                alert('添加');
                break;
             case 'remove' :
                alert('删除');
                break;
             case 'move' :
                alert('移动');
                break;
             case 'select' :
                alert('选择');
                break;         
          }
      }
   }
}

用事件委托就可以只用一次dom操作就能完成所有的效果,比上面的性能肯定是要好一些的。

总结: 适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress. 值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

参考:www.cnblogs.com/liugang-vip…

事件委托.png

<div id="J_container">
    <div class="record-head">
       <div class="head id">序号</div>
       <div class="head name">姓名</div>
       <div class="head sex">性别</div>
       <div class="head tel">电话号码</div>
       <div class="head province">省份</div>
       <div class="head">操作</div>
    </div>
    <ul id="J_List">
       <li>
          <div class="id">1</div>
          <div class="name">张三</div>
          <div class="sex"></div>
          <div class="tel">13788888888</div>
          <div class="province">浙江</div>
          <div class="user-delete">删除</div>
       </li>
       <li>
          <div class="id">2</div>
          <div class="name">李四</div>
          <div class="sex"></div>
          <div class="tel">13788887777</div>
          <div class="province">四川</div>
          <div class="user-delete">删除</div>
       </li>
       <li>
          <div class="id">3</div>
          <div class="name">王二</div>
          <div class="sex"></div>
          <div class="tel">13788889999</div>
          <div class="province">广东</div>
          <div class="user-delete">删除</div>
       </li>
    </ul>
</div>
   * {
     padding: 0;
     margin: 0;
   }
   
   .head, li div {
     display: inline-block;
     width: 70px;
     text-align: center;
   }

   li .id, li .sex, .id, .sex {
     width: 15px;
   }

   li .name, .name {
     width: 40px;
   }

   li .tel, .tel {
     width: 90px;
   }

   li .del, .del {
     width: 15px;
   }

   ul {
     list-style: none;
   }

   .user-delete {
     cursor: pointer;
   }

一般实现方式:这种方法造成的代价是,性能的大量浪费。如果是成千上万条数据,页面将会严重卡顿,甚至崩溃。

function Contact(){
    this.init();
}

Contact.prototype.init=function(){
    var userdel=document.querySelectorAll('.user-delete');
    for(var i=0;i<userdel.length;i++){
        (function(j){
            userdel[j].onclick=function(){
                userdel[j].onclick=function(){
                    userdel[j].parentNode.parentNode.removeChild(userdel[j].parentNode);
                }
            }
        })(i);
    }
}

new Contact();

使用事件委托实现:只绑定一次事件,大大减少了性能的损耗。也是在需要大量事件处理程序中一种非常好的解决方式。

function Contact(){
    this.init();
}

Contact.prototype.init=function(){
    var list=document.querySelector('#J_container');
    list.addEventListener("click",function(e){
        var target=e.target || e.srcElement;
        if(!!target && target.className.toLowerCase()==='user-delete'){
                    target.parentNode.parentNode.removeChild(target.parentNode)
         }
    })
}

new Contact();

参考:blog.csdn.net/bluebluesky…

3.简述下事件冒泡,并讲出如何解决事件冒泡。

事件冒泡(event bubbling):微软提出了名为事件冒泡的事件流。事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。可以想象把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。

<div id="dv1">
    <div id="dv2">
        <div id="dv3">click</div>
    </div>
</div>
var dv1=document.getElementById('dv1');
var dv2=document.getElementById('dv2');
var dv3=document.getElementById('dv3');

dv1.onclick=function(){
    console.log(this.id)
}

dv2.onclick=function(){
    console.log(this.id)
}

dv3.onclick=function(){
    console.log(this.id)
}
#dv1{
    width: 400px;
    height: 400px;
    background-color: red;
}

#dv2{
    width: 300px;
    height: 300px;
    background-color: green;
}

#dv3{
    width: 200px;
    height: 200px;
    background-color: blue;
}

结果如下:

事件冒泡.jpg

针对这个例子,三个元素间的触发顺序就应该是 dv3->dv2->dv1。

取消事件冒泡的方法:

  1. 标准的W3C 方式:e.stopPropagation();这里的stopPropagation是标准的事件对象的一个方法,调用即可
  2. 非标准的IE方式:ev.cancelBubble=true; 这里的cancelBubble是 IE事件对象的属性,设为true就可以了

通常我们会封装这样一个函数:

function stopBubble(e){
    //如果提供了事件对象,则这是一个非IE浏览器
    if(e && e.stopPropagation){
        //因此它支持W3C的stopPropagation()方法
        e.stopPropagation();
    } else{
        //否则,我们需要使用IE的方式来取消事件冒泡
        window.event.cancelBubble=true;
    }
}

然后在事件中调用即可。

dv2.onclick=function(e){
    console.log(this.id)
    stopBubble(e)
}

dv3.onclick=function(e){
    console.log(this.id)
    stopBubble(e)
}

参考:zhuanlan.zhihu.com/p/100831300

4.事件捕获。

事件捕获(event bubbling):网景提出另一种事件流名为事件捕获。事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定),与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。同样形象的比喻一下可以想象成警察逮捕屋子内的小偷,就要从外面一层层的进入到房子内。

<div id="dv1">
    <div id="dv2">
        <div id="dv3">click</div>
    </div>
</div>
#dv1{
    width: 400px;
    height: 400px;
    background-color: red;
}

#dv2{
    width: 300px;
    height: 300px;
    background-color: green;
}

#dv3{
    width: 200px;
    height: 200px;
    background-color: blue;
}
var dv1=document.getElementById('dv1');
var dv2=document.getElementById('dv2');
var dv3=document.getElementById('dv3');

dv1.addEventListener('click',f1,true);
dv2.addEventListener('click',f1,true);
dv3.addEventListener('click',f1,true);

function f1(){
    console.log(this.id)
}

element.addEventListener(event, function, useCapture)

addEventListener方法用来为一个特定的元素绑定一个事件处理函数,是JavaScript中的常用方法,其传入三个参数,分别是‘没有on的事件类型’,‘事件处理函数’,‘控制事件阶段’,第三个参数是boolean类型,默认是false,表示在事件冒泡的阶段调用事件处理函数,像上图中传入true,就表示在事件捕获的阶段调用事件处理函数。

事件捕获.jpg

针对这个例子,三个元素间的触发顺序应该是dv1->dv2->dv3。

参考:zhuanlan.zhihu.com/p/100831300

一个事件触发后,会在子元素和父元素之间传播(propagation)。这种传播分成三个阶段

事件冒泡与捕获.jpg

如上图所示,事件传播分成三个阶段:

捕获阶段:从window对象传导到目标节点(上层传到底层)称为“捕获阶段”(capture phase),捕获阶段不会响应任何事件; 目标阶段:在目标节点上触发,称为“目标阶段” 冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”(bubbling phase)。事件代理即是利用事件冒泡的机制把里层所需要响应的事件绑定到外层。

参考:blog.csdn.net/qq_38128179…

阻止事件捕获:

  1. stopPropagation()方法既可以阻止事件冒泡,也可以阻止事件捕获,也可以阻止处于目标阶段。
  2. 我们可以使用DOM3级新增事件stopImmediatePropagation()方法来阻止事件捕获
document.getElementById("second").addEventListener("click",function(){
    alert("second");
    event.stopImmediatePropagation();
},true);

那么 stopImmediatePropagation() 和 stopPropagation()的区别在哪儿呢?

后者只会阻止冒泡或者是捕获。 但是前者除此之外还会阻止该元素的其他事件发生,但是后者就不会阻止其他事件的发生。 例子点击这里。

 DOM2级事件规定的时间流包括 三个阶段:

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段   注意:在DOM事件流中,实际的目标在捕获阶段不会接收到事件,下一个阶段是处于目标阶段,这时事件被触发,最后进入事件冒泡阶段。我们认为处于目标阶段是事件冒泡阶段的一部分。

5.简述事件循环。

zhuanlan.zhihu.com/p/87684858 blog.csdn.net/wu_xianqian… www.ruanyifeng.com/blog/2014/1…

6.请简述从输入url到页面渲染的完成过程。

→ 1- 输入网址 → 2- 缓存解析 → 3- 域名解析 → 4- tcp连接,三次握手 → 6- 页面渲染

1、首先,在浏览器地址栏中输入url

2、浏览器先查看浏览器缓存-系统缓存-路由器缓存,如果缓存中有,会直接在屏幕中显示页面内容。若没有,则跳到第三步操作。

3、在发送http请求前,需要域名解析(DNS解析),解析获取相应的IP地址。

4、浏览器向服务器发起tcp连接,与浏览器建立tcp三次握手。

5、握手成功后,浏览器向服务器发送http请求,请求数据包。

6、服务器处理收到的请求,将数据返回至浏览器

7、浏览器收到HTTP响应

8、读取页面内容,浏览器渲染,解析html源码

9、生成Dom树、解析css样式、js交互

10、客户端和服务器交互

11、ajax查询

7.1px解决方案。

DPR = 设备像素 / css像素(某一方向上) 这句话看起来很难理解,可以结合下面这张图(1px在各个DPR上面的展示),一般我们h5拿到的设计稿都是750px的,但是如果在DPR为2的屏幕上,手机的最小像素却是要用2 * 2px来进行绘制,这也就导致了为什么1px会比较粗了。

1px问题产生的原因: 从移动端的角度说个具体的场景,以iphone6为例。

iphone6的屏幕宽度为375px,设计师做的视觉稿一般是750px,也就是2x,这个时候设计师在视觉稿上画了1px的边框,于是你就写了“border-width:1px”,so...1px边框问题产生了。

对设计师来说它的1px是相对于750px的(物理像素),对你来说你的1px是相对于375px的(css像素)“实际上你应该是border-width:0.5px”。

  1. 0.5px实现

.hairlines div { border-width: 0.5px;}

  1. 使用border-image实现
.border{    
    border-width: 1px;    
    border-image: url(border.gif) 2 repeat;
}
  1. 用多背景渐变实现

  2. 使用box-shadow模拟边框实现

  3. 通过 viewport + rem 实现

  4. 使用伪类 + transform实现

参考:blog.csdn.net/snsHL9db69c…

8.谈谈对权重的理解。

  1. !important,加在样式属性值后,权重值为10000
  2. 内联样式,如:style="",权重值为1000
  3. ID选择器,如:# content权重为100
  4. 类,伪类,属性选择器如:content、:hover权值为10
  5. 标签选择器和伪元素选择器,如:div、p、:before权值为1
  6. 通用选择器(*)、子类选择器(>)、相邻选择器(+)、同胞选择器(~)权值为0

权重规则总结:

  • !important 优先级最高,但也会被权重高的important所覆盖
  • 行内样式总会覆盖外部样式表的任何样式(除了!important)
  • 单独使用一个选择器的时候,不能跨等级使css规则生效
  • 如果两个权重不同的选择器作用在同一元素上,权重值高的css规则生效
  • 如果两个相同权重的选择器作用在同一元素上:以后面出现的选择器为最后规则.
  • 权重相同时,与元素距离近的选择器生效

参考:segmentfault.com/a/119000001…

9.怎么清除浮动?

  1. 浮动元素结尾增加空标签,设置clear:both。
  2. 父级定义overflow:hidden。
  3. 对父级设置适合高度。
  4. 父元素添加伪类:after和zoom。

参考:zhuanlan.zhihu.com/p/94697222

10.父组件怎么监听子组件的生命周期?

  1. 在子组件生命周期中使用$emit('callback')触发父组件中的方法.
  2. 使用hook钩子函数。

11.const a={b:1};a.b=2;这个程序会不会报错?

答:不会,const声明的限制只适用于它指向的变量的引用。换句话说,如果const变量引用的是一个对象,那么修改这个对象内部的属性并不违反const的限制。

12.你对项目有哪些封装?

基本HTTP请求封装

js常用封装参考:blog.csdn.net/qq_29470333…

13.你一般怎么适配?

  1. rem
  2. vw和vh
  3. 媒体查询
  4. 百分比布局

参考:www.cnblogs.com/yxkNotes/p/…