前端面试题(整理)

JavaScript:

  1. 原型继承(手写 class B 继承 class A)
  2. call、apply、bind 区别
  3. 解释什么是闭包及其应用
  4. 变量作用域 + 变量提升
  5. 字符串整型数字转数字有多少种实现方法(隐式转换)
  6. 异步编程(回调、promise、async await)(js 单线程 event loop)(手写原生实现简易 promise)
  7. js 事件模型(手写原生实现简易事件监听)
  8. localStorage、cookie(与缓存相关的 cookie 有哪些)
  9. csrf、xss、https(网络安全)
  10. 跨域请求如何实现

ES6:

  1. let、const(for 循环示例)
  2. set、map
  3. 箭头函数 this 的特点
  4. 常用语法有哪些

布局:

  1. 水平垂直居中(定尺寸、不定尺寸)
  2. flex(等高布局、自适应布局、垂直居中)
  3. 自适应正方形(padding、vw)
  4. 移动端屏幕适配(如何实现 rem)(如何实现 1px border)
  5. 清浮动有哪些方法(涉及 BFC)

Vue:

  1. vue 响应式原理
  2. 计算属性、watch 差别
  3. vue 生命周期
  4. 非继承关系组件通讯(vuex、发布订阅)

编程:

  1. 数组去重(数组项类型多样)
  2. 求无序数值数组序列最大值
  3. 常用数组操作有哪些(哪些破坏原结构)
  4. 排序算法(涉及复杂度)
  5. 位运算(两数之和)(成对数值数组寻单)
  6. 斐波那契数列
  7. 阶乘
  8. 反转链表
  9. 遍历 dom 树(广度优先、深度优先)

其它:

  1. html5 新特性有哪些(语义化标签、音视频、缓存…)
  2. hybrid 与 native 通讯方式有哪些(混合开发有什么优势)(如何实现热更新)
  3. 遇到过哪些 ios、android 兼容问题
  4. sass、less、postcss(作用、优点)
  5. 对 webpack 的理解及使用
  6. 开启一 html 经历哪些
  7. 性能优化(图片处理:webp、base64、合并、压缩、延时加载…)(预渲染)(静态资源体积减小)(资源异步加载)(资源缓存)
  8. git 常用命令(git revert、git reset 区别)
  9. 微信授权登录流程(openid、unionid 区别)
  10. AMD、CMD(require()、import、import() 应用场景)

实践:

  1. 富文本混入多种类型卡片的实现
  2. 纯 css 绘制正六边形(一个标签实现)(svg、裁切亦可)
  3. 纯 css 绘制进度圆环(应用于任务中心)(svg、裁切亦可)
  4. 如何实现拖拽

Ajax、Comet与Websocket

http协议说起

1996年IETF  HTTP工作组发布了HTTP协议的1.0版本 ,到现在普遍使用的版本1.1,HTTP协议经历了17 年的发展。这种分布式、无状态、基于TCP的请求/响应式、在互联网盛行的今天得到广泛应用的协议,相对于互联网的迅猛发展,它似乎进步地很慢。互联网从兴起到现在,经历了门户网站盛行的web1.0时代,而后随着ajax技术的出现,发展为web应用盛行的web2.0时代,如今又朝着web3.0的方向迈进。反观http协议,从版本1.0发展到1.1,除了默认长连接之外就是缓存处理、带宽优化和安全性等方面的不痛不痒的改进。它一直保留着无状态、请求/响应模式,似乎从来没意识到这应该有所改变。

 

Ajax—脚本发送的http请求

传统的web应用要想与服务器交互,必须提交一个表单(form),服务器接收并处理传来的表单,然后返回全新的页面,因为前后两个页面的数据大部分都是相同的,这个过程传输了很多冗余的数据、浪费了带宽。于是Ajax技术便应运而生。

Ajax是Asynchronous JavaScript and 的简称,由Jesse James Garrett 首先提出。这种技术开创性地允许浏览器脚本(JS)发送http请求。Outlook Web Access小组于98年使用,并很快成为IE4.0的一部分,但是这个技术一直很小众,直到2005年初,google在他的goole groups、gmail等交互式应用中广泛使用此种技术,才使得Ajax迅速被大家所接受。

Ajax的出现使客户端与服务器端传输数据少了很多,也快了很多,也满足了以丰富用户体验为特点的web2.0时代 初期发展的需要,但是慢慢地也暴露了他的弊端。比如无法满足即时通信等富交互式应用的实时更新数据的要求。这种浏览器端的小技术毕竟还是基于http协议,http协议要求的请求/响应的模式也是无法改变的,除非http协议本身有所改变。

 

Comet—一种hack技术

以即时通信为代表的web应用程序对数据的Low Latency要求,传统的基于轮询的方式已经无法满足,而且也会带来不好的用户体验。于是一种基于http长连接的“服务器推”技术便被hack出来。这种技术被命名为Comet,这个术语由Dojo Toolkit 的项目主管Alex Russell在博文Comet: Low Latency Data for the Browser首次提出,并沿用下来。

其实,服务器推很早就存在了,在经典的client/server模型中有广泛使用,只是浏览器太懒了,并没有对这种技术提供很好的支持。但是Ajax的出现使这种技术在浏览器上实现成为可能, google的gmail和gtalk的整合首先使用了这种技术。随着一些关键问题的解决(比如 IE 的加载显示问题),很快这种技术得到了认可,目前已经有很多成熟的开源Comet框架。

以下是典型的Ajax和Comet数据传输方式的对比,区别简单明了。典型的Ajax通信方式也是http协议的经典使用方式,要想取得数据,必须首先发送请求。在Low Latency要求比较高的web应用中,只能增加服务器请求的频率。Comet则不同,客户端与服务器端保持一个长连接,只有客户端需要的数据更新时,服务器才主动将数据推送给客户端。

Comet的实现主要有两种方式:

  1. 基于Ajax的长轮询(long-polling)方式

  浏览器发出

  2.  基于 Iframe 及 htmlfile 的流(http streaming)方式

  Iframe是html标记,这个标记的src属性会保持对指定服务器的长连接请求,服务器端则可以不停地返回数据,相对于第一种方式,这种方式跟传统的服务器推则更接近。

在第一种方式中,浏览器在收到数据后会直接调用JS回调函数,但是这种方式该如何响应数据呢?可以通过在返回数据中嵌入JS脚本的方式,如“<script type=”text/javascript”>js_func(“data from server ”)</script>”,服务器端将返回的数据作为回调函数的参数,浏览器在收到数据后就会执行这段JS脚本。

但是这种方式有一个明显的不足之处:IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。Google 的天才们使用一个称为“htmlfile”的 ActiveX 解决了在 IE 中的加载显示问题,并将这种方法应用到了 gmail+gtalk 产品中。

Websocket—未来的解决方案

         如果说Ajax的出现是互联网发展的必然,那么Comet技术的出现则更多透露出一种无奈,仅仅作为一种hack技术,因为没有更好的解决方案。Comet解决的问题应该由谁来解决才是合理的呢?浏览器,html标准,还是http标准?主角应该是谁呢?本质上讲,这涉及到数据传输方式,http协议应首当其冲,是时候改变一下这个懒惰的协议的请求/响应模式了。

W3C给出了答案,在新一代html标准html5中提供了一种浏览器和服务器间进行全双工通讯的网络技术Websocket。从Websocket草案得知,Websocket是一个全新的、独立的协议,基于TCP协议,与http协议兼容、却不会融入http协议,仅仅作为html5的一部分。于是乎脚本又被赋予了另一种能力:发起websocket请求。这种方式我们应该很熟悉,因为Ajax就是这么做的,所不同的是,Ajax发起的是http请求而已。 

与http协议不同的请求/响应模式不同,Websocket在建立连接之前有一个Handshake(Opening Handshake)过程,在关闭连接前也有一个Handshake(Closing Handshake)过程,建立连接之后,双方即可双向通信。

Opening Handshake

    客户端发起连接Handshake请求

   GET /chat HTTP/1.1

        Host: server.example.com

        Upgrade: websocket

        Connection: Upgrade

        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

        Origin: http://example.com

        Sec-WebSocket-Protocol: chat, superchat

        Sec-WebSocket-Version: 13

         服务器端响应:

        HTTP/1.1 101 Switching Protocols

        Upgrade: websocket

        Connection: Upgrade

        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

        Sec-WebSocket-Protocol: chat

“Upgrade:WebSocket”表示这是一个特殊的 HTTP 请求,请求的目的就是要将客户端和服务器端的通讯协议从 HTTP 协议升级到 WebSocket 协议。

“Sec-WebSocket-Key”是一段浏览器base64加密的密钥

“Sec-WebSocket-Accept”服务器端在接收到的Sec-WebSocket-Key密钥后追加一段神奇字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,并将结果进行sha-1哈希,然后再进行base64加密返回给客户端。

“Sec-WebSocket-Protocol”表示客户端请求提供的可供选择的子协议,及服务器端选中的支持的子协议,“Origin”服务器端用于区分未授权的websocket浏览器

“HTTP/1.1 101 Switching Protocols”中101为服务器返回的状态码,所有非101的状态码都表示handshake并未完成。

 

Data Framing

Websocket协议通过序列化的数据包传输数据。数据封包协议中定义了opcode、payload length、Payload data等字段。具体封包格式如下图所示:

FIN: 标识是否为此消息的最后一个数据包,占 1 bit

RSV1, RSV2, RSV3:  用于扩展协议,一般为0,各占1bit

Opcode:数据包类型(frame type),占4bits

         0x0:标识一个中间数据包

         0x1:标识一个text类型数据包

         0x2:标识一个binary类型数据包

         0x3-7:保留

         0x8:标识一个断开连接类型数据包

         0x9:标识一个ping类型数据包

         0xA:表示一个pong类型数据包

         0xB-F:保留

Payload length:Payload data的长度,占7bits,如果这个值等于126,则此后16bits用于表示Payload length,如果这个值等于127,则此后64bits用于表示Payload length

Payload data:应用层数据

Closing Handshake

相对于Opening Handshake,Closing Handshake则简单得多,主动关闭的一方向另一方发送一个关闭类型的数据包,对方收到此数据包之后,再回复一个相同类型的数据包,关闭完成。

关闭类型数据包遵守封包协议,Opcode为0x8,Payload data可以用于携带关闭原因或消息。 

虽然现阶段websocket协议还处于草案阶段,不过浏览器早就开始开始支持了,以下是不同浏览器兼容列表:

 

Feature

Chrome

Firefox (Gecko)

Internet Explorer

Opera

Safari

Version -76 support Obsolete

6

4.0 (2.0)

Not supported

11.00 (disabled)

5.0.1

Protocol version 7 support Obsolete

Not supported

6.0 (6.0)
Moz

Not supported

Not supported

Not supported

Protocol version 10 support Obsolete

14

7.0 (7.0)
Moz

HTML5 Labs

?

?

Standard – RFC 6455 Support

16

11.0 (11.0)

10

12.10

6.0

从浏览器支持角度来看,websocket还有很长的路要走,特别是在中国这个IE6依然盛行的国家,即便在websocket标准化之后,旧版本浏览器的消亡也需要很长一段时间,因此现阶段comet依然是最好的解决方案。

转载:http://www.shaoqun.com/a/54588.html

函数防抖与函数节流

应用场景

我们经常需要监听滚动条滚动或者鼠标的移动,但浏览器触发这类事件的频率非常高,可能在10几毫秒就触发一次,如果我们处理事件的函数需要操作大范围的DOM,这对于浏览器的性能是个考验,可能像chrome浏览器这样优秀的浏览器会好一点,但放到老版本的IE下,就可能发生卡顿现象。有的时候,我们只需要处理函数执行一次,比如文本输入验证,执行多次处理函数反而没有必要。

所以我们得想个办法,减少DOM操作的频度,也就是说稀释处理函数的执行频率,解决方法就是函数防抖和函数分流。函数防抖表示只执行一次处理函数,函数分流指降低处理函数的执行频率,下面是具体解释。

概念

函数防抖(debounce)

当调用动作过n毫秒后,才会执行该动作,若在这n毫秒内又调用此动作则将重新计算执行时间

函数节流(throttle)

预先设定一个执行周期,当调用动作的时刻大于等于执行周期则执行该动作,然后进入下一个新周期

函数节流(throttle)与 函数防抖(debounce)都是为了限制函数的执行频次,以优化函数触发频率过高导致的响应速度跟不上触发频率,出现延迟,假死或卡顿的现象。

比如如下的情况:

  • window对象的resize、scroll事件
  • 拖拽时的mousemove事件
  • 文字输入、自动完成的keyup事件

区别

可以拿我们平时坐电梯为例来形象地表述二者的区别

函数防抖:如果有人进电梯(触发事件),那电梯将在10秒钟后出发(执行事件监听器),这时如果又有人进电梯了(在10秒内再次触发该事件),我们又得等10秒再出发(重新计时)。

函数节流 :保证如果电梯第一个人进来后,10秒后准时运送一次,这个时间从第一个人上电梯开始计时,不等待,如果没有人,则不运行

实现

函数防抖(debounce)

function _debounce(fn,wait){
    var timer = null;
    return function(){
        clearTimeout(timer)
        timer = setTimeout(()=>{
            fn()
        },wait)
    }
}

function _log(){
    console.log(1)
}
window.onscroll = _debounce(_log,500)

但是,仔细想想,上面的实现方式还是有一定的缺点。如果页面很长,我们一直在滚动页面,那_log方法就一直不会被执行。所以我们可以升级一下上述的防抖方法。

function _debounce(fn,wait,time){
    var previous = null; //记录上一次运行的时间
    var timer = null;

    return function(){
        var now = +new Date();

        if(!previous) previous = now;
        //当上一次执行的时间与当前的时间差大于设置的执行间隔时长的话,就主动执行一次
        if(now - previous > time){
            clearTimeout(timer);
            fn();
            previous = now;// 执行函数后,马上记录当前时间
        }else{
            clearTimeout(timer);
            timer = setTimeout(function(){
                fn();
            },wait);
        }
    }
}
function _log(){
    console.log(1)
}
window.onscroll = _debounce(_log,500,2000)

函数节流(throttle)

function _throttle(fn, time) { 

  let _self = fn, 
      timer,  
      firstTime = true //记录是否是第一次执行的flag

  return function() { 
    let args = arguments, //解决闭包传参问题
        _me = this //解决上下文丢失问题

    if(firstTime) { //若是第一次,则直接执行
      _self.apply(_me, args)
      return firstTime = false
    }
    if(timer) { //定时器存在,说明有事件监听器在执行,直接返回
      return false
    }

    timer = setTimeout(function() { 
      clearTimeout(timer)
      timer = null
      _self.apply(_me, args)
    }, time || 500)
  }
}

function _log(){
    console.log(1)
}
window.onscroll = _throttle(_log,500)

总结

函数防抖和函数分流的思想都是通过定时器控制函数的执行频率。

参考:

https://blog.csdn.net/w_q_1025/article/details/64221654

https://www.cnblogs.com/woodyblog/p/6238445.html

jquery中attr和prop的区别

今天研究前端jquery的attr给固有属性加值是出现错误,搜索了下,找到了原因,就是:jquery中attr和prop的区别。下面和大家分享下:

在高版本的jquery引入prop方法后,什么时候该用prop?什么时候用attr?它们两个之间有什么区别?这些问题就出现了。

关于它们两个的区别,网上的答案很多。这里谈谈我的心得,我的心得很简单:

  1. 对于HTML元素本身就带有的固有属性,在处理时,使用prop方法。
  2. 对于HTML元素我们自己自定义的DOM属性,在处理时,使用attr方法。

上面的描述也许有点模糊,举几个例子就知道了。

<a href="http://www.baidu.com" target="_self" class="btn">百度</a>

这个例子里元素的DOM属性有“href、target和class”,这些属性就是元素本身就带有的属性,也是W3C标准里就包含有这几个属性,或者说在IDE里能够智能提示出的属性,这些就叫做固有属性。处理这些属性时,建议使用prop方法。

<a href="#" id="link1" action="delete">删除</a>

这个例子里元素的DOM属性有“href、id和action”,很明显,前两个是固有属性,而后面一个“action”属性是我们自己自定义上去的,元素本身是没有这个属性的。这种就是自定义的DOM属性。处理这些属性时,建议使用attr方法。使用prop方法取值和设置属性值时,都会返回undefined值。

再举一个例子:

<input id="chk1" type="checkbox" />是否可见
<input id="chk2" type="checkbox" checked="checked" />是否可见

像checkbox,radio和select这样的元素,选中属性对应“checked”和“selected”,这些也属于固有属性,因此需要使用prop方法去操作才能获得正确的结果。

$("#chk1").prop("checked") == false
$("#chk2").prop("checked") == true

如果上面使用attr方法,则会出现:

$("#chk1").attr("checked") == undefined
$("#chk2").attr("checked") == "checked"