BAT 前端开发面经 —— 吐血总结 前端相关片断整理——持续更新 前端基础精简总结 Web Storage You don't know js

BAT 前端开发面经 —— 吐血总结

 

 

更好阅读,请移步这里

聊以前

最近暑期实习招聘已经开始,我的目前参加了阿里的内推及腾讯和百度的实习生招聘,在此总结一下
一是备忘、总结提高,二是但愿给你们一些参考
其余面试及基础相关能够参考其余博文:css

每位面试官的面试时间基本都在 40-80 分钟,下面先简要介绍各个面试流程,问题详情见具体公司分类html

腾讯内推&校招 offer got前端

首先腾讯分为三面,都为技术面:vue

  • 初试一个面试官
  • 复试两个面试官,包括电话远程 online codinghtml5

    (我也不知道为何几天内三个面试官面我,只算两个面试过程 ヾ(´A`)ノ゚)
  • 终面一位面试官java

    这位应该是大 boss ,他说并非前端开发的,面试的问题也不会很深刻,主要看我的对这个领域的想法以及对常见问题在前端领域的解决思路,不按套路出牌,emmm 感受回答的不理想
    相似这种问题:
    • 说说最能体现你能力的工做
    • 网络安全的实现node

      如何检测恶意脚本
      如何屏蔽
      ...react

腾讯二面 & online codingwebpack

  • 腾讯的远程 online coding 时间较长,大概一个多小时,不只要按要求编程,也要口述思路以及关键点,过程相似压力面,面试官说完题目要求会给你思考及coding 时间,他能够远程看到面试者的 coding 状态,主要考察应变能力,思惟活跃度,编码习惯,调试能力,以及测试方法,本人在此过程当中没有太注意测试过程,致使对于特殊状况考虑不全面,测试样例不完善,望小伙伴们注意 ヾ(´A`)ノ゚,不过,在口述代码时发现也能够本身提出来须要完善的地方。

  • coding 为一到两题,后续问题都是围绕它结合实际应用进行拓展,主要考察是否能灵活运用以及类似的思路转换,当时面试时间太长以及基础知识较差,进制转换,存储那些个基础被小学老师收回,一连串的炮轰简直爽歪歪,我的表示此过程本身的表现较差,也要重视基础基础啊老铁们 ( ̄_ ̄ )

经历了腾讯云的四个面试官,以及其余部门一个面试官的酱油面,腾讯的技术面试官广泛语速较快,思路转换很快,要跟上面试官的节奏,聊到本身比较熟悉的能够多说几句,他们也会顺着你回答的内容进行深刻,也会引导面试者的回答方向,若是不太熟尽可能坦白一些,不懂装懂很容易 gg

才接到通知另加两轮面试委员会现场技术面,简直爽歪歪 ヾ(´A`)ノ

腾讯校招现场

  • 一面

    面试官很 nice,基本看着简历问的,话题围绕项目(项目隔得时间过久,心里宁愿粗暴的技术面),基本为大体的了解,包括平时的学习习惯和关注的技术发展
    • 问到了 ES5 ES6 ES7 甚至 ES8
    • 项目中用过vue, 就问了 react vue 相关
    • 实习中的工做方式,如何处理设计师的设计稿,回答了包括考虑响应式布局的经常使用方案
      • sass--定义function,使用rem

        问是否有其余负面做用

        想到了页面闪烁,因为初始化时须要计算页面尺寸,可能有缩放

        如何避免:

        <!-- 若是在页面加载完成后,页面是用js动态添加的,这个问题就不太明显, --> doc.addEventListener('DOMContentLoaded‘', function(e) { <!-- doc.body.style.fontSize = 12 * dpr + 'px'; 淘宝处理 --> }, false);
      • 媒体查询

    • 移动端和pc端的工做
  • 二面

    面试官不是前端的,因此没有问很深刻的问题,主要模拟实际场景考察实现思路以及先后端的方案,后端没说出什么,基本上只是就前端层面进行了描述,包括实现思路、须要注意的问题、以及性能和安全方面的考虑
    • 前端安全
    • 场景考察
      • 直播弹幕处理
      • 后端返回大量数据且包含运算,如何实时显示
      • 。。。记不清了
  • HR 面
    • 介绍经历
    • 家庭状况
    • 职业规划
    • 对腾讯的认识及与其余实习单位对比
    • 对比实习过的企业
    • 是否还报了其余企业
    • 男友工做找的怎么样了

阿里内推 二面 卒

我投的是蚂蚁金服的前端开发,投过简历硬生生排队等了12天,还被内推人提早告知蚂蚁的前端很严格 ( ̄_ ̄ )
阿里分为在线测试,初试 ......

  • 首先是在线测试

    投完简历后官网会有相关在线测试题
    阿里前端的在线测试只有一道coding 题,限时 30 分,因为初次在线答题,看着倒计时紧张的思路不通,未能准确理解题意,但实际题目并不难,考察使用原生 js 实现相似css 的层级选择器的功能,具体题目记不太清,将仅存的记忆写了下来并附上我的实现,详情见本文后部分
  • 一面
  • 二面
  • 挂掉了。。。

baidu 等通知

百度投的是核心搜索部门,但一面后估计不合适,没有了消息,后简历转到百度金融

  • 一面

    面试官语速很快,通常不给太多思考时间--------感受本身说话都打着节拍 ( ̄_ ̄ )

  • 二面

    面试官很和善,是惟一一个认真看了我博客的面试官,很荣幸,也很紧张

    基本偏向基础
    但因为没安排好时间,面试时在户外,听不太清面试官的问题,在此提醒各位小伙伴提早选好面试场地,避免环境影响

  • 三面

    一样和善的面试官,开始考察基础知识,编译原理,网络协议等基础,后面考察我的素质,后续更注重我的经历,包括如何学习,找实习,实习中遇到的问题及如何解决,最骄傲的事情等等

关于结尾

  • 百度 & 阿里面试结束后都有问:你以为在面试过程当中有什么没问到,但本身掌握比较好的技能么

    面阿里时,头脑发晕,回答:没有,我感受您面的很专业,问的知识点都是比较核心的 ( ̄_ ̄ )
    百度经验: 回答本身掌握还比较好的知识后,面试官真的会问不少问题(菜鸟能够参考以上作法),若是面试官面试的较偏,倒能够补充
  • 有什么要问的
    • 腾讯二面教训

      我:是否有导师带,实习的大体安排以及部门业务等

      效果 

      面试官曰:

      你那么关系有没有导师的问题,但学习中最重要的是靠本身,导师并非负责回答你全部问题的
    • 百度经验

      我:部门是否有按期交流分享的机会;工做中是按照职位仍是业务部门划分,如何交流;偏向页面仍是业务逻辑

      我的在自学过程当中发现前端体系太大,不知对于前端学习,您有什么建议么

      面试官:不少前端初学者都有相似的困惑,你最好本身从头开始作一个项目,无论是本身瞎想的仍是模仿的,在项目过程当中去发现须要学习什么技术,在遇到问题的时候才知道去哪一个方向发展和思考,只有在项目中才能持续的学习和提升,前端知识很碎,没有项目很难有一连串的经历

整体

整体来讲,面试中有按照面试题出的,也有直接聊的,通常也会结合实际工做中会遇到的场景以及技术中的一些坑,回答时结合本身的项目经验会更好,大厂的面试官更侧重于面试者对深层原理的理解,对于实习生来讲通常面基础,若是有深查原理的习惯,我的的可塑造性也会较高

三厂体验对比:

  • 腾讯阿里面试官一面开始都比较侧重实践,若是简历上有过实践项目和实习经历,会问更实际的问题,构造场景让解答
  • 协议都会问到

  • 相对来讲,百度更多基础知识点,更多考察对更基本的知识掌握,不限于前端,还包括组成原理和编译原理的一些知识,固然前端偏多(好比选取一个class 标签元素有几种方式等小细节的问题来考察,细到要把每一个表达说完整,把每一个单词拼出来)

  • 阿里腾讯更侧重应用中的注意事项(如:IE 和其余浏览器中的事件处理机制)不太揪细节

  • 三厂都有问到算法,腾讯相对更注重对算法和逻辑,对面试者基础知识要求较高,甚至涉及更底层的。

  • 另两厂对算法,数据结构的要求都是了解阶段

如下为面试中的一些知识点以及我的的一些补充,敲黑板啦啦啦

1. Tencent

1.1. js 的事件机制

事件阶段

通常的,事件分为三个阶段:捕获阶段、目标阶段和冒泡阶段。

  • 捕获阶段(Capture Phase)

    • 事件的第一个阶段是捕获阶段。事件从文档的根节点流向目标对象节点。途中通过各个层次的DOM节点,并在各节点上触发捕获事件,直到到达事件的目标节点。捕获阶段的主要任务是创建传播路径,在冒泡阶段,事件会经过这个路径回溯到文档跟节点。
    • 或这样描述:

      任何事件产生时,如点击一个按钮,将从最顶端的容器开始(通常是html的根节点)。浏览器会向下遍历DOM树直到找到触发事件的元素,一旦浏览器找到该元素,事件流就进入事件目标阶段

  • 目标阶段(Target Phase)

    • 当事件到达目标节点的,事件就进入了目标阶段。事件在目标节点上被触发,而后会逆向回流,直到传播至最外层的文档节点。
  • 冒泡阶段(Bubble Phase)
    • 事件在目标元素上触发后,并不在这个元素上终止。它会随着DOM树一层层向上冒泡,回溯到根节点。
    • 冒泡过程很是有用。它将咱们从对特定元素的事件监听中释放出来,若是没有事件冒泡,咱们须要监听不少不一样的元素来确保捕获到想要的事件

事件处理程序

  • DOM0 级事件处理程序

    var btn5 = document.getElementById('btn5'); btn5.onclick=function(){ console.log(this.id);//btn5 };
    • 基于 DOM0 的事件,对于同一个 dom 节点而言,只能注册一个,后边注册的 同种事件 会覆盖以前注册的。

      利用这个原理咱们能够解除事件,btn5.onclick=null;其中this就是绑定事件的那个元素;
    • 以这种方式添加的事件处理程序会在事件流的冒泡阶段被处理;

  • DOM2 级事件处理程序
    • DOM2支持同一dom元素注册多个同种事件,事件发生的顺序按照添加的顺序依次触发(IE是相反的)。
    • DOM2事件经过 addEventListener 和 removeEventListener 管理

      // addEventListener(eventName,handlers,boolean);removeEventListener() // 两个方法都同样接收三个参数,第一个是要处理的事件名,第二个是事件处理程序, // 第三个值为false时表示在事件冒泡阶段调用事件处理程序,通常建议在冒泡阶段使用, // 特殊状况才在捕获阶段; // 注意:经过addEventListener()添加的事件处理程序只能用removeEventListener()来移除 // 而且移除时传入的参数必须与添加时传入的参数同样;好比 var btn2 = document.getElementById('btn2'); var handlers = function () { console.log(this.id); }; btn2.addEventListener('click',handlers,false); btn2.removeEventListener('click',handlers.false);
    • ie 事件处理程序

      //IE事件处理程序(IE和Opera支持) /* IE用了attachEvent(),detachEvent(),接收两个参数,事件名称和事件处理程序,  * 经过attachEvent()添加的事件处理程序都会被添加到冒泡阶段,因此平时为了兼容更多的浏览器最好将事件添加到事件冒泡阶段,IE8及之前只支持事件冒泡;  */ var btn3 = document.getElementById('btn3'); var handlers2=function(){ console.log(this===window); // true,注意attachEvent()添加的事件处理程序运行在全局做用域; }; btn3.attachEvent('onclick',handlers2);
---ie 与其余浏览器的区别

总结

DOM事件模型中的事件对象经常使用属性:

  • type用于获取事件类型
  • target获取事件目标
  • stopPropagation()阻止事件冒泡
  • preventDefault()阻止事件默认行为
  • 判断加载状态 —— onload 事件

IE事件模型中的事件对象经常使用属性:

  • type用于获取事件类型
  • srcElement获取事件目标
  • cancelBubble 阻止事件冒泡
  • returnValue 阻止事件默认行为
  • 经过 readystate 属性值判断什么时候方法下载完毕可用

    readystate共有如下几个值:
    • uninitialized: 对象存在但未初始化;
    • loading:对象正在加载;
    • loaded:对象数据加载完毕;
    • interactive:能够操做对象了,但还没加载完毕;
    • complete:加载完毕。

    注意上面5个值并不必定每一个事件都全包含,而且不必定是什么顺序。

    Document.readyState 属性
    一个文档的 readyState 能够是如下之一:
    • loading / 加载

      document 仍在加载。
    • interactive / 互动

      文档已经完成加载,文档已被解析,可是诸如图像,样式表和框架之类的子资源仍在加载。
    • complete / 完成

      T文档和全部子资源已完成加载。状态表示 load 事件即将被触发。

    当这个属性的值变化时,document 对象上的readystatechange 事件将被触发。

事件对象

  • IE

    IE中事件对象是做为全局对象 window.event 存在的
  • Firefox

    Firefox中则是作为句柄( handler )的第一个参数传入
  • 通用

    var evt = window.event || arguments[0];

事件监听

  • Chrome、FireFox、Opera、Safari、IE9.0及其以上版本

    addEventListener(eventName,handler,boolean); removeEventListener() /* 两个方法都同样接收三个参数, * 事件名 * 事件处理程序 * boolean false时表示在事件冒泡阶段调用事件处理程序,通常建议在冒泡阶段使用 */

  • IE8.0及其如下版本

    element.attachEvent(type, handler); element.detachEvent(type, handler); /* element 要绑定事件的对象,html 节点 * type 事件类型 +'on' 如: "onclick, onmouseover" * listener 事件处理程序(只写函数名,不带括号) */
  • 早期浏览器
    obj['on' + type] = handler

阻止冒泡

  • event.stopPropagation
  • event.cancelBubble = true //IE

阻止默认事件

  • event.preventDefault()
  • event.returnValue = false //IE
---通用的事件监听器
// event(事件)工具集,来源:github.com/markyun markyun.Event = { // 页面加载完成后 readyEvent: function (fn) { if (fn == null) { fn = document; } var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = fn; } else { window.onload = function () { oldonload(); fn(); }; } }, // 视能力分别使用dom0||dom2||IE方式 来绑定事件 // 参数: 操做的元素,事件名称 ,事件处理程序 addEvent: function (element, type, handler) { if (element.addEventListener) { //事件类型、须要执行的函数、是否捕捉 element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, function () { handler.call(element); }); } else { element['on' + type] = handler; } }, // 移除事件 removeEvent: function (element, type, handler) { if (element.removeEnentListener) { element.removeEnentListener(type, handler, false); } else if (element.detachEvent) { element.detachEvent('on' + type, handler); } else { element['on' + type] = null; } }, // 阻止事件 (主要是事件冒泡,由于IE不支持事件捕获) stopPropagation: function (ev) { if (ev.stopPropagation) { ev.stopPropagation(); } else { ev.cancelBubble = true; } }, // 取消事件的默认行为 preventDefault: function (event) { if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } }, // 获取事件目标 getTarget: function (event) { return event.target || event.srcElement; }, // 获取event对象的引用,取到事件的全部信息,确保随时能使用event; getEvent: function (e) { var ev = e || window.event; if (!ev) { var c = this.getEvent.caller; while (c) { ev = c.arguments[0]; if (ev && Event == ev.constructor) { break; } c = c.caller; } } return ev; } };

1.2. vue

---Vue 生命周期
---双向绑定原理 & 如何实现
---vuex 原理
---vue 数据更新后执行

1.3. 跨域

什么叫跨域

方案

1.4. 安全 && 怎样预防

1.5. session & cookie

1.6. 本地存储

1.7. 浏览器缓存

1.8. 页面从输入URL 到加载过程

1.9. HTTP

---content-type
  • application/x-www-form-urlencoded

    最多见的 POST 提交数据的方式了。浏览器的原生 form 表单,若是不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded方式提交数据。

    传递的key/val会通过URL转码,因此若是传递的参数存在中文或者特殊字符须要注意。

    //例子 //b=曹,a=1 POST HTTP/1.1(CRLF) Host: www.example.com(CRLF) Content-Type: application/x-www-form-urlencoded(CRLF) Cache-Control: no-cache(CRLF) (CRLF) b=%E6%9B%B9&a=1(CRLF) //这里b参数的值"曹"由于URL转码变成其余的字符串了
  • multipart/form-data

    常见的 POST 数据提交的方式。咱们使用表单上传文件时,必须让 form 的 enctyped 等于这个值
    而且Http协议会使用boundary来分割上传的参数

  • text/xml

    <!-- 例子 --> POST http://www.example.com HTTP/1.1(CRLF) Content-Type: text/xml(CRLF) (CRLF) <?xml version="1.0"?> <resource> <id>123</id> <params> <name> <value>homeway</value> </name> <age> <value>22</value> </age> </params> </resource>
  • application/json

    用来告诉服务端消息主体是序列化后的 JSON 字符串

    //例子 //传递json POST HTTP/1.1(CRLF) Host: www.example.com(CRLF) Content-Type: application/json(CRLF) Cache-Control: no-cache(CRLF) Content-Length: 24(CRLF) (CRLF) { "a":1, "b":"hello" }

(CRLF)指 \r\n
参考: HTTP常见Content-Type比较

1.10. get & post

1.11. TCP & UDP & 握手

---TCP (Transmission Control Protocol)

两个序号和三个标志位:

  • 序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  • 确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。

标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义以下:

  • URG:紧急指针(urgent poin* 有效。
  • ACK:acknowledgement 确认序号有效。
  • PSH:接收方应该尽快将这个报文交给应用层。
  • RST:reset 重置链接。
  • SYN:synchronous 创建联机,发起一个新链接。
  • FIN:finish 释放一个链接。

须要注意的是:

  • 不要将确认序号ack与标志位中的ACK搞混了。
  • 确认方ack=发起方req+1,两端配对。

  1. 第一次握手:
    Client将标志位SYN置为1,随机产生一个值 seq=J,并将该数据包发送给Server
    Client进入SYN_SENT状态,等待Server确认。
  2. 第二次握手:
    Server收到数据包后由标志位SYN=1知道Client请求创建链接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认链接请求,Server进入SYN_RCVD状态。
  3. 第三次握手:
    Client收到确认后,检查ack是否为J+1,ACK是否为1,若是正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,若是正确则链接创建成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间能够开始传输数据了

为何须要确认

---四次挥手

因为TCP链接时全双工的,所以,每一个方向都必需要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的链接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,可是在这个TCP链接上仍然可以发送数据,直到这一方向也发送了FIN

首先进行关闭的一方将执行主动关闭,而另外一方则执行被动关闭,上图描述的便是如此。

  • 第一次挥手:
    Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
  • 第二次挥手:
    Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  • 第三次挥手:
    Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
  • 第四次挥手:
    Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。

为何TIME_WAIT状态须要通过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

  • 可靠的实现 TCP 全双工链接的终止
  • 容许老的重复分节在网络中消失

MSL是Maximum Segment Lifetime英文的缩写,中文能够译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃,RFC 793中规定MSL为2分钟,实际应用中经常使用的是30秒,1分钟和2分钟等。2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态

为何链接的时候是三次握手,关闭的时候倒是四次握手?

  • 由于当Server端收到Client端的SYN链接请求报文后,能够直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。可是关闭链接时
  • 当Server端收到FIN报文时,极可能并不会当即关闭SOCKET,因此只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端全部的报文都发送完了,我才能发送FIN报文,所以不能一块儿发送。故须要四步握手。

参考: TCP三次握手详解及释放链接过程

1.12. HTTP 加密

---加密对象

对于HTTP协议来讲,加密的对象有如下两个:

  • 对通讯的加密:
    HTTP中没有加密功能,可是能够经过和SSL(Secure Socket Layer,安全套接层)组合使用,加密通讯内容。使用SSL创建安全通讯线路后,就能够在这条线路上进行HTTP通讯了。与SSL组合使用的HTTP被称为HTTPS(HTTP Secure,超文本传输安全协议)。
  • 对通讯内容自己进行加密
    即对HTTP报文里所包含的内容进行加密。这样,首先客户端要先对报文进行加密,而后再发给服务器。服务器在接受到请求时,须要对报文进行解密,再处理报文。该方式不一样于SSL将整个通讯线路进行加密处理,因此内容仍然有被篡改的风险。
    • A、任何人均可以发起请求
      HTTP协议中,并未有确认通讯方这一步骤,因此,任何人均可以发送请求,而服务器在接受到任何请求时,都会作出相应的响应。
      • 解决方案:

        查明对手的证书

        虽然HTTP不能确认通讯方,但SSL是能够的。SSL不只提供了加密处理,还使用了"证书"的手段,可用于确认通讯方。证书是由值得信赖的第三方机构颁布,可用于肯定证实服务器和客户端是实际存在的。因此,只要能确认通讯方持有的证书,便可判断通讯方的真实意图。
    • B、没法判断报文是否完整(报文可能已遭篡改)

      HTTP协议没法判断报文是否被篡改,在请求或者响应发出后,在对方接收以前,即便请求或者响应遭到篡改是没法得知的。
      • 防止篡改:

        经常使用的,肯定报文完整性方法:MD五、SHA-1 等 散列值校验方法,以及用来确认文件的数字签名方法。可是,使用这些方法,也没法百分百确保结果正确,由于MD5自己被修改的话,用户是没办法意识到得。

    为了有效防止这些弊端,能够采用HTTPS。
  • POST 用户安全登录

    在关系到用户隐私的时候,要时刻遵循两个原则:
    • 任何应用程序都不能在本地存储与安全相关的用户信息
    • 任何应用程序在向服务器传递数据的时候,都不能直接传递与安全相关的用户信息。

    要想让用户信息安全,就必须对其进行加密,让别人即使是拿到了安全信息,摆在眼前的也是一串乱码,没有半点用处

    MD5是一种经常使用的加密方法,它是一种散列函数,利用MD5对用户信息进行加密,会增长用户信息安全性。

    网上有关于MD5的第三方框架Category

    利用这个第三方框架能够实现对密码进行MD5加密

    +随机乱码字符防止被破解

---加密算法

对称加密

非对称加密

---HTTPS 加密原理

HTTPS简介

HTTPS实际上是有两部分组成:HTTP + SSL / TLS,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会经过TLS进行加密,因此传输的数据都是加密后的数据

SSL协议是经过非对称密钥机制保证双方身份认证,并完成创建链接,在实际数据通讯时经过对称密钥机制保障数据安全性

  • 服务器 用RSA生成公钥和私钥

  • 把公钥放在证书里发送给客户端,私钥本身保存

  • 客户端首先向一个权威的服务器检查证书的合法性,若是证书合法,客户端产生一段随机数,这个随机数就做为通讯的密钥,咱们称之为对称密钥,用公钥加密这段随机数,而后发送到服务器

  • 服务器用密钥解密获取对称密钥,而后,双方就已对称密钥进行加密解密通讯了

HTTPS 在传输数据以前须要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程当中将确立双方加密传输数据的密码信息。TLS/SSL 协议不只仅是一套加密传输的协议,更是一件通过艺术家精心设计的艺术品,TLS/SSL 中使用了非对称加密,对称加密以及 HASH 算法。握手过程的具体描述以下:

  1. 浏览器将本身支持的一套加密规则发送给网站。
  2. 网站从中选出一组加密算法与HASH算法,并将本身的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
  3. 浏览器得到网站证书以后浏览器要作如下工做:

    a) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),若是证书受信任,则浏览器栏里面会显示一个小锁头,不然会给出证书不受信的提示。

    b) 若是证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。

    c) 使用约定好的HASH算法计算握手消息,并使用生成的随机数对消息进行加密,最后将以前生成的全部信息发送给网站。
  4. 网站接收浏览器发来的数据以后要作如下的操做:

    a) 使用本身的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。

    b) 使用密码加密一段握手消息,发送给浏览器。
  5. 浏览器解密并计算握手消息的HASH,若是与服务端发来的HASH一致,此时握手过程结束,以后全部的通讯数据将由以前浏览器生成的随机密码并利用对称加密算法进行加密。

参考:图解HTTPS

如何保证 HTTP 传输安全性

  • 重要的数据,要加密

    好比用户名密码(若是简单的md5,是能够暴力破解),常见的是 md5(不可逆),aes(可逆),自由组合,还能够加一些特殊字符

    举例:username = aes(username), pwd = MD5(pwd + username)

  • 非重要数据,要签名

    签名的目的是为了防止篡改,好比 http://www.xxx.com/getnews?id=1,获取id为1的新闻,若是不签名那么经过id=2,就能够获取2的内容等等。怎样签名呢?

    一般使用sign,好比原连接请求的时候加一个 sign 参数,sign=md5(id=1),服务器接受到请求,验证sign是否等于 md5(id=1) ,若是等于说明正常请求。

    这会有个弊端,假如规则被发现,那么就会被伪造,因此适当复杂一些,仍是可以提升安全性的。

  • 登陆态怎么作

    http是无状态的,也就是服务器无法本身判断两个请求是否有联系,那么登陆以后,之后的接口怎么断定是否登陆呢

    简单的作法,在数据库中存一个token字段(名字随意),当用户调用登录接口成功的时候,就将该字段设一个值,(好比aes(过时时间)),同时返回给前端,之后每次前端请求带上该值,服务器首先校验是否过时,其次校验是否正确,不经过就让其登录。(redis 作这个很方便哦,key有过时时间)

来自:如何保证http传输安全性

1.13. 排序:冒泡,选择,快速

1.14. 数据库

---触发器

一种特殊的存储过程,存储过程通常经过定义的名字直接调用,而触发器是经过增、删、改进行触发执行的。会在事件发生时自动强制执行

触发器是一种特殊的存储过程,主要是经过事件来触发而被执行的。它能够强化约束,来维护数据的完整性和一致性,能够跟踪数据库内的操做从而不容许未经许可的更新和变化。能够联级运算。如,某表上的触发器上包含对另外一个表的数据操做,而该操做又会致使该表触发器被触发。

---事务 & 锁
  • 事务

    就是被绑定在一块儿做为一个逻辑工做单元的SQL语句分组,若是任何一个语句操做失败那么整个操做就被失败,之后操做就会回滚到操做前状态,或者是上有个节点。

    为了确保要么执行,要么不执行,就能够使用事务。要将一组语句做为事务考虑,就须要经过ACID测试,即原子性,一致性,隔离性和持久性。
  • 锁:

    在全部的DBMS中,锁是实现事务的关键,锁能够保证事务的完整性和并发性。与现实生活中锁同样,它能够使某些数据的拥有者,在某段时间内不能使用某些数据或数据结构。固然锁还分级别的。共享锁(只读不写)、排他锁(可读可写)

1.15. 软件设计模式

设计原则

  • 对接口编程而不是对实现编程
  • 优先使用对象组合而不是继承

---单例模式

单体是一个用来划分命名空间并将一批相关的属性和方法组织在一块儿的对象,若是他能够被实例化,那么他只能被实例化一次

// 对象字面量 var Singleton = { attr1: 1, attr2: 2, method1: function(){ return this.attr1; }, method2: function(){ return this.attr2; } }; // 上面的全部成员变量都是经过Singleton来访问的,可是它并非单体模式; // 由于单体模式还有一个更重要的特色,就是能够仅被实例化一次,上面的只是不能被实例化的一个类,所以不是单体模式;对象字面量是用来建立单体模式的方法之一; /*要实现一个单体模式的话,咱们无非就是使用一个变量来标识该类是否被实例化 若是未被实例化的话,那么咱们能够实例化一次,不然的话,直接返回已经被实例化的对象 */ // 单体模式 var Singleton = function(name){ this.name = name; this.instance = null; }; Singleton.prototype.getName = function(){ return this.name; } // 获取实例对象 function getInstance(name) { if(!this.instance) { this.instance = new Singleton(name); } return this.instance; } // 测试单体模式的实例 var a = getInstance("aa"); var b = getInstance("bb"); console.log(a === b) // true console.log(a.getName()) // aa console.log(b.getName()) // aa

应用案例

  • 弹窗

    传统建立:好比我点击一个元素须要建立一个div,我点击第二个元素又会建立一次div,咱们频繁的点击某某元素,他们会频繁的建立div的元素,虽然当咱们点击关闭的时候能够移除弹出代码,可是呢咱们频繁的建立和删除并很差,特别对于性能会有很大的影响,对DOM频繁的操做会引发重绘等,从而影响性能;所以这是很是很差的习惯;咱们如今能够使用单体模式来实现弹窗效果,咱们只实例化一次就能够

编写通用的单体模式

咱们使用一个参数fn传递进去,若是有result这个实例的话,直接返回,不然的话,当前的getInstance函数调用fn这个函数,是this指针指向与这个fn这个函数;以后返回被保存在result里面;如今咱们能够传递一个函数进去,无论他是建立div也好,仍是建立iframe也好,总之若是是这种的话,均可以使用getInstance来获取他们的实例对象;

// 建立div var createWindow = function(){ var div = document.createElement("div"); div.innerHTML = "我是弹窗内容"; div.style.display = 'none'; document.body.appendChild(div); return div; }; // 建立iframe var createIframe = function(){ var iframe = document.createElement("iframe"); document.body.appendChild(iframe); return iframe; }; // 获取实例的封装代码 var getInstance = function(fn) { var result; return function(){ return result || (result = fn.call(this,arguments)); } }; // 测试建立div var createSingleDiv = getInstance(createWindow); document.getElementById("Id").onclick = function(){ var win = createSingleDiv(); win.style.display = "block"; }; // 测试建立iframe var createSingleIframe = getInstance(createIframe); document.getElementById("Id").onclick = function(){ var win = createSingleIframe(); win.src = "http://cnblogs.com"; };
---如下为补充
---工厂模式

客户类和工厂类分开。消费者任什么时候候须要某种产品,只需向工厂请求便可。消费者无须修改就能够接纳新产品。

工厂模式是为了解决多个相似对象声明的问题;也就是为了解决实列化对象产生重复的问题。

  • 优势:能解决多个类似的问题。
  • 缺点:
    • 不能知道对象识别的问题(对象的类型不知道)。
    • 当产品修改时,工厂类也要作相应的修改。
function CreatePerson(name,age,sex) { var obj = new Object(); obj.name = name; obj.age = age; obj.sex = sex; obj.sayName = function(){ return this.name; } return obj; } var p1 = new CreatePerson("longen",'28','男'); var p2 = new CreatePerson("tugenhua",'27','女');
---模块模式

模块模式的思路是为单体模式添加私有变量和私有方法可以减小全局变量的使用

prototype + constructor

---装饰者模式

装饰者(decorator)模式可以在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责(方法或属性)。
与继承相比,装饰者是一种更轻便灵活的作法。

能够动态的给某个对象添加额外的职责,而不会影响从这个类中派生的其它对象。

// ES7装饰器 function isAnimal(target) { target.isAnimal = true return target } // 装饰器 @isAnimal class Cat { // ... } console.log(Cat.isAnimal) // true // 做用于类属性的装饰器: function readonly(target, name, descriptor) { discriptor.writable = false return discriptor } class Cat { @readonly say() { console.log("meow ~") } } var kitty = new Cat() kitty.say = function() { console.log("woof !") } kitty.say() // meow ~
---观察者模式(发布-订阅)

发布---订阅模式又叫观察者模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,全部依赖于它的对象都将获得通知

发布订阅模式的流程以下:

  1. 肯定谁是发布者(好比个人博客)。
  2. 而后给发布者添加一个缓存列表,用于存放回调函数来通知订阅者。
  3. 发布消息,发布者须要遍历这个缓存列表,依次触发里面存放的订阅者回调函数。
  4. 退订(好比不想再接收到这些订阅的信息了,就能够取消掉)

【实现事件模型】

即写一个类或是一个模块,有两个函数,一个bind一个trigger,分别实现绑定事件和触发事件,核心需求就是能够对某一个事件名称绑定多个事件响应函数,而后触发这个事件名称时,依次按绑定顺序触发相应的响应函数。

大体实现思路就是建立一个类或是匿名函数,在bind和trigger函数外层做用域建立一个字典对象,用于存储注册的事件及响应函数列表,bind时,若是字典没有则建立一个,key是事件名称,value是数组,里面放着当前注册的响应函数,若是字段中有,那么就直接push到数组便可。trigger时调出来依次触发事件响应函数便可

var Event = (function(){ var list = {}, listen, trigger, remove; listen = function(key,fn){ if(!list[key]) { list[key] = []; } list[key].push(fn); }; trigger = function(){ var key = Array.prototype.shift.call(arguments), fns = list[key]; if(!fns || fns.length === 0) { return false; } for(var i = 0, fn; fn = fns[i++];) { fn.apply(this,arguments); } }; remove = function(key,fn){ var fns = list[key]; if(!fns) { return false; } if(!fn) { fns && (fns.length = 0); }else { for(var i = fns.length - 1; i >= 0; i--){ var _fn = fns[i]; if(_fn === fn) { fns.splice(i,1); } } } }; return { listen: listen, trigger: trigger, remove: remove } })(); // 测试代码以下: Event.listen("color",function(size) { console.log("尺码为:"+size); // 打印出尺码为42 }); Event.trigger("color",42);
---代理模式

代理是一个对象,它能够用来控制对本体对象的访问,它与本体对象实现了一样的接口,代理对象会把全部的调用方法传递给本体对象的
本地对象注重的去执行页面上的代码,代理则控制本地对象什么时候被实例化,什么时候被使用

优势:

  • 代理对象能够代替本体被实例化,并使其能够被远程访问;
  • 它还能够把本体实例化推迟到真正须要的时候;对于实例化比较费时的本体对象,或者由于尺寸比较大以致于不用时不适于保存在内存中的本体,咱们能够推迟实例化该对象;
// 先申明一个奶茶妹对象 var TeaAndMilkGirl = function(name) { this.name = name; }; // 这是京东ceo先生 var Ceo = function(girl) { this.girl = girl; // 送结婚礼物 给奶茶妹 this.sendMarriageRing = function(ring) { console.log("Hi " + this.girl.name + ", ceo送你一个礼物:" + ring); } }; // 京东ceo的经纪人是代理,来代替送 var ProxyObj = function(girl){ this.girl = girl; // 经纪人代理送礼物给奶茶妹 this.sendGift = function(gift) { // 代理模式负责本体对象实例化 (new Ceo(this.girl)).sendMarriageRing(gift); } }; // 初始化 var proxy = new ProxyObj(new TeaAndMilkGirl("奶茶妹")); proxy.sendGift("结婚戒"); // Hi 奶茶妹, ceo送你一个礼物:结婚戒
  • TeaAndMilkGirl 是一个被送的对象(这里是奶茶妹);
  • Ceo 是送礼物的对象,他保存了奶茶妹这个属性,及有一个本身的特权方法sendMarriageRing 就是送礼物给奶茶妹这么一个方法;
  • 而后呢他是想经过他的经纪人去把这件事完成,因而须要建立一个经济人的代理模式,名字叫ProxyObj ;
  • 他的主要作的事情是,把ceo交给他的礼物送给ceo的情人,所以该对象一样须要保存ceo情人的对象做为本身的属性,同时也须要一个特权方法sendGift ,该方法是送礼物,所以在该方法内能够实例化本体对象,这里的本体对象是ceo送花这件事情,所以须要实例化该本体对象后及调用本体对象的方法(sendMarriageRing).

理解使用虚拟代理实现图片的预加载

在网页开发中,图片的预加载是一种比较经常使用的技术,若是直接给img标签节点设置src属性的话,若是图片比较大的话,或者网速相对比较慢的话,那么在图片未加载完以前,图片会有一段时间是空白的场景,这样对于用户体验来说并很差,那么这个时候咱们能够在图片未加载完以前咱们能够使用一个loading加载图片来做为一个占位符,来提示用户该图片正在加载,等图片加载完后咱们能够对该图片直接进行赋值便可;下面咱们先不用代理模式来实现图片的预加载的状况下代码以下:

// 不使用代理的预加载图片函数以下 var myImage = (function(){ var imgNode = document.createElement("img"); document.body.appendChild(imgNode); var img = new Image(); img.onload = function(){ imgNode.src = this.src; }; return { setSrc: function(src) { imgNode.src = "http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif"; img.src = src; } } })(); // 调用方式 myImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");
//利用代理模式来编写预加载图片 var myImage = (function(){ var imgNode = document.createElement("img"); document.body.appendChild(imgNode); return { setSrc: function(src) { imgNode.src = src; } } })(); // 代理模式 var ProxyImage = (function(){ var img = new Image(); img.onload = function(){ myImage.setSrc(this.src); }; return { setSrc: function(src) { myImage.setSrc("http://img.lanrentuku.com/img/allimg/1212/5-121204193Q9-50.gif"); img.src = src; } } })(); // 调用方式 ProxyImage.setSrc("https://img.alicdn.com/tps/i4/TB1b_neLXXXXXcoXFXXc8PZ9XXX-130-200.png");

这种懒加载方法不用代理模式也是能够实现的,只是用代理模式。咱们可让 myImage 只作一件事,只负责将实际图片加入到页面中,而loading图片交给ProxyImage去作。从而下降代码的耦合度。由于当我不想用loading的时候,能够直接调用myImage 方法。也便是说假如我门不须要代理对象的话,直接能够换成本体对象调用该方法便可
对比

  • 不代理:不知足单一职责原则,代码耦合度高
  • myimage 函数只负责一件事,其余交给代理

优势

  • 用户能够放心地请求代理,他们只关心是否能获得想要的结果。假如我门不须要代理对象的话,直接能够换成本体对象调用该方法便可。
  • 在任何使用本体对象的地方均可以替换成使用代理。

参考:Javascript设计模式详解

1.2. Online Coding

js 实现两个超大数相加
基础:

  • JS 中全部的数字类型,实际存储都是经过 8 字节 double 浮点型 表示的,并非可以精确表示范围内的全部数
  • 大整数存储(安全使用范围)
    • 其余语言 2^63 - 1
    • js Math.pow(2, 53) - 1

      //js 最大和最小安全值 Number.MAX_SAFE_INTEGER //9007199254740991 Number.MIN_SAFE_INTEGER //-9007199254740991
var largeNumberAdd = function(num1, num2) { var arr1 = num1.split(''), arr2 = num2.split(''), tem = '', num3 = 0, result = [] var longDiff = arr1.length - arr2.length if (longDiff > 0) { for (let i = 0; i < longDiff; i++) { arr2.unshift('0') } }else if (longDiff < 0) { for (let i = 0; i < Math.abs(longDiff); i++) { arr1.unshift('0') } } for (let i = arr1.length - 1; i >= 0; i--) { tem = parseInt(arr1[i]) + parseInt(arr2[i]) + num3 // check if tem > 10 if (tem >= 10) { num3 = 1 result.push((tem + '')[1]) }else { num3 = 0 result.push(tem) } } return result.reverse().join('') } // console.log(largeNumberAdd('11111','11111')) console.log(largeNumberAdd('00000000000000000000011111','333331999')) console.log(11111+333331999) // console.log(largeNumberAdd('3333333333333333333333333333333311111111111111111111111111111111111111','333333333333333331111111111111111111111111111166666666666666'))

js 每秒钟的计算量
js 如何解析后台返回的超大数据
前提:

  • js 用浮点数表示全部64位数字,全部达到 2^53 的能够被精确表示,更大的数字都会被裁剪,——如何表示64位数字
    虽然js 可以解析进制数字表示64位数字,但底层的数字表示不支持 64 位
    在浏览器中执行如下代码

    <html> <head> <script language="javascript"> function showPrecisionLimits() { document.getElementById("r50").innerHTML = 0x0004000000000001 - 0x0004000000000000; document.getElementById("r51").innerHTML = 0x0008000000000001 - 0x0008000000000000; document.getElementById("r52").innerHTML = 0x0010000000000001 - 0x0010000000000000; document.getElementById("r53").innerHTML = 0x0020000000000001 - 0x0020000000000000; document.getElementById("r54").innerHTML = 0x0040000000000001 - 0x0040000000000000; } </script> </head> <body onload="showPrecisionLimits()"> <p>(2^50+1) - (2^50) = <span id="r50"></span></p> <p>(2^51+1) - (2^51) = <span id="r51"></span></p> <p>(2^52+1) - (2^52) = <span id="r52"></span></p> <p>(2^53+1) - (2^53) = <span id="r53"></span></p> <p>(2^54+1) - (2^54) = <span id="r54"></span></p> </body> </html>

    在Firefox,Chrome和IE浏览器中,能够看到,若是可以存储64位数字,则如下减法结果皆为1。而结果相反,能够看到2 ^ 53 + 1和2 ^ 53 间的差别丢失

    2 ^ 50 + 1) - (2 ^ 50)= 1 (2 ^ 51 + 1) - (2 ^ 51)= 1 (2 ^ 52 + 1) - (2 ^ 52)= 1 (2 ^ 53 + 1) - (2 ^ 53)= 0 (2 ^ 54 + 1) - (2 ^ 54)= 0

    位运算
    所以,咱们能够选择用两个 32 位的数字表示 64 位整数,而后进行按位与

    var a = [ 0x0000ffff, 0xffff0000 ]; var b = [ 0x00ffff00, 0x00ffff00 ]; var c = [ a[0] & b[0], a[1] & b[1] ]; document.body.innerHTML = c[0].toString(16) + ":" + c[1].toString(16); //结果 ff00:ff0000

网络安全

前端网络安全的实现

  • 保证http传输安全性

如何检测恶意脚本
如何屏蔽

2. 阿里

一面

  1. 简单介绍一下本身
  2. 问题
    • HTTP 相关
      HTTP 与 HTTPS 区别,HTTPS 原理,如何加密
    • 谈谈闭包
    • 谈谈做用域链
    • 经常使用的跨域方式
    • vue
      • 特色
      • 生命周期
      • vuex
        实现原理(事件绑定)-如何实现
      • 与 react 的不一样
        应用场景
      • 如何实现双向绑定
      • 如何通讯
  3. 其余
    • 还会哪些语言
    • 还有什么感受没问到但掌握很好的
    • 是否还有其余的问题
    • 有什么问题想要问的

二面

  • react
    屡次调用 setstate 为何不立刻渲染
  • 解析 json ,手写 parse 函数
  • csrf 举例
  • 获取dom节点方式
  • 将给定节点子元素第一个和最后一个元素替换
  • 端口号的做用

端口

IP地址让网络上的两个节点之间能够创建点对点的链接

端口号则为端到端的链接提供了可能 (程序间通信的接口)

IP协议是由TCP、UDP、ARP、ICMP等一系列子协议组成的。其中

  • TCP和UDP协议

    主要用来作传输数据使用的

    在TCP和UDP协议中,都有端口号的概念存在

  • 端口号的做用

    主要是区分服务类别和在同一时间进行多个会话
    • 服务类别

      • 举例来讲,有主机A须要对外提供FTP和WWW两种服务,若是没有端口号存在的话,这两种服务是没法区分的。

        实际上,当网络上某主机B须要访问A的FTP服务时,就要指定目的端口号为21;
        当须要访问A的WWW服务时,则须要将目的端口号设为80,这时A根据B访问的端口号,就能够区分B的两种不一样请求
    • 多个会话
      • 主机A须要同时下载网络上某FTP服务器B上的两个文件,那么A须要 与B同时创建两个会话,而这两个传输会话就是靠源端口号来区分的。在这种状况下若是没有源端口号的概念,那么A就没法区分B传回的数据到底是属于哪一个会话,属于哪一个文件
      • 通讯过程是,A使用本机的1025号端口请求B的21号端口上的文件1,同时又使用1026号端口请求文件2。对于返回的数据,发现是传回给1025号端口的,就认为是属于文件1;传回给1026号端口的,则认为是属于文件2。这就是端口号区分多个会话的做用。

在线编程——编写一个 css 层级选择器

根据一个给定的元素生成一个css 选择器,函数名为genCssSelector ,
点击某元素弹出该元素及其父元素,相似 querySelector

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Document</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <script language="javaScript"> // your code here var genCssSelector = function (e) { e = e || window.event var tar = e.target || e.srcElement var objArr = [] while (tar) { if (tar.id) { objArr.push('#' + tar.id) console.log('id') return objArr.reverse().join(' ') // 考虑 id 的惟一性,若是有 id,则中止查找 }else if (tar.className) { objArr.push('.' + tar.className.split(' ')[0]) // 考虑若是有多个 class }else { objArr.push(tar.nodeName.toLowerCase()) } tar = tar.parentNode } objArr.pop() return objArr.reverse().join(' ') } document.addEventListener('click', function (e) { //点击li时,返回:html body #page .content.main .refer ul li  console.log(genCssSelector(e)); }) </script> </head> <body> <div id="page"> <div class="main" id="main"> <div class="reference refer"> <ul> <li></li>  <li></li> 23333 </ul>  </div> </div>  </div> </body> </html>

获取 dom 元素

JS获取DOM元素的方法(8种)

  • getElementById

    只获取到一个元素,没有找到返回null
  • getElementsByName
  • getElementsByTagName
  • getElementsByClassName
  • document.documentElement

    获取html
  • document.body

    获取body
  • querySelector

    获取一个元素
  • querySelectorAll

    获取一组元素

获取子元素

  • childNodes
    dom.childNodes 返回一个nodeList(元素的全部子元素)
    • nodeType
      • 元素节点的nodeType属性值为1
      • 属性节点的nodeType属性值为2
      • 文本节点的nodeType属性值为3
    • nodeValue属性

      得到和改变文本节点的值
  • firstChild 第一个子元素
  • lastChild

获取父、兄

  • parentNode
  • nextSibling
  • previousSbiling

建立元素

  • createDocumentFragment

    建立一个dom片断
  • createElement

    建立一个具体的元素
  • createTextNode

    建立一个文本节点

增删改元素

  • appendChild
  • removeChild
  • replaceChild
  • insertBefore

如何用 JS 实现 JSON.parse

---eval

直接调用eval

var json = '{"a":"1", "b":2}'; var obj = eval("(" + json + ")"); // obj 就是 json 反序列化以后获得的对象

原理

JSON 脱胎于 JS,同时也是 JS 的子集,因此可以直接交给 eval 运行

  • 加上圆括号的目的是迫使eval函数在处理JavaScript代码的时候强制将括号内的表达式(expression)转化为对象,而不是做为语句(statement)来执行

    例如对象字面量{},如若不加外层的括号,那么eval会将大括号识别为JavaScript代码块的开始和结束标记,那么{}将会被认为是执行了一句空语句

缺点

  • XSS 漏洞

    如:参数 json 并不是真正的 JSON 数据,而是可执行的 JS 代码
  • 对参数 json 作校验,只有真正符合 JSON 格式,才能调用 eval

    // 1. 用 4 个正则表达式分为两个阶段解决(包容ie 和safari 的regexp 引擎) // 2. 将 json 反斜杠替换为 '@' (non-json字符) // 3. 用 ']' 替换全部简单标记 // 4. 删除全部跟随冒号,逗号或文本开始的方括号 // 5. 若是只剩下 '] , { }' 则是安全的 var rx_one = /^[\],:{}\s]*$/; var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; var rx_four = /(?:^|:|,)(?:\s*\[)+/g; if ( rx_one.test( json .replace(rx_two, "@") .replace(rx_three, "]") .replace(rx_four, "") ) ) { var obj = eval("(" +json + ")"); }
---递归

第一种 eval 的方法,至关于一古脑儿把 JSON 字符串塞进去。
其实咱们还能够手动逐个字符地扫描,而后进行判断,这就是第二种方法:递归

// 所谓递归,就是重复调用value 函数 value = function () { // Parse a JSON value. It could be an object, an array, a string, a number, // or a word. white(); // 根据当前字符是什么,咱们便能推导出后面应该接的是什么类型 switch (ch) { case "{": return object(); case "[": return array(); case "\"": return string(); case "-": return number(); default: return (ch >= "0" && ch <= "9") ? number() : word(); } }; // 调用核心的 next 函数,逐个读取字符 var next = function (c) { // If a c parameter is provided, verify that it matches the current character. if (c && c !== ch) { error("Expected '" + c + "' instead of '" + ch + "'"); } // Get the next character. When there are no more characters, // return the empty string. ch = text.charAt(at); at += 1; return ch; };
  • 对于常量token false true null 进行匹配,不匹配返回错误

以 {"a":"1", "b":2} 为例

程序大体逻辑是:启动 → 首次调用 value() → 发现是 { → 原来是对象,走 object() → 经过 string() 获得 key 值为 "a" → 读取到冒号,哦,后面多是对象、数组、布尔值等等,具体是什么,还得再次调用 value() 才知道 → ……

xml 解析

  • close tag

    使用一个 nodeStack 栈,在 opentag 时推入节点,close tag 时检查当前节点是否和栈尾节点是否匹配,匹配则推出末尾节点
  • comment

参考:JSON.parse 三种实现方式

react setState

  • setState 不保证同步

    • 可能会为了性能收益批量执行
    • setState() 不会马上改变 this.state,而是建立一个即将处理的 state 转变。在调用该方法以后访问 this.state可能会返回现有的值。
    解决方案
    • 使用回调函数

      setState 方法接收一个 function 做为回调函数。这个回掉函数会在 setState 完成之后直接调用,这样就能够获取最新的 state

      this.setState({ selection: value }, this.fireOnSelect)
    • setTimeout
      在 setState 使用 setTimeout 来让 setState 先完成之后再执行里面内容

      this.setState({ selection: value }); setTimeout(this.fireOnSelect, 0);
  • 形成没必要要的渲染
    • shouldComponentUpdate 解决

      setState() 将老是触发一次重绘,除非在 shouldComponentUpdate() 中实现了条件渲染逻辑
    • 和渲染无关的状态尽可能不要放在 state 中来管理

      一般 state 中只来管理和渲染有关的状态 ,从而保证 setState 改变的状态都是和渲染有关的状态。这样子就能够避免没必要要的重复渲染。其余和渲染无关的状态,能够直接以属性的形式保存在组件中,在须要的时候调用和改变,不会形成渲染。

  • 不能颇有效的管理全部的组件状态

参考:浅谈使用React.setState须要注意的三点

3. 百度

一面

jsonp cors

css

  • 分栏布局哪些方式
  • 详细说下flex

js & jq

  • 如何获取一个元素(js jq)
  • 异步事件是如何发送的,经常使用机制
  • 有哪些接收后台数据的方法
    • ajax
    • fetch
    • jsonp
    • websocket
    • SSE
  • event loop
  • 喜欢 es6 的哪些属性
  • 箭头函数与普通函数的不一样
  • 闭包
    • 简述
    • 应用
    • 在循环中如何用其余方式代替闭包

vue

  • 和 react 区别
  • 如何向服务器传递数据

操做系统

  • 线程 && 进程

计算机网络

  • 除了tcp 还用到哪些
  • http 与 tcp 分别属于第几层

数据结构

  • 有哪些线性存储空间(array 栈)

二面

  • 如何作一个 css 选择器

    见本文 阿里-在线编程
  • 给定一组dom 节点,和一个css样式表,找出不含有样式的dom

    面试官很耐心的解释了,仍是没听明白题目
  • 链表操做
    • 单向链表反转
  • 居中问题

    没有详细问,我分了两个方面分别回答
    • 水平居中
    • 垂直水平
    详细见 CSS 居中
  • get & post

    回答了大多数应聘者的 “标准答案”, 但经面试官指点,顿悟,大概这就叫高手吧

三面

  • 数字的存储

  • 前端的编码问题
    • 种类
    • 乱码如何处理
  • 有哪些协议,分别有什么做用
  • 关于实习经历,找实习的过程,项目中的二三事及解决方案
  • 大学中最有成就感的事
  • 闲聊了一下,关于保研等等,总共 四十分钟

分栏布局

---等分布局

 

 

  • float
    • 原理:增大父框的实际宽度后,使用CSS3属性box-sizing进行布局的辅助。

    • 用法:先将父框设置为 margin-left: -*px,再设置子框 float: left、width: 25%、padding-left、box-sizing: border-box

      .parent{ margin-left: -20px; } .column{ float: left; width: 25%; padding-left: 20px; box-sizing: border-box; /*包含padding区域 w+g*/ }
  • table
    • 原理:经过增长一个父框的修正框,增大其宽度,并将父框转换为 table,将子框转换为 tabel-cell 进行布局。

    • 用法:先将父框的修正框设置为 margin-left: -*px,再设置父框 display: table、width:100%、table-layout: fixed,设置子框 display: table-cell、padding-left

      .parent-fix{ margin-left: -20px; } .parent{ display: table; width:100%; table-layout: fixed; } .column{ display: table-cell; padding-left: 20px; }
  • flex
    • 原理:经过设置CSS3布局利器flex中的flex属性以达到等分布局。

    • 用法:将父框设置为display: flex,再设置子框flex: 1,最后设置子框与子框的间距margin-left。

如何获取一个class

// 直接获取---须要高版本浏览器支持 document.querySelectorAll("div.aa") // 相似属性选择器的写法 document.querySelectorAll("div[class='aa']") // 补充一下还能够not选择器 document.querySelectorAll(".aa:not(ul)") document.getElementsByClassName('cls') // jq $('.className')

Event Loop

js 单线程:
用途决定,操做 DOM

任务队列

排队缘由:计算量大的同步执行,IO设备(输入输出设备)很慢(好比Ajax操做从网络读取数据)异步。

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务能够执行了,该任务才会进入主线程执行

"任务队列"中的事件,除了IO设备的事件之外,还包括一些用户产生的事件(好比鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。

  • 全部同步任务都在主线程上执行,造成一个执行栈(execution context stack)。

  • 主线程以外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

  • 一旦"执行栈"中的全部同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,因而结束等待状态,进入执行栈,开始执行。

  • 主线程不断重复上面的第三步。

只要主线程空了,就去检查异步的任务队列,若是异步事件触发,则将其加到主线程的执行栈

深刻了解定时器

  • 零延迟 setTimeout(func, 0)

    零延迟并非意味着回调函数马上执行。它取决于主线程当前是否空闲与“任务队列”里其前面正在等待的任务。
  • 调用setTimeout()以后,该方法会返回一直数值ID,表示超时调用。这个超时调用ID是计划执行代码的惟一标识符,能够经过它来取消超时调用

  • 超时调用的代码都是在全局做用域中执行的,所以函数中this的值在非严格模式下指向window对象,严格模式下是undefined。

Event Loop

异步与event loop没有太直接的关系,准确的来说event loop 只是实现异步的一种机制
主任务 ——> micro task ——> 渲染视图 ——> macro task

主线程从"任务队列"中读取事件,这个过程是循环不断的,因此整个的这种运行机制又称为Event Loop(事件循环)

Javascript 中的事件循环是以任务为单位的,将不少个待执行的任务串联在一块儿就造成了队列 Task Queue,不少的队列前后按顺序执行任务就造成了 Event Loop

一个事件循环(EventLoop)中会有一个正在执行的任务(Task),而这个任务就是从 macrotask 队列中来的。
当这个 macrotask 执行结束后,全部可用的 microtask 将会在同一个事件循环中执行
当这些 microtask 执行结束后还能继续添加 microtask 一直到真个 microtask 队列执行结束。

  • 一个事件循环(event loop)会有一个或多个任务队列(task queue)

    task queue 就是 macrotask queue
  • 每个 event loop 都有一个 microtask queue
  • task queue == macrotask queue != microtask queue
  • 一个任务 task 能够放入 macrotask queue 也能够放入 microtask queue 中
  • 当一个 task 被放入队列 queue(macro或micro) 那这个 task 就能够被当即执行了

Micro Task

当咱们想以同步的方式来处理异步任务时候就用 microtask(好比咱们须要直接在某段代码后就去执行某个任务,就像Promise同样)

  • process.nextTick
  • promise
  • Object.observe
  • MutationObserver

Macro Task

  • setTimeout
  • setInterval
  • setImmediate
  • I/O

任务队列中,在每一次事件循环中,从 macrotask 队列开始执行,macrotask只会提取一个执行,而microtask会一直提取,直到microsoft队列为空为止。

也就是说若是某个microtask任务被推入到执行中,那么当主线程任务执行完成后,会循环调用该队列任务中的下一个任务来执行,直到该任务队列到最后一个任务为止。而事件循环每次只会入栈一个macrotask,主线程执行完成该任务后又会检查microtasks 队列并完成里面的全部任务后再执行macrotask的任务。

执行过程以下:

  • 主线程空闲时首先执行 micro
  • 以后从 macro 中提取一个 task 到主任务,完成后再次执行 micro queue(执行一个cycle)
  • 反复过程2, 每一个周期为一个事件循环

为啥要用 microtask?

  • micro 执行总在 macro 以前
  • micro 所有执行完毕后会更新 UI 和执行下一个macro
    根据HTML Standard,在每一个 task 运行完之后,UI 都会重渲染,那么在 microtask 中就完成数据更新,当前 task 结束就能够获得最新的 UI 了
// 验证 (function () { const $test = document.getElementById('test') let counter = 0 function func1() { $test.innerText = ++counter alert('func1') } function func2() { $test.innerText = ++counter alert('func2') } function func3() { $test.innerText = ++counter alert('func3') } function func4() { $test.innerText = ++counter alert('func4') } (function () { // main task func1() // macro task setTimeout(() => { func2() // micro task Promise.resolve().then(func4) }, 0); // macro task setTimeout(func1, 0); // micro task Promise.resolve().then(func3) // main task func4() })() // alert func1 // alert func4 // alert func3 // UI update ---> counter = 3 // alert func2 // alert func4 // UI update ---> counter = 5 // alert func1 // UI update ---> counter = 6 })()

接收后台资源的方法(除了Ajax)

  • Ajax
  • fetch
    返回一个Promise对象, 根据 Promise Api 的特性, fetch能够方便地使用then方法将各个处理逻辑串起来

    mode

    // fetch能够设置不一样的模式使得请求有效 fetch(url, {mode: 'cors'});
  • Jsonp

  • websocket
    服务器推送技术之一
    全双工
  • SSE(server-sent-events)
    单向通道(服务器 -> 浏览器)

异步编程经常使用方法

ES 6之前:

  • 回调函数
  • 事件监听(事件发布/订阅)
  • Promise对象

ES 6:

  • Generator函数(协程coroutine)

ES 7:

  • async和await

回调函数

通常是须要在一个耗时操做以后执行某个操做时能够使用回调函数

  • 定时器
  • 读取文件

问题:
在回调函数以外没法捕获到回调函数中的异常

var fs = require('fs'); try{ fs.readFile('not_exist_file', 'utf8', function(err, data){ console.log(data); }); } catch(e){ console.log("error caught: " + e); }

尝试读取一个不存在的文件,这固然会引起异常,可是最外层的try/catch语句却没法捕获这个异常。这是异步代码的执行机制致使的

为何异步代码回调函数中的异常没法被最外层的try/catch语句捕获?

异步调用通常分为两个阶段,提交请求和处理结果,这两个阶段之间有事件循环的调用,它们属于两个不一样的事件循环(tick),彼此没有关联。

异步调用通常以传入callback的方式来指定异步操做完成后要执行的动做。而异步调用本体和callback属于不一样的事件循环。

try/catch语句只能捕获当次事件循环的异常,对callback无能为力。

事件监听(订阅-发布)

典型的逻辑分离方式,对代码解耦颇有用处
把不变的部分封装在组件内部,供外部调用,须要自定义的部分暴露在外部处理。
从某种意义上说,事件的设计就是组件的接口设计。

//发布和订阅事件 var events = require('events'); var emitter = new events.EventEmitter(); emitter.on('event1', function(message){ console.log(message); }); emitter.emit('event1', "message for you");

Promise 对象

用同步操做的流程写法来表达异步操做,避免了层层嵌套的异步回调

  • Promise.prototype.then()

    //原生Primose顺序嵌套回调示例 var fs = require('fs') var read = function (filename){ var promise = new Promise(function(resolve, reject){ fs.readFile(filename, 'utf8', function(err, data){ if (err){ reject(err); } resolve(data); }) }); return promise; } read('./text1.txt') .then(function(data){ console.log(data); return read('./text2.txt'); // 返回了一个新的Promise实例 }) .then(function(data){ console.log(data); });
    Promise构造函数的参数是一个函数,在这个函数中咱们写异步操做的代码
    在异步操做的回调中,根据err变量来选择是执行resolve方法仍是reject方法
    • 通常来讲调用resolve方法的参数是异步操做获取到的数据(若是有的话),但还多是另外一个Promise对象,表示异步操做的结果有多是一个值
    • 也有多是另外一个异步操做,调用reject方法的参数是异步回调用的err参数

调用read函数时,实际上返回的是一个Promise对象,经过在这个Promise对象上调用then方法并传入resolve方法和reject方法来指定异步操做成功和失败后的操做。

  • Promise.prototype.catch()

    用于指定发生错误时的回调函数

    read('./text1.txt') .then(function(data){ console.log(data); return read('not_exist_file'); }) .then(function(data){ console.log(data); }) .catch(function(err){ console.log("error caught: " + err); }) .then(function(data){ console.log("completed"); })

    使用Promise对象的catch方法能够捕获异步调用链中callback的异常
    Promise对象的catch方法返回的也是一个Promise对象,所以,在catch方法后还能够继续写异步调用方法

  • Promise异步并发
    • Promise.all()
      • 将多个Promise实例,包装成一个新的Promise实例
        var p = Promise.all([p1,p2,p3]);
      • 接受一个数组做为参数,p一、p二、p3都是Promise对象实例
      • 只有p一、p二、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p一、p二、p3的返回值组成一个数组,传递给p的回调函数。
      • 只要p一、p二、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

        var promises = [1, 2].map(function(fileno){ return read('./text' + fileno + '.txt'); }); Promise.all(promises) .then(function(contents){ console.log(contents); }) .catch(function(err){ console.log("error caught: " + err); })
    • Promise.race()
      • 将多个Promise实例,包装成一个新的Promise实例
        var p = Promise.race([p1,p2,p3]);
      • p一、p二、p3只要有一个实例率先改变状态,p的状态就会跟着改变,那个率先改变的Promise实例的返回值,就传递给p的返回值。
      • 若是Promise.all方法和Promise.race方法的参数不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理
    • Promise.resolve()
      • 将现有对象转换成Promise对象
      var p = Promise.resolve('Hello'); p.then(function (s){ console.log(s) });
    • Promise.reject()
      • 返回一个新的Promise实例,该实例的状态为rejected。
      • Promise.reject 方法的参数reason,会被传递给实例的回调函数。
      var p = Promise.reject('出错了'); p.then(null, function (s){ console.log(s) });
  • Generator函数
    • 能够交出函数的执行权(暂停执行)
    • 整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器。异步操做须要暂停的地方,都用yield语句注明。

      function* gen(x){ var y = yield x + 2; return y; } var g = gen(1); var r1 = g.next(); // { value: 3, done: false } console.log(r1); var r2 = g.next() // { value: undefined, done: true } console.log(r2);

    **Generator函数的函数名前面有一个"*"**

    • 调用Generator函数,会返回一个内部指针(即遍历器)g,这是Generator函数和通常函数不一样的地方,调用它不会返回结果,而是一个指针对象。
    • 调用指针g的next方法,会移动内部指针,指向第一个遇到的yield语句
      • next方法的做用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)
      • value属性是yield语句后面表达式的值,表示当前阶段的值;
      • done属性是一个布尔值,表示Generator函数是否执行完毕,便是否还有下一个阶段
  • Thunk 函数
    Thunk 函数的含义和用法
  • ES 7中的async和await

  • fetch
    • 替代浏览器原生的XMLHttpRequest异步请求

来自深刻解析Javascript异步编程

get & post 剖析

入门回答

区别:

  • get
    • 参数包含在 URL 中
    • 大小有限制
    • 使用Request.QueryString 获取变量的值
    • 安全性差,直观显示
  • post
    • 经过 request body 传递参数
    • 经过 Request.Form 获取变量的值

当我满心充满着自信和喜悦时,仿佛看到了面试官眉头一皱,So?

“标准答案”
aspect GET POST
浏览器回退 无影响 回退会再次提交请求
地址标记 产生的 URL 地址能够被 Bookmark 提交地址不被标记
cache 该请求会被浏览器主动 cache 该请求不会被缓存
编码 只能进行url编码 支持多种编码方式
参数保留 请求参数会被完整保留在浏览器历史记录里 POST中的参数不会被保留
长度限制 有(浏览器限制,IE-2083个字符) 无(限制做用的是服务器的处理程序的处理能力)
参数类型 只接受ASCII字符 没有限制
参数传递 经过URL传递 放在Request body中
裸奔剖析

99%的人都理解错了HTTP中GET与POST的区别

前端编码

字符 表示 补充
二进制 0/1 八个二进制位能够组合出256种状态,这被称为一个字节(byte)
八进制 0~7  
十进制 0~9  
十六禁止 0~9 A~F  
  • 8byte = 1bit
  • 1024 字节 = 1k
  • 1024k = 1M
  • 1024M = 1G
  • 1024G = 1T
编码 特征 补充
二十一进制码(BCD码) 保留了十进制数的权,而数字则用二进制数码0和1的组合来表示 在须要高精度的计算中BCD编码比较经常使用(了解)
ASCII码 美国信息交换标准委员会制定的7位字符编码,用7位二进制码表示一个字符,第8 位用于肯定附加的128 个特殊符号字符、外来语字母和图形符号  
GB2312 为了保存非英文,使用127号以后的空位保存新的字母(一个8位的字节能够组合256种状态,ASCII只编到127号),一直编到最后一位255,并且不一样国家表示的符号也不同,也能够说GB2312是对ASCII的中文扩展 不够用,后来只要求只要第一个字节是大于127就固定表示这是一个汉字的开始,称之为GBK编码
GB18030 / DBCS 编码中又增长了几千个新的少数民族的字,GBK扩展成了GB18030统称它们叫作DBCS  
Unicode ISO(国际标准化组织)废弃了全部地区性编码方案,作了一套包括了地球上全部文化、符号以及字母的编码;ISO规定:必须用两个字节,16位来统一表示全部的字符,不管是半角的英文字母,仍是全角的汉字,它们都是统一的一个字符!也就是两个字节  
UTF-8 UTF-8 互联网上使用最广的一种 Unicode 的实现方式,每次以8个位为单位传输数据;UTF-16就是每次 16 个位 UTF-8 最大的一个特色,就是它是一种变长的编码方式,Unicode一个中文字符占 2 个字节,而UTF-8一个中文字符占3个字节,UTF-8是Unicode的实现方式之一

进制转换

------十进制转其余------- var a = 24; a.toString(2);//11000 a.toString(8);//30 a.toString(16);//18 ------其余转十进制------- var b=11000,c=30,d=18; console.log(parseInt(b, 2)); // 二进制转十进制 console.log(parseInt(c, 8)); // 八进制转十进制 console.log(parseInt(d, 16));// 十六进制转十进制

前端编码问题

在使用nodeJS编写前端工具时,对文本文件的操做比较多,这就涉及到了文件的编码问题,经常使用的文本编码有UTF8和GBK两种,而且UTF8文件还可能带有BOM(字节顺序标记),在读取不一样编码的文本文件时,须要将文件内容转换为JS使用的UTF8编码字符串后才能正常处理

  • 移除 BOM

    BOM用于标记一个文本文件使用Unicode编码,其自己是一个Unicode字符("\uFEFF"),位于文本文件头部以告诉其余编辑器以utf8来显示字符

    可是在网页上并不须要添加BOM头识别,由于网页上能够使用 head头 指定charset=utf8告诉浏览器用utf8来解释.可是你用window自动的编辑器,编辑,而后有显示在网页上这样就会显示出0xEF 0xBB 0xBF这3个字符。这样网页上就须要去除0xEF 0xBB 0xBF
    • 能够使用editplus 选择不带BOM的编码,这样就能够去除了
    • js 去除

      // 能够经过文件头的几个字节来判断文件是否包含BOM以及使用哪一种Unicode,若是读取文件的时候不去掉BOM // 假如咱们将几个JS文件合并成一个,若是文件中含有BOM字符,就会致使语法错误 // 因此咱们用 nodeJS读取文件是通常先去掉BOM var bin = fs.readFileSync(pathname);//经过node 中fs模块同步读文件内容 //判断文件头的字节 if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) { bin = bin.slice(3); }
  • GBK 转 UTF8

    NodeJS支持在读取文本文件时,或者在Buffer转换为字符串时指定文本编码,但GBK编码不在NodeJS自身支持范围内,通常咱们借助iconv-lite这个三方包来转换编码,首先使用npm下载这个第三方包,读取GBK文件函数以下:

    var iconv = require('iconv-lite'); function readGBKText(pathname) { var myFs = fs.readFileSync(pathname); return iconv.decode(myFs, 'gbk'); }

数据结构

经常使用的线性结构有:线性表,栈,队列,循环队列,数组
线性表中包括顺序表、链表等,其中:

  • 栈和队列只是属于逻辑上的概念,实际中不存在,仅仅是一种思想,一种理念;
  • 线性表则是在内存中数据的一种组织、存储的方式。

常见的排序算法及其时间复杂度

参考:十大经典排序算法总结(JavaScript描述)

其余

  • git 与 GitHub 有什么区别
  • git 的一些指令
  • Linux

计算机网络

**若有不足,欢迎交流,祝各位看官 offer 拿到手软 O(∩_∩)O**

 

看样子博主成功了哈。 恭喜。。。 我面了两次阿里都挂在电话面试了,第一次一年前推了个p6的,问了不少react的问题,挂了,第二次前几天, p7的(推简历的给我推了个p7,我崩溃) 其余的答的都还好主要问到react内部实现,redux connect方法的实现等

 

 

 

 

前端相关片断整理——持续更新

 

 

更好阅读 移步这里

1. ES6

  • 箭头函数
  • 字符串模板
  • generators(生成器)
  • async/await
  • 解构赋值
  • class
  • 引入module模块的概念

1.1. 箭头函数:

  • 函数内的this对象,是定义时所在的对象,不是使用时所在的对象
  • 不可当构造函数
  • 用rest代替argument
  • this指向通常可变,但在箭头函数中固定
  • 简单,单行,不会复用的函数建议使箭头函数
    复杂,行多,使用传统

1.2. promise

解决异步回调多层嵌套的问题

  • 是一个容器;
    包含某个将来结束的事件
  • 是一个对象:
    从它可获取异步操做的消息
  1. pending 进行中
  2. resolved 已完成
  3. rejected 已失败

特色

  • 状态不受外界影响,只有事件结果决定
  • 状态改变不会再变

缺点:

  • 没法取消promise,一旦创建当即执行,中途没法撤回
  • 无回掉函数的话,错误不反应到外部
  • pending时,状态没法得知

Promise.all

接收 Promise 数组为参数,将多个Promise实例,包装成一个新的Promise实例,全部 resolve ,返回全部值

在不一样的接口请求数据而后拼合成本身所需的数据,一般这些接口之间没有关联(例如不须要前一个接口的数据做为后一个接口的参数)

var p = Promise.all([p1, p2, p3]);

p的状态由p一、p二、p3决定,分红两种状况:

  • 只有p一、p二、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p一、p二、p3的返回值组成一个数组,传递给p的回调函数。
  • 只要p一、p二、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Promise.race
它一样接收一个数组,不一样的是只要该数组中的 Promise 对象的状态发生变化(不管是 resolve 仍是 reject)该方法都会返回

async/await

async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来

  • 是写异步代码的新方式,之前的方法有回调函数和Promise。
  • 是基于Promise实现的,它不能用于普通的回调函数。
  • 与Promise同样,是非阻塞的。
  • 使得异步代码看起来像同步代码,这正是它的魔力所在。

1.3. interator

是一种接口,为全部数据结构提供一种统一的访问机制,即for...of 循环

做用:

  • 一是为各类数据结构,提供一个统一的、简便的访问接口;
  • 二是使得数据结构的成员可以按某种次序排列;
  • 三是ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。

interator遍历过程:

  1. 建立一个只针对象,指向当前数据结构的起始位置(遍历器对象本质是指针对象)
  2. 调用指针对象的next方法

使用场合:

  • 解构赋值
  • 扩展运算符(...)
  • yield*

for...in

  • 为遍历对象设计,不适用数组
  • key
  • 以字符串做为键名
  • 遍历数字键以及手动添加的其余键
  • 可能会以任意顺序遍历键名

for...of

  • 语法简洁,无以上缺点
  • 循环value
  • 不一样用于foreach方法,能够与break,continue,return配合使用
  • 提供了遍历全部数据结构的统一操做接口,循环普通对象结合 bject.keys() 搭配使用
  • 可自动遍历generator函数生成的iterator对象
  • 除了遍历数组元素之外,还会遍历自定义属性

1.4. generator 函数

一种异步解决方案(一种封装了多个内部状态的状态机)

  • 返回的不是函数运行结果,而是指向内部状态的指针对象
  • 调用next方法,从中止地方开始执行,移向下一个状态

1.5. yield 与 return

类似:都能返回紧跟在语句后面那个表达式的值
区别:记忆功能,执行次数,返回值数量

1.6. 回调函数

JavaScript对异步编程的实现

1.7. ES6 Object.assign

将源对象(source)的全部可枚举属性,复制到目标对象(target)

Object.assign(target, source1, source2);
后面属性覆盖前面同名属性

  • 一个参数时,返回该参数
  • 参数不是对象,转成对象(undefined,null会报错),若为源对象位置,则跳过
  • 可用来操做数组,将数组视为对象
  • 浅拷贝非深拷贝(若源对象的有对象属性值,则拷贝的是该引用)

用途:

  • 为兑现添加属性/方法
  • 克隆对象
  • 合并对象
  • 为属性指定默认值

2. 通讯

2.1. JSONP

被包含在一个回调函数中的 json
核心是: 动态添加script标签调用服务器提供的js脚本

2.2. cors

使用自定义的http头部让浏览器与服务器进行沟通,肯定该请求是否成功
核心:由服务器发送一个响应标头

2.3. web安全

1) 将重要的cookie标记为http only
2) 只容许用户输入指望值
3) encode
4) 过滤或移除特殊标签
5) 过滤JavaScript事件标签

3. 架构

3.1. 模块化

原理: 将复杂系统分解为代码结构更合理,可维护性更高,可管理的模块
目的: 只需完成本身的业务代码
发展过程:

  1. commonjs
    模块为单独的文件,require,同步使用
    nodejs主要用于服务器进程,加载内容在本地磁盘
    异步状况:浏览器环境中须要从服务器加载模块,须要采用异步模式
  2. AMD(Asynchronous Module Definition)
    • 容许输出模块兼容commonjs规范
    • require([module], callback);
    • 模块加载与调用不一样步,浏览器不会发生假死
    • requirejs curljs
  3. CMD
    seajs推广中对模块定义的产出

CMD与AMD区别:

  • amd推崇依赖前置(定义模块时申明其依赖的模块),cmd推崇依赖就近(用时再require)
  • amd的api默认一当多,cmd推崇职责单一(amd中require分全局和局部)

requirejs 与 seajs 分析:

  1. 定位,requirejs想成为浏览器端的模块加载器,也想成为rhino/node等环境的模块加载器
    seajs专一web浏览器端,经过node扩展方式方便跑在node端
  2. 标准,requirejs醉醺amd规范,seajs遵循cmd,api不一样
  3. 理念,requirejs尝试让第三方类库修改自身来支持requirejs,seajs不强推,采用资助封装方式,已较成熟封装策略
  4. 质量,require<seajs
  5. 插件

更多了解:

3.2. react

用于构建用户界面的JavaScript库,主要用于构建ui,将普通的DOM以数据结构的形式展示出来

永远只须要关心数据总体,两次数据之间的UI如何变化,则彻底交给框架去作,使用React大大下降了逻辑复杂性
Virtual DOM并无彻底实现DOM,Virtual DOM最主要的仍是保留了Element之间的层次关系和一些基本属性

基于React进行开发时全部的DOM构造都是经过虚拟DOM进行,每当数据变化时,React都会从新构建整个DOM树,而后React将当前整个DOM树和上一次的DOM树进行对比,获得DOM结构的区别,而后仅仅将须要变化的部分进行实际的浏览器DOM更新

虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操做的仅仅是Diff部分,于是能达到提升性能的目的。这样,再也不须要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只须要关心在任意一个数据状态下,整个界面是如何Render的

设计特色:

  • 变换:react核心认为ui只是把数据经过映射关系变换成另外一种形式的数据——函数
  • 组合:将两个或多个不一样的抽象合并为一个
  • 组件化:推荐以组件的方式思考ui构成,将小组件经过组合或嵌套构成大组件

组件特征:

  • 可组合
  • 可重用
  • 可维护

jsx语法:
HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它容许 HTML 与 JavaScript 的混写

生命周期:
组件的生命周期分红三个状态:

  • Mounting:已插入真实 DOM
  • Updating:正在被从新渲染
  • Unmounting:已移出真实 DOM

React 为每一个状态都提供了两种处理函数,will 函数在进入状态以前调用,did 函数在进入状态以后调用,三种状态共计五种处理函数:

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate(object nextProps, object nextState)
  • componentDidUpdate(object prevProps, object prevState)
  • componentWillUnmount()

两种特殊状态的处理函数:

  • componentWillReceiveProps(object nextProps):已加载组件收到新的参数时调用
  • shouldComponentUpdate(object nextProps, object nextState):判断是否从新渲染时调用

3.3. angular

特性:

  • MVVM
  • 模块化
  • 自动化双向数据绑定
  • 语义化标签
  • 依赖注入

3.4. vue

  • 父-子
    props
  • 子-父
    on/emit
  • 其余
    使用空的vue实例做为中央事件总线

3.5. angular与react之对比

  • React 和 Angular 之间的巨大差别是 单向与双向绑定
  • React 和 Vue 都使用了虚拟 DOM —— 没必要在每一个元素每次变化时从新渲染整个巨大的table
  • 若是应用时常要处理大量的动态数据集,并以相对简便和高性能的方式对大型数据表进行显示和变动,因为双向数据绑定须要监听每个可变元素, 数据量变大就会带来显著的性能问题,React是至关不错的选择。可是React不像AngularJS那样包含完整的功能,举例来讲,React没有负责数据展示的控制器

3.6. 软件架构

模式之间不一样 主要是 M与V 的数据传递的流程不一样

3.6.1. mvc

  • View 传送指令到 Controller
  • Controller 完成业务逻辑后,要求 Model 改变状态
  • Model 将新的数据发送到 View,用户获得反馈

MVC 能够分红两种方式:

  • 经过 View 接受指令,传递给 Controller
  • 直接经过controller接受指令

3.6.2. MVP

  • 各部分之间的通讯,都是双向的。
  • View 与 Model 不发生联系,都经过 Presenter 传递。
  • View 很是薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter很是厚,全部逻辑都部署在那里。

3.6.3. MVVM

  • 用数据“绑定”的形式让数据更新的事件不须要开发人员手动去编写特殊用例,而是自动地双向同步。
  • 数据绑定能够认为是Observer模式或者是Publish/Subscribe模式,原理都是为了用一种统一的集中的方式实现频繁须要被实现的数据更新问题。
  • MVVM不只简化了业务与界面的依赖关系,还优化了数据频繁更新的解决方案

3.7. restful架构

Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。我对这个词组的翻译是"资源的表现层状态转化"。

4. js

4.1. js垃圾回收与内存管理

各大浏览器一般用采用的垃圾回收有两种方法:标记清除、引用计数

4.1.1. 垃圾回收

自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程当中使用的内存
垃圾收集器会按期(周期性)找出那些不在继续使用的变量,而后释放其内存

  • 标记清除
    1. 垃圾收集器在运行的时候会给存储在内存中的全部变量都加上标记
    2. 而后,它会去掉环境中的变量以及被环境中的变量引用的标记
    3. 而在此以后再被加上标记的变量将被视为准备删除的变量,缘由是环境中的变量已经没法访问到这些变量了
    4. 最后,垃圾收集器完成内存清除工做,销毁那些带标记的值,并回收他们所占用的内存空间
  • 引用计数
    跟踪记录每一个值被引用的次数
    当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,若是包含对这个值引用的变量又取得了另一个值,则这个值的引用次数就减1,释放那些引用次数为0的值所占的内存。

    function problem() { var objA = new Object(); var objB = new Object(); objA.someOtherObject = objB; objB.anotherObject = objA; }

    这个方式存在一个比较大的问题就是循环引用
    能够手动切断他们的循环引用

    myObj.element = null; element.someObject =null;

4.1.2. 减小JavaScript中的垃圾回收

  • 在初始化的时候新建对象,而后在后续过程当中尽可能多的重用这些建立好的对象。
  • 另外还有如下三种内存分配表达式(可能不像new关键字那么明显了):
    • {} (建立一个新对象)
    • [] (建立一个新数组)
    • function() {…} (建立一个新的方法,注意:新建方法也会致使垃圾收集!!)

4.1.3. 优化

  1. 对象object优化
    • 避免使用new/{}来新建对象
    • cr.wipe(obj)—遍历此对象的全部属性,并逐个删除,最终将对象清理为一个空对象
  2. 数组array优化

    arr = []; //将原数组变成一小片内存垃圾 arr.length = 0 //清空数组

4.2. 闭包

特色:

  • 函数
  • 能访问另一个函数做用域中的变量

ES 6以前,Javascript只有函数做用域的概念,没有块级做用域。即外部是访问不到函数做用域中的变量。

总结

  • 能够访问外部函数做用域中变量的函数
  • 被内部函数访问的外部函数的变量能够保存在外部函数做用域内而不被回收---这是核心,后面咱们遇到闭包都要想到,咱们要重点关注被闭包引用的这个变量

4.3. 做用域链

为何闭包就能访问外部函数的变量呢

Javascript中有一个执行环境(execution context)的概念,它定义了变量或函数有权访问的其它数据,决定了他们各自的行为。每一个执行环境都有一个与之关联的变量对象,环境中定义的全部变量和函数都保存在这个对象中

当访问一个变量时,解释器会首先在当前做用域查找标示符,若是没有找到,就去父做用域找,直到找到该变量的标示符或者再也不存在父做用域了,这就是做用域链。

做用域链的顶端是全局对象。对于全局环境中的代码,做用域链只包含一个元素:全局对象

做用域链和原型继承:
有点相似,但又有点小区别:

  • 若是去查找一个普通对象的属性时,在当前对象和其原型中都找不到时,会返回undefined
  • 查找的属性在做用域链中不存在的话就会抛出ReferenceError

更多了解:

闭包的运用

  • 匿名自执行函数
    有的函数只须要执行一次,其内部变量无需维护,执行后释放变量
  • 实现封装/模块化代码
    变量做用域为函数内部,外部没法访问
  • 实现面向对象中的对象
    这样不一样的对象(类的实例)拥有独立的成员及状态,互不干涉

优势:

  • 可让一个变量常驻内存 (若是用的多了就成了缺点
  • 避免全局变量的污染
  • 私有化变量

缺点:

  • 由于闭包会携带包含它的函数的做用域,所以会比其余函数占用更多的内存
  • 引发内存泄露

4.4. 事件委托和this

4.4.1. 事件委托

由其它元素而非事件目标元素来响应事件产生的行为的思想。如用ul元素来处理其子元素li的事件。

事件冒泡: stopPropagation、stopImmediatePropagation、preventDefault

订阅发布
优势:减小监听器数量,改善性能
缺点:父容器的侦听器可能须要检查事件来选择正确的操做

4.4.2. this

this 关键字在JavaScript中的一种经常使用方法是指代码当前上下文

  • 默认指向全局对象,其一般是window
  • this老是表明它的直接调用者(js的this是执行上下文), 例如 obj.func ,那么func中的this就是obj
  • 在严格模式下,没有直接调用者的函数中的this是 undefined
  • 使用call,apply,bind绑定的,this指的是 绑定的对象

在异步编程中,this能够很容易改变过程当中一个功能操做。保持处理程序上下文的一个小技巧是将其设置到闭包内的一个变量,当在上下文改变的地方调用一个函数时,如setTimeout,你仍然能够经过该变量引用须要的对象。

箭头函数中的this

  • 箭头函数没有本身的this, 它的this是继承而来
  • 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window
  • 箭头函数能够方便地让咱们在 setTimeout ,setInterval中方便的使用this

持续更新,欢迎交流~

 

 
 
 
 
 

前端基础精简总结

 

 

更好阅读 移步这里

1. JavaScript

1.1. 基础语法

包括:变量声明、数据类型、函数、控制语句、内置对象等

1.1.1. 变量声明

ES5:

var //普通变量 function //函数

ES6新增:

let //普通变量 const //常量 -- 声明的同时初始化,不可从新赋值,不能重复声明 import //模块 class //类
1.1.2. 数据类型
-- 类型介绍

ES5:
String、Number、Boolean、Null、Undefined、Object
ES6增:
Symbol
其中,object为引用,其余为基本类型

  • 基本类型
    占据空间固定,是简单的数据段,将其存储在(stack)中(按值访问) 便于提高变量查询速度
    为了便于操做这类数据,ECMAScript提供了 3 个 基本包装类型 :Boolean、Number 和 String
    • 基本包装类型
      一种特殊的引用类型,每当读取一个基本类型值的时候,JS内部就会建立一个对应的包装对象,从而能够调用一些方法来操做这些数据
  • 引用类型
    • 因为其值的大小会改变,因此不能将其存放在栈中,不然会下降变量查询速度
    • 将其存储在(heap)中,存储在变量处的值是一个指针,指向存储对象的内存处(按址访问)
    • 能够为其添加、改变和删除属性和方法;但基本类型不能够添加属性和方法
-- 类型判断
  • 原始类型
    • typeof
  • 引用类型
    • isinstanceof -- 判断已知对象
    • constructor -- 根据对象的constructor判断
      constructor原本是原型对象上的属性,指向构造函数。可是根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,所以,实例对象也是能使用constructor属性的
    • Object.prototype.toString.call
    • $.type() -- 万能判断
    var a = new Array(); console.log(a instanceof Array) // a是否Array的实例 true console.log(a.constructor == Array) // a实例所对应的构造函数是否为Array true // constructor属性是能够被修改的,会致使检测出的结果不正确 function Dog(){ } function Cat(){ } Cat.prototype = new Dog(); var m= new Cat(); console.log(m.constructor==Cat); // false console.log(John.constructor==Person); // true // instanceof 对于直接或间接引用都是true console.log(m instanceof Cat); // true console.log(John instanceof Person); // true //Object.prototype.toString.call function a() { }; var toString = Object.prototype.toString; console.log(toString.call(new Date) === '[object Date]'); //true console.log(toString.call(new String) ==='[object String]');//true console.log(toString.call(a) ==='[object Function]'); //true //$.type jQuery.type( undefined ) === "undefined" // true jQuery.type() === "undefined" // true jQuery.type( null ) === "null" // true jQuery.type( true ) === "boolean" // true

1.1.3. 函数

  • 普通函数 -- 直接调用
  • 构造函数 -- new 建立对象
  • 对象方法 -- 对象调用

1.1.4. 内置对象

  • window
    • 全局对象,主要描述浏览器窗口相关的属性和状态
  • Date
  • Array
  • JSON
    • 主要用于对象的序列化和反序列化
    • 实现对象的深拷贝
  • RegExp
-- 浅复制与深拷贝
  1. 浅复制
    对对象地址的复制,并无开辟新的栈,复制的结果是两个对象指向同一个地址,修改其中一个对象的属性,则另外一个对象的属性也会改变
  2. 深拷贝
    开辟新的栈,两个对象对应两个不一样的地址,修改一个对象的属性,不会改变另外一个对象的属性

方法

  1. 递归

    var china = { nation : '中国', birthplaces:['北京','上海','广州'], skincolr :'yellow', friends:['sk','ls'] } //深复制,要想达到深复制就须要用递归 function deepCopy(o,c){ var c = c || {} for(var i in o){ if(typeof o[i] === 'object'){ //要考虑深复制问题了 if(o[i].constructor === Array){ //这是数组 c[i] =[] }else{ //这是对象 c[i] = {} } deepCopy(o[i],c[i]) }else{ c[i] = o[i] } } return c } var result = {name:'result'} result = deepCopy(china,result) console.dir(result)

  2. JSON

    var test ={ name:{ xing:{ first:'张', second:'李' }, ming:'老头' }, age :40, friend :['隔壁老王','宋经纪','同事'] } var result = JSON.parse(JSON.stringify(test)) result.age = 30 result.name.xing.first = '往' result.friend.push('fdagldf;ghad') console.dir(test) console.dir(result)

1.2. 函数原型链

JS是一种基于对象的语言,但在ES6 以前是不支持继承的,为了具有继承的能力,Javascript 在 函数对象上创建了原型对象prototype,并以函数对象为主线,从上至下,在JS内部构建了一条 原型链

Object 是全部对象的祖宗, 任何对象所创建的原型链最终都指向了Object

简单来讲:
创建了变量查找机制,当访问一个对象的属性时,先查找对象自己是否存在,若是不存在就去该对象所在的原型连上去找,直到Object对象为止,若是都没有找到该属性才会返回undefined。所以,咱们能够经过原型链来实现JS继承

1.3. 函数做用域

变量在声明它们的函数体以及这个函数体嵌套的任意函数体

JS中没有块级做用域,只有函数做用域
致使JS中出现了变量提高的问题
—— 将变量声明提高到它所在做用域的最开始的部分
为了解决变量提高带来的反作用,ES6新增了let 命令来声明变量,let 所声明的变量只在 let 命令所在的代码块内有效,因此不存在变量提高问题

1.4. this 指针

this 指针存在于函数中,用以标识函数运行时所处的上下文

  • 普通函数
    始终指向全局对象window
  • 构造函数
    指向新建立的对象
  • 方法
    指向调用该方法的对象
  • call、apply 和 bind
    方法来改变函数的 this 指向,其中,call 和 apply 主动执行函数,bind通常在事件回调中使用, call 和 apply的区别只是参数的传递方式不一样

1.5. new 操做符

函数的建立有三种方式,即 显式声明、匿名定义 和 new Function()

JS将新对象的原型链指向了构造函数的原型对象,因而就在新对象和函数对象之间创建了一条原型链,经过新对象能够访问到函数对象原型prototype中的方法和属性

1.6. 闭包

具备独立做用域的静态执行环境

和函数做用域不一样的是:

  • 闭包的做用域
    静态的,能够永久保存局部资源
  • 函数做用域
    只存在于运行时,函数执行结束后当即销毁

所以,闭包能够造成一个独立的执行过程

1.7. 单线程和异步

JavaScript

  • 单线程语言,在浏览器中,当JS代码被加载时,浏览器会为其分配一个主线程来执行任务(函数)
    主线程会造成一个全局执行环境,执行环境在栈中采用后进先出(LIFO)的顺序来执行代码块,以保证全部的函数能按照正确的顺序被执行
  • 执行环境中维护了一个异步队列(也叫工做线程),并将这些耗时任务放入队列中进行等待
    • 如ajax请求、定时器、事件等
    • 这些任务的执行时机并不肯定,只有当主线程的任务执行完成之后,主线程才会去检查异步队列中的任务是否须要开始执行。
    • JS中的 setTimeout 和 setInterval 就是典型的异步操做,它们会被放入异步队列中等待,即便 setTimeout(0)也不会被当即执行,须要等到当前同步任务结束后才会被执行。

1.8. 异步通讯

浏览器专门用来和服务器进行交互的异步通信技术

1.8.1. Ajax
  • Ajax是浏览器专门用来和服务器进行交互的异步通信技术
  • 其核心对象是XMLHttpRequest,经过该对象能够建立一个Ajax请求
  • Ajax请求是一个耗时的异步操做,当请求发出之后,Ajax提供了两个状态位来描述请求在不一样阶段的状态,这两个状态位分别是
    • readyState
    • status
      readyState 经过5个状态码来描述一个请求的5个阶段:
      0 - 请求未发送,初始化阶段
      1 - 请求发送中,服务器还未收到请求
      2 - 请求发送成功,服务器已收到请求
      3 - 服务器处理完成,开始响应请求,传输数据
      4 - 客户端收到请求,并完成了数据下载,生成了响应对象
      status
      • 1xx(临时响应)表示临时响应并须要请求者继续执行操做的状态码。
        • 2xx(成功)表示成功处理了请求的状态码。
        • 200(成功):服务器已成功处理了请求。一般,这表示服务器提供了请求的网页。
      • 3xx(重定向)要完成请求,须要进一步操做。
        • 301(永久移动):请求的网页已永久移动到新位置。
        • 302(临时移动):服务器目前从不一样位置的网页响应请求,但请求者应继续使用原有位置来响应之后的请求。
        • 304(未修改):自从上次请求后,请求的网页未修改过。
      • 4xx(请求错误)这些状态码表示请求可能出错,妨碍了服务器的处理。
        • 400(错误请求):服务器不理解请求的语法。
        • 404(未找到):服务器找不到请求的网页。
      • 5xx(服务器错误)这些状态码表示服务器在处理请求时发生内部错误。
        • 500(服务器内部错误):服务器遇到错误,没法完成请求。
        • 503(服务不可用):服务器目前没法使用(因为超载或停机维护)

常见问题:

  • timeout 只会影响readyState,而不会影响status,由于超时只会中断数据传输,但不会影响服务器的处理结果。 若是 timeout 设置的不合理,就会致使响应码status 是200,但 response里却没有数据,这种状况就是服务器正确响应了请求,但数据的下载被超时中断了。

HTTP 相关请见:

只容许请求和当前地址同域的服务器资源。但不限制脚本和标签发送跨域请求,好比script 和 img 标签,所以能够利用脚本跨域能力来实现跨域请求,即JSONP 的原理。

JSONP虽然能够解决跨域问题,但只能是get请求,而且没有有效的错误捕获机制
为了解决这个问题,XMLHttpRequest Level2 提出了CORS 模型,即 跨域资源共享, 它不是一个新的API,而是一个标准规范,当浏览器发现该请求须要跨域时,就会自动在头信息中添加一个 Origin字段,用以说明本次请求来自哪一个源。服务器根据这个值,决定是否赞成此次请求。

随着移动端的快速发展,Web技术的应用场景正在变得愈来愈复杂, 关注点分离 原则在系统设计层面就显得愈来愈重要,而XMLHttpRequest 是 Ajax 最古老的一个接口,于是不太符合现代化的系统设计理念。所以,浏览器提供了一个新的 Ajax 接口,即 Fetch API ,Fetch API 是基于Promise 思想设计的,更符合关注点分离原则。

更多请见:

1.9. 模块化

模块加载方案,最主要有 CMD 和 AMD 两种,分别以commonjs 和 requirejs为表明

ES6 在语言标准的层面上,实现了模块化编程,其设计思想是,尽可能静态化,使得编译时就能肯定模块的依赖关系,即编译时加载

CMD和AMD是在运行时肯定依赖关系,即运行时加载

详情:
AMD && CMD

ES6 模块化

每个ES6模块都是一个包含JS代码的文件,模块本质上就是一段脚本,而不是用module关键字定义一个模块,可是模块与脚本仍是有两点区别:

  • 在ES6模块中,不管你是否加入“use strict;”语句,默认状况下模块都是在严格模式下运行。
  • 在模块中你能够使用import和export关键字。

默认状况下,你在模块中的全部声明相对于模块而言都是寄存在本地的。若是你但愿公开在模块中声明的内容,并让其它模块加以使用,你必定要导出这些功能。想要导出模块的功能有不少方法,其中最简单的方式是添加export关键字,能够导出全部的最外层函数、类以及var、let或const声明的变量。

es6中 代码就是模块,不是一段脚本,因此全部的声明都被限定在模块的做用域中,对全部脚本和模块全局不可见。你须要作的是将组成模块公共API的声明所有导出。

webpack
在编译时计算全部依赖并将全部模块打包成一个文件,经过网络一次传输全部模块
减小加载模块时的网络往返时间

深刻浅出ES6(模块):http://www.infoq.com/cn/articles/es6-in-depth-modules

1.10. Node.js

一个基于 Chrome V8 引擎的 JavaScript运行环境

Node.js在服务端的优点是,它采用单线程和异步I/O模型,实现了一个高并发、高性能的运行时环境。相比传统的多线程模型,Node.js实现简单,而且能够减小资源开销

1.11. ES6

目标是让JS可以方便的开发企业级大型应用程序

变化

  • 新增 let、const 命令 来声明变量
    和var 相比,let声明的变量不存在变量提高问题,但没有改变JS弱类型的特色,依然能够接受任意类型变量的声明;const
    声明的变量不容许在后续逻辑中改变,提升了JS语法的严谨性。
  • 新增解构赋值、rest语法、箭头函数
    这些都是为了让代码看起来更简洁,而包装的语法糖。
  • 新增模块化
    这是JS走向规范比较重要的一步,让前端更方便的实现工程化。
  • 新增类和继承的概念
    配合模块化,JS也能够实现高复用、高扩展的系统架构。
  • 新增模板字符串功能
    高效简洁,结束拼接字符串的时代。
  • 新增Promise对象
    解决异步回调多层嵌套的问题
    使得本来的多层级的嵌套代码,变成了链式调用 让代码更清晰,减小嵌套数

promise

  • 容器:里面保存着某个将来才会结束的事件(一般是一个异步操做)的结果
  • 对象:从它能够获取异步操做的消息

特色

  • 对象的状态不受外界影响
    Promise对象表明一个异步操做,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操做的结果,能够决定当前是哪种状态,任何其余操做都没法改变这个状态。
  • 一旦状态改变,就不会再变,任什么时候候均可以获得这个结果
    Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。

缺点

  • 没法取消Promise
    一旦新建它就会当即执行,没法中途取消
  • 若是不设置回调函数,Promise内部抛出的错误,不会反应到外部
  • 当处于Pending状态时,没法得知目前进展到哪个阶段(刚刚开始仍是即将完成)

更多:

2. CSS

2.1. CSS 选择器

  • 类选择器
  • 标签选择器
  • ID选择器
  • 后代选择器
  • 群组选择器
  • 伪类选择器(before/after)
  • 兄弟选择器(+~)
  • 属性选择器等等

2.2. 盒子模型

  • 块级盒子(block)
  • 行内盒子(inline-block)

相关属性
margin、border、padding、content

注意

  • 只有普通文档流中块级盒子的垂直外边距才会发生合并,而行内盒子、浮动盒子或绝对定位之间的外边距不会合并
    根据规范,一个盒子若是没有上补白(padding-top)和上边框(border-top),那么这个盒子的上边距会和其内部文档流中的第一个子元素的上边距重叠
    为父元素增长一个border-top或者padding-top便可解决这个问题
  • box-sizing 属性的设置会影响盒子width和height的计算

更多:

2.3. 浮动布局

设置元素的 float 属性,能使该元素脱离普通文档流
若是子元素所有设置为浮动,则父元素是塌陷的

  • 清除浮动
    clear:both,
  • BFC
    浮动元素的父元素 + overflow:hidden 样式
  • 行内盒子(inline-block)
  • table也能够实现一样的效果。

2.4. 定位布局

脱离文档流:position 值为 relative/absolute/fixed

  • relative
    相对定位,它以本身原来的位置进行偏移,偏移后,原来的空间不会被其余元素占用
  • absolute
    绝对定位,它以离本身最近的定位父容器做为参照进行偏移
    经常使用的方式就是设置父容器的poistion:relative
  • fixed
    固定定位,以浏览器窗口为参照物
    PC网页底部悬停的banner通常均可以经过fixed定位来实现,但fixed属性在移动端有兼容性问题,所以不推荐使用,可替代的方案是:绝对定位+内部滚动。

更多:

2.5. 弹性布局

即Flex布局,定义了flex的容器一个可伸缩容器

  • 容器自己会根据容器中的元素动态设置自身大小
  • 当Flex容器被应用一个大小时(width和height),将会自动调整容器中的元素适应新大小
  • Flex容器也能够设置伸缩比例和固定宽度,还能够设置容器中元素的排列方向(横向和纵向)和是否支持元素的自动换行
    容器的属性
    • flex-direction属性
    • flex-wrap属性
    • flex-flow
    • justify-content属性
    • align-items属性
    • align-content属性
      项目的属性
    • order属性
    • flex-grow属性
    • flex-shrink属性
    • flex-basis属性
    • flex属性
    • align-self属性
  • 注意,设为Flex布局之后,子元素的float、clear和vertical-align属性将失效。

更多: Flex 布局

2.6. CSS3 动画

  • transition
    让元素的CSS属性值的变化在一段时间内平滑的过渡
    CSS3引入了transfrom属性,它能够经过对元素进行 平移(translate)、旋转(rotate)、放大缩小(scale)、倾斜(skew)
    等操做,来实现2D和3D变换效果
    transiton 还有一个结束事件 transitionEnd,该事件是在CSS完成过渡后触发,若是过渡在完成以前被移除,则不会触发transitionEnd
  • animation
    须要设置一个@keyframes,来定义元素以哪一种形式进行变换
    而后再经过动画函数让这种变换平滑的进行,从而达到动画效果
    • 动画能够被设置为永久循环演示
    • animation-play-state:paused能够暂停动画
    • animation-fill-mode:forwards 可让动画完成后定格在最后一帧
    • 能够经过JS监听animation的开始、结束和重复播放时的状态,分别对应三个事件,即 animationStart、animationEnd、animationIteration
      注意,当播放次数设置为1时,不会触发 animationIteration

对比

  • animation 设置动画效果更灵活更丰富
  • transition 只能经过主动改变元素的css值才能触发动画效果,而animation一旦被应用,就开始执行动画

2.7. BFC

BFC---Block Formatting Context

是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素

好比:内部滚动就是一个BFC,当一个父容器的overflow-y设置为auto时,而且子容器的长度大于父容器时,就会出现内部滚动,不管内部的元素怎么滚动,都不会影响父容器之外的布局,这个父容器的渲染区域就叫BFC。

特色

  • 盒子们自所在的containing block顶部一个接一个垂直排列
  • 水平方向上撑满整个宽度(除非内部盒子本身创建了新的BFC)
  • 两个相邻的BFC之间的距离由margin决定
  • 在同一个BFC内部,两个垂直方向相邻的块级元素的margin会发生“塌陷”

触发BFC
根元素或其它包含它的元素

  • float的值不为none
  • overflow的值不为visible
  • display的值为inline-block、table-cell、table-caption
  • position的值为absolute或fixed
  • flex boxes (元素的display: flex或inline-flex)

应用

  • 清除内部浮动
    对子元素设置浮动后,父元素会发生高度塌陷,也就是父元素的高度变为0。解决这个问题,只须要把把父元素变成一个BFC就好了。经常使用的办法是给父元素设置overflow:hidden
  • 垂直margin合并
    属于同一个BFC的两个相邻元素的margin会发生重叠 —— 建立 BFC
  • 建立自适应两栏布局 —— 解决侵占浮动元素的问题

参考: CSS: 潜藏着的BFC

2.8. Sprite,Iconfont,@font-face

  • Sprite图
    为了减小http请求的次数,通常会将经常使用的小图标排到一个大图中,页面加载时只需请求一次网络,在css中经过设置background-position来控制显示所须要的小图标
  • Iconfont
    即字体图标,就是将经常使用的图标转化为字体资源存在文件中,经过在CSS中引用该字体文件,而后能够直接用控制字体的css属性来设置图标的样式
    字体图标的好处是节省网络请求、其大小不受屏幕分辨率的影响,而且能够任意修改图标的颜色
  • @font-face
    是CSS3中的一个模块
    经过@font-face能够定义一种全新的字体,经过css属性font-family来使用这个字体

3. HTML

3.1. BOM

Browser Object Model 浏览器对象模型

当一个浏览器页面初始化时,会在内存建立一个全局的对象,用以描述当前窗口的属性和状态,这个全局对象被称为浏览器对象模型,即BOM

BOM的核心对象就是window,window对象也是BOM的顶级对象,其中包含了浏览器的 6个核心模块:

  • document
    • 即文档对象,渲染引擎在解析HTML代码时,会为每个元素生成对应的DOM对象,因为元素之间有层级关系,所以整个HTML代码解析完之后,会生成一个由不一样节点组成的树形结构,俗称DOM树
    • document用于描述DOM树的状态和属性,并提供了不少操做DOM的API。
  • frames
    • HTML 子框架,即在浏览器里嵌入另外一个窗口
    • 父框架和子框架拥有独立的做用域和上下文。
  • history
    • 以栈(FIFO)的形式保存着页面被访问的历史记录
    • 页面前进即入栈,页面返回即出栈。
  • location
    • 提供了当前窗口中加载的文档相关信息以及一些导航功能
  • navigator
    • 用来描述浏览器自己,包括浏览器的名称、版本、语言、系统平台、用户特性字符串等信息
  • screen
    • 提供了浏览器显示屏幕的相关属性,好比显示屏幕的宽度和高度,可用宽度和高度。

3.2. DOM 系统

Document Object Model 文档对象模型,是全部浏览器公共遵照的标准

  • DOM将HTML和XML文档映射成一个由不一样节点组成的树型结构,俗称DOM树
  • 其核心对象是document,用于描述DOM树的状态和属性,并提供对应的DOM操做API

3.3. 事件系统

事件是用户与页面交互的基础,到目前为止,DOM事件从PC端的 鼠标事件(mouse) 发展到了 移动端的 触摸事件(touch) 和
手势事件(guesture),touch事件描述了手指在屏幕操做的每个细节,guesture 则是描述多手指操做时更为复杂的状况

总结以下:

  • 第一根手指放下,触发 touchstart,除此以外什么都不会发生
  • 手指滑动时,触发touchmove
  • 第二根手指放下,触发 gesturestart
  • 触发第二根手指的 touchstart
  • 当即触发 gesturechange
  • 任意手指移动,持续触发 gesturechange
  • 第二根手指弹起时,触发 gestureend,之后将不会再触发 gesturechange
  • 触发第二根手指的 touchend
  • 触发touchstart (多根手指在屏幕上,提起一根,会刷新一次全局touch)
  • 弹起第一根手指,触发 touchend

DOM2.0 模型将事件处理流程分为三个阶段,即 事件捕获阶段 、 事件处理阶段 、 事件冒泡阶段

  • 事件捕获
    当用户触发点击事件后,顶层对象document 就会发出一个事件流,从最外层的DOM节点向目标元素节点传递,最终到达目标元素。
  • 事件处理
    当到达目标元素以后,执行目标元素绑定的处理函数。若是没有绑定监听函数,则不作任何处理。
  • 事件冒泡
    事件流从目标元素开始,向最外层DOM节点传递,途中若是有节点绑定了事件处理函数,这些函数就会被执行。

利用事件冒泡原理能够实现 事件委托

所谓事件委托,就是在父元素上添加事件监听器,用以监听和处理子元素的事件,避免重复为子元素绑定相同的事件

  • 方式
    当目标元素的事件被触发之后,这个事件就从目标元素开始,向最外层元素传递,最终冒泡到父元素上,父元素再经过event.target获取到这个目标元素
  • 好处
    父元素只需绑定一个事件监听,就能够对全部子元素的事件进行处理了,从而减小了没必要要的事件绑定,对页面性能有必定的提高。

更多: 事件委托和 this

3.4. HTML 解析过程

浏览器加载 html 文件之后,渲染引擎会从上往下,一步步来解析HTML标签

过程以下:

  • 请求服务器返回HTML文件
    用户输入网址,浏览器向服务器发出请求,服务器返回html文件;
  • 生成dom 树
    渲染引擎开始解析 html 标签,并将标签转化为DOM节点,生成 DOM树;
  • css文件请求
    若是head 标签中引用了外部css文件,则发出css文件请求,服务器返回该文件,该过程会阻塞后面的解析;
  • js 请求
    若是引用了外部 js 文件,则发出 js 文件请求,服务器返回后当即执行该脚本,这个过程也会阻塞html的解析;
  • 生成渲染树
    引擎开始解析 body 里面的内容,若是标签里引用了css 样式,就须要解析刚才下载好的css文件,而后用css来设置标签的样式属性,并生成渲染树;
  • 下载图片资源
    若是 body 中的 img 标签引用了图片资源,则当即向服务器发出请求,此时引擎不会等待图片下载完毕,而是继续解析后面的标签;
  • 从新渲染
    • 服务器返回图片文件,因为图片须要占用必定的空间,会影响到后面元素的排版,所以引擎须要从新渲染这部份内容;
    • 若是此时 js 脚本中运行了 style.display="none",布局被改变,引擎也须要从新渲染这部分代码;
  • 直到 html 结束标签为止,页面解析完毕。

3.5. 重绘 和 回流

  • 回流
    当渲染树中的一部分(或所有)由于元素的规模尺寸,布局,隐藏等改变而须要从新构建
    如上面的img文件加载完成后就会引发回流,每一个页面至少须要一次回流,就是在页面第一次加载的时候
  • 重绘
    当渲染树中的一些元素须要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,好比 background-color

从上面能够看出,回流必将引发重绘,而重绘不必定会引发回流

会引发重绘和回流的操做以下:

  • 添加、删除元素(回流+重绘)
  • 隐藏元素,display:none(回流+重绘),visibility:hidden(只重绘,不回流)
  • 移动元素,好比改变top,left的值,或者移动元素到另一个父元素中。(重绘+回流)
  • 对style的操做(对不一样的属性操做,影响不同)
  • 还有一种是用户的操做,好比改变浏览器大小,改变浏览器的字体大小等(回流+重绘)
  • 另外,transform操做不会引发重绘和回流,是一种高效率的渲染。这是由于transform属于合成属性,对合成属性进行 transition/animation 动画时将会建立一个合成层,这使得动画元素在一个独立的层中进行渲染,当元素的内容没有发生改变,就不必进行重绘,浏览器会经过从新复合来建立动画帧。

3.6. 本地存储

避免取回数据前页面空白,减小请求服务器次数

  • cookie
    • 本地存储最原始的方式
      cookie 是存放在本地浏览器的一段文本,数据以键值对的形式保存,能够设置过时时间。
    • 不适合大量数据的存储
      由于每请求一次页面,cookie 都会发送给服务器,这使得 cookie速度很慢并且效率也不高。所以cookie的大小被限制为4k左右(不一样浏览器可能不一样,分HOST)
  • html5 提供了两种在客户端存储数据的新方法:
    • localStorage
      永久存储
    • sessionStorage
      存储期限仅限于浏览器会话(session),即当浏览器窗口关闭后,sessionStorage中的数据被清除
    都是以key/value的形式来存储数据
    localStorage的存储空间大约5M左右(不一样浏览器可能不一样,分 HOST),这个至关于一个5M大小的前端数据库,相比于cookie,能够节约带宽,但localStorage在浏览器隐私模式下是不可读取的,当存储数据超过了 localStorage 的存储空间后会抛出异常。
    此外,H5还提供了逆天的 websql 和 indexedDB,容许前端以关系型数据库的方式来存储本地数据

cookie做用是与服务器交互,做为HTTP规范的一部分,web storage仅仅为本地存储而生

更多:经常使用的 web 客户端存储

3.7. 浏览器缓存机制

浏览器缓存机制是指经过 HTTP 协议头里的 Cache-Control (或 Expires) 和 Last-Modified (或 Etag) 等字段来控制文件缓存的机制。

  • Cache-Control
    • 用于控制文件在本地缓存有效时长
    • 好比服务器回包:Cache-Control:max-age=600
      表示文件在本地应该缓存,且有效时长是600秒 (从发出请求算起)。在接下来600秒内,若是有请求这个资源,浏览器不会发出 HTTP请求,而是直接使用本地缓存的文件。
  • Last-Modified
    • 标识文件在服务器上的最新更新时间
      下次请求时,若是文件缓存过时,浏览器经过 If-Modified-Since 字段带上这个时间,发送给服务器,由服务器比较时间戳来判断文件是否有修改。若是没有修改,服务器返回304告诉浏览器继续使用缓存;若是有修改,则返回200,同时返回最新的文件。
  • Cache-Control 一般与 Last-Modified 一块儿使用
    一个用于控制缓存有效时间,一个在缓存失效后,向服务查询是否有更新。
    • Cache-Control 还有一个同功能的字段:Expires。Expires 的值为一个绝对的时间点
      如:Expires: Thu, 10 Nov 2015 08:45:11 GMT,表示在这个时间点以前,缓存都是有效的。
  • Etag 也是和 Last-Modified 同样,对文件进行标识的字段
    不一样的是,Etag 的取值是一个对文件进行标识的特征字串。在向服务器查询文件是否有更新时,浏览器经过 If-None-Match
    字段把特征字串发送给服务器,由服务器和文件最新特征字串进行匹配,来判断文件是否有更新。没有更新回包304,有更新回包200。Etag 和 Last-Modified 可根据需求使用一个或两个同时使用。两个同时使用时,只要知足基中一个条件,就认为文件没有更新。

另外有两种特殊的状况:

  • 手动刷新页面(F5)
    浏览器会直接认为缓存已通过期(可能缓存尚未过时),在请求中加上字段:Cache-Control:max-age=0,发包向服务器查询是否有文件是否有更新。
  • 强制刷新页面(Ctrl+F5)
    浏览器会直接忽略本地的缓存(有缓存也会认为本地没有缓存),在请求中加上字段:Cache-Control:no-cache (或 Pragma:no-cache),发包向服务从新拉取文件

3.8. History

用户访问网页的历史记录一般会被保存在一个相似于栈的对象中,即history对象,点击返回就出栈,跳下一页就入栈

它提供了如下方法来操做页面的前进和后退:

  • window.history.back( ) 返回到上一个页面
  • window.history.forward( ) 进入到下一个页面
  • window.history.go( [delta] ) 跳转到指定页面

HTML5 对History Api 进行了加强,新增了两个Api 和一个事件,分别是pushState、replaceState 和 onpopstate:

  • pushState是往history对象里添加一个新的历史记录,即压栈。
  • replaceState 是替换history对象中的当前历史记录。
  • 当点击浏览器后退按钮或 js调用history.back 都会触发 onpopstate 事件。
    与其相似的还有一个事件:onhashchange,onhashchange是老API,浏览器支持度高,原本是用来监听hash变化的,但能够被利用来作客户端前进和后退事件的监听,而onpopstate是专门用来监听浏览器前进后退的,不只能够支持hash,非hash的同源 url 也支持。

3.9. HTML5离线缓存

HTML5离线缓存又叫Application Cache,是从浏览器的缓存中分出来的一块缓存区,若是要在这个缓存中保存数据,能够使用一个描述文件(manifest file),列出要下载和缓存的资源。

manifest 文件是简单的文本文件,它告知浏览器被缓存的内容(以及不缓存的内容)。manifest 文件可分为三个部分:

  • CACHE MANIFEST - 在此标题下列出的文件将在首次下载后进行缓存
  • NETWORK - 在此标题下列出的文件须要与服务器的链接,且不会被缓存
  • FALLBACK - 在此标题下列出的文件规定当页面没法访问时的回退页面(好比 404 页面)

离线缓存为应用带来三个优点:

  • 离线浏览 - 用户可在应用离线时使用它们
  • 速度 - 已缓存资源加载得更快
  • 减小服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源。

参考:

 
let //普通变量
const //静态变量
import //模块
class //类

const 关键字用于声明一个不可从新声明和赋值的常量,MDN: const
 
 
 
 

Web Storage

 

 

经常使用的web客户端存储

1. 引言

本地存储:避免取回数据前页面空白,减小请求服务器次数 chrome浏览器,查看resources 

2. 经常使用存储方案

2.1. indexDB

相似SQL数据库的结构化数据存储机制,可以在客户端存储大量的结构化数据 缺点: 兼容性很差,浏览器支持度低

2.2. cookie

HTTP cookie 「浏览器」提供的一种机制,经过document.cookie访问

  • 既能够服务器端设置,也能够客户端设置,会跟随任意HTTP请求发送
  • cookie是存于用户硬盘的一个文件,这个文件一般对应于一个域名,也就是说,cookie能够跨越一个域名下的多个网页,但不能跨越多个域名使用

cookie_limit!(/img/in-post/post-js-storage/cookie_limit.png)![]

2.2.1. 用途
  1. 保存用户信息
  2. 购物车
  3. 跟踪用户行为
2.2.2. 查找过程
  1. 用户访问站点
  2. web应用读取cookie包含的信息
  3. 再次访问时,浏览器在本地硬盘上查找相关cookie
  4. 若存在该cookie,添加到request header cookie字段中,与该HTTP请求一块儿发送 存储在cookie的数据每次都会自动添加到请求中,滥用则下降性能 cookie
2.2.3. cookie相关属性

resources_cookie

  1. domain 和 path 共同决定了cookie的共享页面

    例如cookie设置为"domain=.google.com.hk; path=/webhp",那么只有".google.com.hk/webhp"及"/webhp"下的任一子目录如"/webhp/aaa"或"/webhp/bbb"会发送cookie信息,而".google.com.hk"就不会发送,即便它们来自同一个域。

  2. expries 和 max-age 是用来决定cookie的生命周期

  3. secure cookie的安全标志 cookie中惟一一个非名值对儿的部分,默认为空,不管是 http 请求仍是 https 请求,均会发送cookie。 指定后,cookie只有在使用SSL链接(如HTTPS请求)时才会发送到服务器

  4. httponly 服务端设置 限制客户端脚本对cookie的访问,将 cookie 设置成 httponly 能够减轻xss攻击的危害

2.2.4. cookie设置
  1. 服务器端 经过发送一个名为 Set-Cookie 的HTTP头来建立一个cookie,做为 Response Headers 的一部分,每一个Set-Cookie 表示一个 cookie(若是有多个cookie,需写多个Set-Cookie),每一个属性也是以名/值对的形式(除了secure),属性间以分号加空格隔开。格式以下: Set-Cookie: name=value[; expires=GMTDate][; domain=domain][; path=path][; secure] cookie_server

只有name,value发送到服务器,其余是给浏览器的指示 2. 客户端 document.cookie = "name=value[; expires=GMTDate][; domain=domain][; path=path][; secure]"

var cookie = { /**设置cookie ** name 标识 ** value 值 ** options { ** 'path': '访问路径', ** 'domain' : '域名', ** 'expire' : 过时时间 } **/ setCookie : function(name,value,options){ var options = options ? options : {}, path = options.path ? options.path : '/', domain = options.domain ? options.domain : document.domain, time = options.expire ? (new Date().getTime() + options.expire * 1000) : '', expire = new Date(time).toUTCString(); document.cookie = encodeURIComponent(name) + "="+ encodeURIComponent(value) + ";expires=" + expire + ";domain=" + domain + ";path=" + path; }, //获取cookie getCookie: function(name){ var arr, reg=new RegExp("(^| )"+name+"=([^;]*)(;|$)"); if(arr=document.cookie.match(reg)){ console.log(arr); return unescape(arr[2]); } return null; }, //移除cookie removeCookie: function(name){ var val = this.getCookie(name); if(val != null){ this.setCookie(name,val, { expire : - 1 }) } } } 
2.2.5. 优缺点

优势: 兼容性好 缺点:

  1. 增长了网络流量
  2. 数据容量有限(最多4kb,浏览器间有区别)
  3. 安全性

2.3. sessionStorage和localStorage

key value形式存储

storage_support

  • localStorage - 没有时间限制的数据存储
  • sessionStorage - 针对一个 session 的数据存储
if(typeof(Storage)!=="undefined") { // 是的! 支持 localStorage sessionStorage 对象! // 一些代码..... } else { // 抱歉! 不支持 web 存储。 } 
2.3.1. 使用

localStorage.sitename = "超然haha"; localStorage.removeItem("lastname");

  • 保存数据: localStorage.setItem(key,value);
  • 读取数据: localStorage.getItem(key);
  • 删除单个数据: localStorage.removeItem(key);
  • 删除全部数据: localStorage.clear();
  • 获得某个索引的key:localStorage.key(index);
2.3.2. 适用范围
  • 不须要和服务器进行交互的一些数据 好比导航栏当前的状态,一些普通的数据进行缓存。甚至咱们能够存储html片断,js或者css文件段
  • 不少应用经过版本控制来存储一些不常常改动的js/css文件。减小用户请求带宽的同时优化整个页面的加载速度。
2.3.3. 注意

localstorage存储的值只能是字符串的形式 当咱们存储数据为引用对象的时候,会默认调用对象的toString方法,转化为字符串存储 因此咱们在存储数组时,存储的数据会将数据项以,隔开,解析的时候须要咱们分解成为数组再操做。而对于对象,咱们须要用JSON.stringify转化存储,获取数据后再用JSON.parse转化为对象

2.4. web storage 与 cookie

web storage优势:

  1. web storage 为了更大容量存储,通常限制为同一域名5M,,而且不一样域名的数据不能相互访问
  2. localStorage是存储在用户本地的浏览器上,不像cookie同样携带在http请求头部的字段里面,这有效的节约了带宽
  3. cookie须要指定做用域,不可跨域调用
  4. 拥有setItem,getItem,removeItem,clear等方法,不像cookie须要前端开发者本身封装setCookie,getCookie

cookie优势:

  1. cookie做用是与服务器交互,做为HTTP规范的一部分,web storage仅仅为本地存储而生

参考:经常使用的本地存储——cookie篇

 

 

 

 

You don't know js

 

 

“Give me a chance to know you. ”

更多内容: 移步这里

1. 做用域

1.1. 编译原理

尽管一般将 JavaScript 归类为“动态” 或“解释执行” 语言, 但事实上它是一门编译语言。
程序中的一段源代码在执行以前会经历三个步骤, 统称为“编译”

  1. 分词/词法分析(Tokenizing/Lexing)
    • 这个过程会将由字符组成的字符串分解成(对编程语言来讲) 有意义的代码块, 这些代
      码块被称为词法单元(token)。 例如, 考虑程序 var a = 2;。 这段程序一般会被分解成
      为下面这些词法单元: var、 a、 =、 2 、 ;。
  2. 解析/语法分析(Parsing)
    • 这个过程是将词法单元流(数组) 转换成一个由元素逐级嵌套所组成的表明了程序语法
      结构的树。 这个树被称为“抽象语法树”(Abstract Syntax Tree, AST)。
  3. 代码生成
    • 将 AST 转换为可执行代码的过程称被称为代码生成。

1.2. 做用域嵌套

当一个块或函数嵌套在另外一个块或函数中时, 就发生了做用域的嵌套。 所以, 在当前做用
域中没法找到某个变量时, 引擎就会在外层嵌套的做用域中继续查找, 直到找到该变量,
或抵达最外层的做用域(也就是全局做用域) 为止。
将做用域处理的过程可视化,以下面的建筑:

做用域是一套规则, 用于肯定在何处以及如何查找变量(标识符)。

2. 词法做用域

做用域共有两种主要的工做模型:

  • 词法做用域(重点讨论)
  • 动态做用域(如bash脚本,perl中的一些模式)

2.1. 词法阶段

词法化的过程会对源代码中的字符进行检查,若是是有状态的解析过程,还会赋予单词语义——名称来历

词法做用域是由你在写代码时将变量和块做用域写在哪里来决定的

如:

function foo(a) { var b = a * 2; function bar(c) { console.log( a, b, c ); } bar( b * 3 ); } foo( 2 ); // 2, 4, 12

能够将以上代码想象成几个逐级包含的气泡

① 包含着整个全局做用域, 其中只有一个标识符: foo。
② 包含着 foo 所建立的做用域, 其中有三个标识符: a、 bar 和 b。
③ 包含着 bar 所建立的做用域, 其中只有一个标识符: c。
做用域气泡由其对应的做用域块代码写在哪里决定, 它们是逐级包含的。

查找

做用域气泡的结构和互相之间的位置关系给引擎提供了足够的位置信息,做用域查找会在找到第一个匹配的标识符时中止

全局变量会自动成为全局对象(好比浏览器中的window对象)的属性,所以能够不直接经过全局对象的词法名称,而是间接地经过对全局对象属性的引用来对其进行访问。
window.a经过这种技术能够访问那些被同名变量所遮蔽的全局变量。 但非全局的变量
若是被遮蔽了, 不管如何都没法被访问到。

不管函数在哪里被调用,也不管它如何被调用,它的词法做用域都只由函数被声明时所处的位置决定。
词法做用域查找只会查找一级标识符,好比a、b和c。若是代码中引用了foo.bar.baz,词法做用域查找只会试图查找 foo 标识符,找到这个变量后, 对象属性访问规则会分别接管对 bar 和 baz 属性的访问

2.2. 欺骗词法

JavaScript 中有两个机制能够“欺骗”词法做用域:eval(..)和with。前者能够对一段包
含一个或多个声明的“代码”字符串进行演算,并借此来修改已经存在的词法做用域(在
运行时)。后者本质上是经过将一个对象的引用看成做用域来处理,将对象的属性看成做
用域中的标识符来处理,从而建立了一个新的词法做用域(一样是在运行时)。

3. 函数做用域和块做用域

到底是什么生成了一个新的气泡?只有函数会生成新的气泡吗?JavaScript中的其余结构能生成做用域气泡吗?

3.1. 隐藏内部实现

3.1.1. 最小受权|最小暴露原则

指在软件设计中,应该最小限度地暴露必要内容,而将其余内容都“隐藏”起来,好比某个模块或对象的API设计。——可延伸到如何选择做用域来包含变量和函数

如:

function doSomething(a) { b = a + doSomethingElse(a * 2); console.log(b * 3); } function doSomethingElse(a) { return a - 1; } var b; doSomething(2); // 15 /*在这个代码片断中, 变量 b 和函数 doSomethingElse(..) 应该是 doSomething(..) 内部具体实现的“私有” 内容。 给予外部做用域对 b 和 doSomethingElse(..) 的“访问权限” 不只没有必要且危险*/ // 更合理 function doSomething(a) { function doSomethingElse(a) { return a - 1; } var b; b = a + doSomethingElse(a * 2); console.log(b * 3); } d oSomething(2); // 15 //设计上将具体内容私有化
3.1.2. 规避冲突
  1. 全局命名空间
    用变量做为库的命名空间
    全部须要暴露给外界的功能都会成为这个对象(命名空间)的属性,而不是将本身的标识符暴漏在顶级的词法做用域中

    如:

    var MyReallyCoolLibrary = { awesome: "stuff", doSomething: function() { // ... }, doAnotherThing: function() { // ...26 } };

3.2. 函数做用域

区分函数声明和表达式最简单的方法是看function关键字出如今声明中的位置(不只仅是一行代码,而是整个声明中的位置)。若是function是声明中的第一个词,那么就是一个函数声明,不然就是一个函数表达式。

1. 匿名和具名

始终给函数表达式命名是一个最佳实践

setTimeout( function timeoutHandler() { // 快看, 我有名字了 console.log( "I waited 1 second!" ); }, 1000 );

2. 当即执行函数表达式

/*第一种*/ var a = 2; (function foo() { var a = 3; console.log( a ); // 3 })(); console.log( a ); // 2 /* 第二种形式*/ (function foo(){ .. })() /*进阶*/ /*将 window 对象的引用传递进去, 但将参数命名为 global*/ var a = 2; (function IIFE( global ) { var a = 3; console.log( a ); // 3 console.log( global.a ); // 2 })( window ); console.log( a ); // 2 /*IIFE 还有一种变化的用途是倒置代码的运行顺序, 将须要运行的函数放在第二位, 在 IIFE执行以后看成参数传递进去。*/ var a = 2; (function IIFE( def ) { def( window ); })(function def( global ) { var a = 3; console.log( a ); // 3 console.log( global.a ); // 2 });

函数表达式 def 定义在片断的第二部分, 而后看成参数(这个参数也叫做 def) 被传递进
IIFE 函数定义的第一部分中。 最后, 参数 def(也就是传递进去的函数) 被调用, 并将
window 传入看成 global 参数的值。

函数不是惟一的做用域单元。块做用域指的是变量和函数不只能够属于所处的做用域,也能够属于某个代码块(一般指 { .. } 内部)。

4. 变量提高

先有蛋(声明) 后有鸡(赋值)。

JavaScript 引擎将 var a和 a = 2 看成两个单独的声明, 第一个是编译阶段的任务, 而第二个则是执行阶段的任务。不管做用域中的声明出如今什么地方,都将在代码自己被执行前首先进行处理。能够将这个过程形象地想象成全部的声明(变量和函数) 都会被“移动” 到各自做用域的最顶端, 这个过程被称为提高

只有声明自己会被提高, 而赋值或其余运行逻辑会留在原地。

4.1. 函数优先

函数声明和变量声明都会被提高。 可是一个值得注意的细节(这个细节能够出如今有多个
“重复” 声明的代码中) 是 函数会首先被提高, 而后才是变量。

5. 做用域闭包

闭包的建立和使用在你的代码中随处可见。你缺乏的是根据你本身的意愿来识别、拥抱和影响闭包的思惟环境

5.1 什么是闭包

当函数能够记住并访问所在的词法做用域,即便函数是在当前词法做用域以外执行,这时就产生了闭包。

function foo() { var a = 2; function bar() { console.log(a); // 2 } bar(); } foo(); //基于词法做用域的查找规则, 函数bar() 能够访问外部做用域中的变量 a function foo() { var a = 2; function bar() { console.log(a); } return bar; } var baz = foo(); baz(); // 2 —— 朋友, 这就是闭包的效果。

bar() 显然能够被正常执行。 可是在这个例子中, 它在本身定义的词法做用域之外的地方执行
foo() 执行后垃圾回收器用来释放再也不使用的内存空间,闭包的“神奇”之处正是能够阻止这件事情的发生。 事实上内部做用域依然存在,bar() 依然持有对该做用域的引用, 而 这个引用就叫做闭包。
常见的闭包:

function wait(message) { setTimeout(function timer() { console.log(message); }, 1000); } wait("Hello, closure!"); //timer 具备涵盖 wait(..) 做用域的闭包, 所以还保有对变量 message 的引用。 //wait(..) 执行 1000 毫秒后, 它的内部做用域并不会消失, timer 函数依然保有 wait(..)做用域的闭包。

只要使用了回调函数, 实际上就是在使用闭包!

5.2. 循环和闭包

for (var i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); //6 }, i * 1000); } /*全部的回调函数依然是在循环结束后才会被执行, 所以会每次输出一个 6 出来。*/

缘由
缺陷是咱们试图假设循环中的每一个迭代在运行时都会给本身“捕获” 一个 i 的副本。
可是根据做用域的工做原理, 实际状况是尽管循环中的五个函数是在各个迭代中分别定义的,
可是它们都被封闭在一个共享的全局做用域中, 所以实际上只有一个 i。
咱们须要更多的闭包做用域, 特别是在循环的过程当中每一个迭代都须要一个闭包做用域

//它须要有本身的变量, 用来在每一个迭代中储存 i 的值: for (var i = 1; i <= 5; i++) { (function() { var j = i; setTimeout(function timer() { console.log(j); }, j * 1000); })(); } //使用let //本质上这是将一个块转换成一个能够被关闭的做用域。 for (var i = 1; i <= 5; i++) { let j = i; // 是的, 闭包的块做用域! setTimeout(function timer() { console.log(j); }, j * 1000); } //块做用域和闭包联手 for (let i = 1; i <= 5; i++) { setTimeout(function timer() { console.log(i); }, i * 1000); }

5.3. 模块

5.3.1. 模块方式演进

模块有两个主要特征:

  1. 为建立内部做用域而调用了一个包装函数;
  2. 包装函数的返回值必须至少包括一个对内部函数的引用,这样就会建立涵盖整个包装函数内部做用域的闭包。
    ```javascript
    //exa1:
    //两个私有数据变量 something和 another, 以及 doSomething() 和 doAnother()
    //它们的词法做用域(而这就是闭包) 也就是 foo() 的内部做用域。
    function foo() {
    var something = "cool";
    var another = [1, 2, 3];

    function doSomething() {
    console.log(something);
    }

    function doAnother() {
    console.log(another.join(" ! "));
    }
    }

//模块
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];

function doSomething() { console.log(something); } function doAnother() { console.log(another.join(" ! ")); } return { doSomething: doSomething, doAnother: doAnother };

}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
//1. CoolModule() 只是一个函数, 必需要经过调用它来建立一个模块实例。 若是不执行外部函数, 内部做用域和闭包都没法被建立。
//2. CoolModule() 返回一个用对象字面量语法 { key: value, ... } 来表示的对象。 这个返回的对象中含有对内部函数而不是内部数据变量的引用

//改进
var foo = (function CoolModule() {
var something = "cool";
var another = [1, 2, 3];

function doSomething() { console.log(something); } function doAnother() { console.log(another.join(" ! ")); } return { doSomething: doSomething, doAnother: doAnother };

})();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3

//模块模式另外一个简单但强大的变化用法是, 命名将要做为公共 API 返回的对象:
var foo = (function CoolModule(id) {
function change() {
// 修改公共 API
publicAPI.identify = identify2;
}

function identify1() { console.log(id); } function identify2() { console.log(id.toUpperCase()); } var publicAPI = { change: change, identify: identify1 }; return publicAPI;

})("foo module");
foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE
```
当经过返回一个含有属性引用的对象的方式来将函数传递到词法做用域外部时,咱们已经创造了能够观察和实践闭包的条件。

所以 一个从函数调用所返回的,只有数据属性而没有闭包函数的对象并非真正的模块

5.3.2. ES6的模块

ES6 的模块没有“行内” 格式, 必须被定义在独立的文件中(一个文件一个模块),可
以在导入模块时异步地加载模块文件。

//bar.js function hello(who) { return "Let me introduce: " + who; } export hello //foo.js // 仅从 "bar" 模块导入 hello() import hello from "bar"; var hungry = "hippo"; function awesome() { console.log( hello(hungry).toUpperCase() ); }

import能够将一个模块中的一个或多个API导入到当前做用域中,并分别绑定在一个变量上(在咱们的例子里是hello)。module会将整个模块的API导入并绑定到一个变量上(在咱们的例子里是foo)。export会将当前模块的一个标识符(变量、函数)导出为公共API。这些操做能够在模块定义中根据须要使用任意屡次。

5.3.3. 动态做用域

动态做用域并不关心函数和做用域是如何声明以及在何处声明的,只关心它们从何处调用。换句话说,做用域链是基于调用栈的,而不是代码中的做用域嵌套。

function foo() { console.log(a); //2 —— 若是是动态做用域3 } function bar() { var a = 3; foo(); } var a = 2; bar();

JavaScript并不具备动态做用域。它只有词法做用域

主要区别:
词法做用域是在写代码或者说定义时肯定的,而动态做用域是在运行时肯定的。(this也是!)词法做用域关注函数在何处声明,而动态做用域关注函数从何处调用

6. this词法

6.1. _self
常见this绑定丢失解决方案: 'var _self = this'

6.2. 箭头函数

ES6 中的箭头函数引入了一个叫做 this 词法的行为

var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout(() => { // 箭头函数是什么鬼东西? this.count++; console.log("awesome?"); }, 100); } } }; obj.cool(); // 很酷吧 ?

简单来讲,箭头函数在涉及this绑定时的行为和普通函数的行为彻底不一致。它放弃了全部普通this绑定的规则,取而代之的是用当前的词法做用域覆盖了this原本的值
这个代码片断中的箭头函数只是“继承”了cool()函数的this绑定(所以调用它并不会出错)。

6.3. bind

//正确使用和包含 this 机制 var obj = { count: 0, cool: function coolFn() { if (this.count < 1) { setTimeout(function timer() { this.count++; // this 是安全的 // 由于 bind(..) console.log("more awesome"); }.bind(this), 100); // look, bind()! } } }; obj.cool(); // 更酷了。

 

 

 

 

var foo=function(){}
1 函数表达式?
2 也要有名字? var foo=function foooo(){}

为何函数表达式有名字是最佳实践?

 

匿名函数写起来很方便,如今的不少库使用的也不少,但有几点风险:1. 若是一个函数被调用了屡次,出现问题后想要使用栈追踪想看看都在哪里调用了,若是是匿名函数的话不会显示有意义的函数名称,不方便调试2. 调用自身只能用arguments.callee了3. 解除事件绑定时,也须要函数名4. 影响可读性

相关文章
相关标签/搜索