其他

优质
小牛编辑
142浏览
2023-12-01

全称:Asynchronous Javascript And XML。异步传输+js+xml。

var xhr = new XMLHttpRequest();

xhr.open("get", "example.php", true); \/\/最后一个参数表示是否异步

xhr.send();\/\/参数为作为请求主体发送的数据,没有就null

收到响应后,响应数据自动填充XHR对象的属性,有responseText、responseXML、status、statusText,异步的话有readyState属性,它的改变会触发readystatechange 事件。

readyState中0表示未初始化,没调用open()方法,1表示启动,调用open()未调用send(),2发送,调用send()未收到响应,3接收到部分响应数据,4接收到全部响应数据。

HTTP头部信息

使用setRequestHeader()方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值。要成功发送请求头部信息,必须在调用open()方法之后且调用send()方法之前调用setRequestHeader()。调用XHR 对象的getResponseHeader() 方法并传入头部字段名称,可以取得相应的响应头部信息。而调用getAllResponseHeaders()方法则可以取得一个包含所有头部信息的长字符串。

get和post请求

get通常请求用于向服务器查询信息,具有幂等性。一般将查询字符串参数追加到URL 的末尾,以便将信息发送给服务器。查询字符串中每个参数的名称和值都必须使用encodeURIComponent()进行编码,且用&号分开。

post通常用于向服务器发送应该被保存的数据。通过创建FormData对象,可以序列化表单以及创建与表单格式相同的数据(用于通过XHR 传输)。使用FormData 的方便之处体现在不必明确地在XHR 对象上设置请求头部。XHR 对象能够识别传入的数据类型是FormData 的实例,并配置适当的头部信息。或者在open()后使用 setRequestHeader() 来添加 HTTP 头。然后在 send() 方法中规定您希望发送的数据: `xhr.setRequestHeader("Content-type","application\/x-www-form-urlencoded"); xhr.send("fname=Bill&lname=Gates");\/\/字符串

xhr.setRequestHeader("Content-Type", "application\/json;charset=UTF-8"); xhr.send(JSON.stringify({name:"John Rambo", time:"2pm"}));\/\/json`

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。然而,在以下情况中,请使用 POST 请求: 无法使用缓存文件(更新服务器上的文件或数据库) 向服务器发送大量数据(POST 没有数据量限制) 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠。

进度事件

  • loadstart:在接收到响应数据的第一个字节时触发。

  • progress:在接收响应期间持续不断地触发。

  • error:在请求发生错误时触发。事件处理程序会接收到一个event 对象,其target 属性是XHR 对象,但包含着三个额外的属性:lengthComputable(进度是否可用的布尔值)、position(表示已经接收的字节数) 和totalSize(根据Content-Length 响应头部确定的预期字节数)。

  • abort:在因为调用abort()方法而终止连接时触发。

  • load:在接收到完整的响应数据时触发。

  • loadend:在通信完成或者触发error、abort 或load 事件后触发。

缺点:ajax不支持浏览器back按钮。安全问题 AJAX暴露了与服务器交互的细节。对搜索引擎的支持比较弱。破坏了程序的异常机制。不容易调试。

IE缓存问题:在IE浏览器下,如果请求的方法是GET,并且请求的URL不变,那么这个请求的结果就会被缓存。解决这个问题的办法可以通过在URL末尾添加上随机的时间戳参数('t'= + new Date().getTime())或者:open('GET','demo.php?rand=+Math.random()',true);

Ajax请求的页面历史记录状态问题可以通过锚点来记录状态,location.hash。让浏览器记录Ajax请求时页面状态的变化。还可以通过HTML5的history.pushState,来实现浏览器地址栏的无刷新改变

跨域

问题来源——同源策略,协议相同、域名相同、端口相同。 一个源可以向另一个不同源发送数据,但不可以从另一个源读取数据。其目的是防止某个文档或脚本从多个不同源装载。同源策略仅仅阻止了第三方站点读取来自其他站点的内容,但是却没有防止这些第三方站点向其他站点发出请求(CSRF攻击)。

CORS(跨源资源共享)

CORS 背后的基本思想,就是使用自定义的HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

发送请求时给它附加一个额外的Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。服务器设置Access-Control-Allow-Origin,请求和响应都不包含cookie 信息。跨域XHR 对象也有一些限制,不能使用setRequestHeader()设置自定义头部、 不能发送和接收cookie、调用getAllResponseHeaders()方法总会返回空字符串。

JSONP

JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。通过动态<script>元素来使用,使用时可以为src 属性指定一个跨域URL。 动态插入script标签,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。 问题:安全问题,难以判断请求是否失败。

封装一个函数:

function getJsonp(options) {
    var callbackName = options.callbackName;
    var url = options.url;
    var scriptElem = document.createElement('script');
    scriptElem.setAttribute('src', url + '?callback=' + callbackName);
    scriptElem.onload = function(e) {
        delete window[callbackName];
        this.parentNode.removeChild(this);
    };
    scriptElem.onerror = function(e) {
        console.log(e, 'load error');
        delete window[callbackName];
        this.parentNode.removeChild(this);
    };
    //设置回调函数
    window[callbackName] = options.success;
    // 添加
     document.querySelector('head').appendChild(scriptElem);
}

window.name

window.name 在一个窗口(标签)的生命周期之内是共享的, 当对一个页面设置 window.name 后,即使改变该页面 url,window.name 也不会被重写。 利用这点就可以传输一些数据。

可以父窗口打开一个不同源的子窗口,该网页信息写入window.name,然后子窗口调回到主窗口同域的代理页面网址( 因为要同源才可以获取到window.name ),就可以读取window.name信息了。结合iframe可以直接读取信息。

document.domain

跨子域。 把两个同主域,不同子域的文件的 document.domain 设置成为页面本身或者更高一级的域名。缺点:安全问题,不适合页面较多的网站。

location.hash

监听hashchange事件,子页面可以通过 parent. location.hash给父页面传数据,父页面可以改变iframe子页面的src来给子页面传数据。

postMessage

使用postMessage(data,url)来发送数据, 调用postMessage方法的window对象是指要接收消息的那一个window对象。通过message事件,监听对方的消息,message事件对象的属性有source、origin、data,接收消息的窗口可以用event.source引用发送消息的窗口,而event.origin可以过滤掉不是发送给本窗口的消息。可以通过它来读写其他窗口的localStorage.

以上后四种方法经常配合iframe使用, iframe主要的两个API就是contentWindow,和contentDocument ,前者用于获取iframe的window对象,后者 获取iframe的document对象。

安全

CSRF 攻击与防御

Web安全测试之跨站请求伪造(CSRF)篇

CSRF 跨站请求伪造

对于未被授权系统有权访问某个资源的情况,我们称之为CSRF(Cross-Site Request Forgery,跨站点请求伪造)。未被授权系统会伪装自己,让处理请求的服务器认为它是合法的。

请注意,下列措施对防范CSRF 攻击不起作用:要求发送POST 而不是GET 请求——很容易改变。 检查来源URL 以确定是否可信——来源记录很容易伪造。 基于cookie 信息进行验证——同样很容易伪造。

原理:会话是由cookie唯一标识的,并且该cookie是由浏览器自动发送的。根源在于Web的身份验证机制虽然可以向目标站点保证一个请求来自于某个用户的浏览器,但是却无法保证该请求的确是那个用户发出的或者是经过那个用户批准的(Web的隐式身份验证机制)。

理解:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1. 登录受信任网站A,并在本地生成Cookie。
  2. 在不登出A的情况下,访问危险网站B。

防御:

1.在请求地址中添加 token 并验证:先服务器端要以某种策略生成随机字符串,作为令牌(token),保存在 Session 里。然后在发出请求的页面,把该令牌以隐藏域一类的形式,与其他信息一并发出。在接收请求的页面,把接收到的信息中的令牌与 Session 中的令牌比较,只有一致的时候才处理请求,处理完成后清理session中的值,否则返回 HTTP 403 拒绝请求或者要求用户重新登陆验证身份

  • Cookie Hashing(所有表单都包含同一个伪随机值):因为攻击者不能获得第三方的Cookie(理论上)。在表单里增加Hash值,以认证这确实是用户发送的请求。然后在服务器端进行Hash值验证。
  • One-Time Tokens(不同的表单包含一个不同的伪随机值):但是得注意用户开多个页来浏览站点的情况,可以通过为一个用户授权多个token来解决,但是得设置生命周期。

2.在 HTTP 头中自定义属性并验证:这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。

然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。

3.验证码

4.良好的API设计,最好遵循Restful风格。GET操作只是获取资源,不能操作资源,它具备幂等性。如果需要对资源进行操作,请使用POST请求,当然你如果想使用PUT和DELETE,可以使用POST来模拟。

5.验证 HTTP Referer 字段:根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。缺点:依赖浏览器,黑客可能可以修改

XSS原理及防范

Xss(cross-site scripting)通常是指,攻击者向一个站点注入恶意代码(通常为JavaScript代码或html标签),以攻击该站点的其它用户(即攻击的最终目标并非站点本身)。比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。

攻击者使被攻击者在浏览器中执行脚本后,如果需要收集来自被攻击者的数据(如cookie或其他敏感信息),可以自行架设一个网站,让被攻击者通过JavaScript等方式把收集好的数据作为参数提交,随后以数据库等形式记录在攻击者自己的服务器上。

当来自用户的不可信数据在没有验证以及反射回浏览器而没有进行编码或转义的情况下进行了处理, 导致浏览器引擎执行了代码。\

反射型XSS:简单地把用户输入“反射”给浏览器,往往需要用户点击一个恶意链接才能攻击成功。

存储型XSS:把用户输入数据存储在服务器端(恶意js代码的博客)

DOM Based XSS:修改页面DOM节点形成的XSS

常用的XSS攻击手段和目的有:

  • 盗用cookie,获取敏感信息。
  • 利用植入Flash,通过crossdomain权限设置进一步获取更高权限;或者利用Java等得到类似的操作。
  • 利用iframe、frame、XMLHttpRequest或上述Flash等方式,以(被攻击)用户的身份执行一些管理动作,或执行一些一般的如发微博、加好友、发私信等操作。
  • 利用可被攻击的域受到其他域信任的特点,以受信任来源的身份请求一些平时不允许的操作,如进行不当的投票活动。
  • 在访问量极大的一些页面上的XSS可以攻击一些小型网站,实现DDoS攻击的效果。

XSS防范方法

原则: 不相信客户输入的数据,验证输入并基于语境和按照正确顺序转义不可信数据。

  • 使用HTTP头指定类型:很多时候可以使用HTTP头指定内容的类型,使得输出的内容避免被作为HTML解析。
  • 将重要的cookie标记为http only, 这样的话Javascript 中的document.cookie语句就不能获取到cookie了.
  • 只允许用户输入我们期望的数据。
  • 对数据进行Html Encode 处理
  • 过滤或移除特殊的Html标签, 例如:<script>, <iframe> , &lt; for <, &gt; for >, &quot for
  • 过滤JavaScript 事件的标签。例如 "onclick=", "onfocus" 等等。

首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。

其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。

尽量采用POST 而非GET 提交表单

XSS与CSRF有什么区别吗?

XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。

CSRF和XSS攻击的区别在于,XSS攻击需要JavaScript,而CSRF攻击不需要;XSS攻击要求站点接受恶意代码,而对于CSRF攻击来说,恶意代码位于第三方站点上。过滤用户的输入可以防止恶意代码注入到某个站点,但是它无阻止法恶意代码在第三方站点上运行。由于恶意代码可以在第三方站点上运行,所以防御XSS攻击的措施无法保护站点不受CSRF攻击的危害。如果站点具有XSS攻击漏洞,那么它也有CSRF攻击漏洞。但是,即使站点针对XSS攻击采取了全面保护,却仍然面临CSRF攻击的威胁。

sql注入原理

通过将数据注入网络应用程序,然后被用于 SQL 请求来操作。数据通常来自类似网页表单的不可信来源。不过,数据也可能来自包括数据库本身在内的其他来源。如果攻击成功,SQL 注入攻击能够操纵受攻击的 SQL 请求,从而进行非程序员意愿的数据库操作。

就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

总的来说有以下几点:

1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。检查变量数据类型和格式,过滤特殊符号。

2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。

3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。

4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息

JSON

JSON 是一个轻量级的数据格式,语法可以包含简单值、对象、数组。和JS对象\/数组字面量相比,JSON对象\/数组没有声明变量且没有末尾分号,JSON对象属性必须加双引号。

stringify方法用于序列化, 除了要序列化的js对象还有两个参数:过滤器(数组或函数),是否保留缩进的选项(数值表示空格数或者字符串),对象上定义toJSON()可以作为函数过滤器的补充,会首先被调用。

JSON.parse()方法也可以接收另一个参数,该参数是一个函数,将在每个键值对儿上调用。、

应用缓存

判断是否离线:navigator.onLine属性,Online,Offline事件。

如何缓存:使用描述文件manifest file.<html manifest = '/offline.manifest'>.

文件列出需要下载和缓存的资源。

Web数据存储

cookie

最初用于客户端存储会话信息,可以用个HTTP头添加Set-Cookie来将cookie发送给服务器。存在客户端计算机上,每个域的cookie总数有限。cookie由名称、值、域、路径、失效时间、安全标志等信息构成。 cookie的最大大约为4096字节,为了兼容性,一般不能超过4095字节。

优点:极高的扩展性和可用性

1.通过良好的编程,控制保存在cookie中的session对象的大小。

2.通过加密和安全传输技术(SSL),减少cookie被破解的可能性。

3.只在cookie中存放不敏感数据,即使被盗也不会有重大损失。

4.控制cookie的生命期,使之不会永远有效。偷盗者很可能拿到一个过期的cookie。

缺点:

1.Cookie数量和长度的限制。每个domain最多只能有20条cookie,每个cookie长度不能超过4KB,否则会被截掉.

2.安全性问题。如果cookie被人拦截了,那人就可以取得所有的session信息。即使加密也与事无补,因为拦截者并不需要知道cookie的意义,他只要原样转发cookie就可以达到目的了。

3.有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用

  • 什么是Cookie 隔离?(或者说:请求资源的时候不要让它带cookie怎么做)

如果静态文件都放在主域名下,那静态文件请求的时候都带有的cookie的数据提交给server的,非常浪费流量, 所以不如隔离开。

因为cookie有域的限制,因此不能跨域提交请求,故使用非主要域名的时候,请求头中就不会带有cookie数据, 这样可以降低请求头的大小,降低请求时间,从而达到降低整体请求延时的目的。

同时这种方式不会将cookie传入Web Server,也减少了Web Server对cookie的处理分析环节, 提高了webserver的http请求的解析速度。

webStorage

sessionStorage,localStorage,区别是前者只保存到浏览器关闭。方法有clear(),getItem(name),key(index),removeItem(name),setItem(name,value)

storage事件,对Storage 对象进行任何修改,都会在文档上触发storage 事件。事件的event对象由以下属性:domain,key,newValue,oldValue

web storage和cookie的区别

  • cookie数据始终在同源的http请求中携带(即使不需要),所以会在浏览器和服务器间来回传递。 sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存;
  • sessionStorage和localStorage的存储空间更大;
  • sessionStorage和localStorage有更多丰富易用的接口,比如setItem,getItem,removeItem,clear等方法,而cookie需要前端开发者自己封装setCookie,getCookie。
  • sessionStorage和localStorage有各自独立的存储空间;
  • 有期时间:
    • localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
    • sessionStorage 数据在当前浏览器窗口关闭后自动删除。
    • cookie 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭

cookie是网站为了标示用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密)。 cookie数据始终在同源的http请求中携带(即使不需要),会在浏览器和服务器间来回传递。 sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。

但是cookie也是不可或缺的:cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而Web Storage仅仅是为了在本地“存储”数据而生。

requestAnimationFrame()

告诉浏览器某些JavaScript 代码将要执行动画。这样浏览器可以在运行某些代码后进行适当的优化。

接收一个参数,即在重绘屏幕前调用的一个函数。这个函数负责改变下一次重绘时的DOM样式。

回调函数只会被传入一个DOMHighResTimeStamp参数,这个参数指示当前被 requestAnimationFrame 序列化的函数队列被触发的时间 。

返回requestID 是一个长整型非零值,作为一个唯一的标识符.你可以将该值作为参数传给window.cancelAnimationFrame() 来取消这个回调函数。

组件

MV*框架的基本原理

MV*框架的思维方式是:以模型为中心,DOM操作只是附加

Model-View-Presenter(MVP) and Model-View-ViewModel(MVVM)模式,实际上就是将职责重分配,都是为了将数据,视图与业务逻辑的拆分。

MVC:View 传送指令到 Controller,Controller 完成业务逻辑后,要求 Model 改变状态,Model 将新的数据发送到 View,用户得到反馈,所有通信都是单向的。

MVVM:组成部分Model(数据访问层)、View(UI界面)、ViewModel(是View的抽象,负责View与Model之间信息转换,将View的Command传送到Model),Angular它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。

如果我们来设计Angular这么一个前端框架,应当如何入手呢?很显然,逻辑的控制必须使用JavaScript,一个框架,最本质的事情在于它的逻辑处理方式。 我们的界面为什么可以多姿多彩?因为有HTML和CSS,注意到这两种东西都是配置式的写法,参照后端的依赖注入,如果把这两者视为跟Spring框架中一些XML等同的配置文件,思路就豁然开朗了。

与后端不同的是,充当前端逻辑工具的JavaScript不能做入口,必须挂在HTML里才能运行,所以出现了一个怪异的状况:逻辑要先挂在配置文件(HTML)上,先由另外的容器(浏览器或者Hybird的壳)把配置文件加载起来,然后才能从某个入口开始执行逻辑。好消息是,过了这一步,逻辑层就开始大放异彩了。

从这个时候开始,框架就启动了,它要做哪些事情呢?

  • 初始化自身(bootstrap)
  • 异步加载可能尚未引入的JavaScript代码(require)
  • 解析定义在HTML上的规则(template parser)
  • 实例化模型(scope)
  • 创建模型和DOM的关联关系(binding, injection)

这些是主线流程,还有一些支线,比如:

  • 解析url的search字符串,恢复状态(route)
  • 加载HTML部件模板(template url)
  • 部件模板和模型的关联(binding)

如何做组件化

  • HTML的组件化

SPA的一个典型特征就是部分加载,界面的部件化也是其中比较重要的一环。界面片段在动态请求得到之后,借助模版引擎之类的技术,经过某种转换,放置到主界面相应的地方。所以,从这个角度来看,HTML的组件化非常容易理解,那就是界面的片段化和模板化。

  • JavaScript的组件化

JavaScript这个部分有好几个发展阶段。

  1. 早期的共享文件,把公共功能的代码提出出来,多个页面共用
  2. 动态引用,消灭全局变量
  3. 在某些框架上进一步划分,比如Angular里面又分为provider,service,factory,controller

JavaScript组件化的目标是什么呢,是清晰的职责,松耦合,便于单元测试和重复利用。这里的松耦合不仅体现在js代码之间,也体现在js跟 DOM之间的关系,所以像Angular这样的框架会有directive的概念,把DOM操作限制到这类代码中,其他任何js代码不操作DOM。

如上图所示,总的原则是先分层次,层内再作切分。这么做的话,不再存在之前那种端到端组件了,使用起来没有原先那么方便,但在另外很多方面比较好。

  • CSS的组件化

这方面,业界也有很多探索,比如LESS,SASS,Stylus等。为什么CSS也要做组件化呢?传统的CSS是一种扁平的文本结构,变更成本较 高,比如说想要把结构从松散改紧凑,需要改动很多。如果把实际使用的CSS只当作输出结果,而另外有一种适合变更的方式当作中间过程,这就好多了。比如 说,我们把一些东西定义成变量,每个细节元素使用这些变量,当需要整体变更的时候,只需修改这些变量然后重新生成一下就可以了。

设计模式

工厂模式的主要好处就是可以消除对象间的耦合,通过使用工程方法而不是new关键字。将所有实例化的代码集中在一个位置防止代码重复。解决了重复实例化的问题 ,但还有一个问题,那就是识别问题,因为根本无法搞清楚他们到底是哪个对象的实例。

使用构造函数的方法 ,即解决了重复实例化的问题 ,又解决了对象识别的问题,该模式与工厂模式的不同之处在于:

  1. 构造函数方法没有显示的创建对象 (new Object());
  2. 直接将属性和方法赋值给 this 对象;
  3. 没有 renturn 语句。

    1) 单例: 任意对象都是单例,无须特别处理
    var obj = {name: 'michaelqin', age: 30};
    
    2) 工厂: 就是同样形式参数返回不同的实例
    function Person() { this.name = 'Person1'; }
    function Animal() { this.name = 'Animal1'; }
    
    function Factory() {}
    Factory.prototype.getInstance = function(className) {
      return eval('new ' + className + '()');
    }
    
    var factory = new Factory();
    var obj1 = factory.getInstance('Person');
    var obj2 = factory.getInstance('Animal');
    console.log(obj1.name); // Person1
    console.log(obj2.name); // Animal1
    
    3) 代理: 就是新建个类调用老类的接口,包一下
    function Person() { }
    Person.prototype.sayName = function() { console.log('michaelqin'); }
    Person.prototype.sayAge = function() { console.log(30); }
    
    function PersonProxy() {
      this.person = new Person();
      var that = this;
      this.callMethod = function(functionName) {
          console.log('before proxy:', functionName);
          that.person[functionName](); // 代理
          console.log('after proxy:', functionName);
      }
    }
    
    var pp = new PersonProxy();
    pp.callMethod('sayName'); // 代理调用Person的方法sayName()
    pp.callMethod('sayAge'); // 代理调用Person的方法sayAge()
    
    4) 观察者: 就是事件模式,比如按钮的onclick这样的应用.
    function Publisher() {
      this.listeners = [];
    }
    Publisher.prototype = {
      'addListener': function(listener) {
          this.listeners.push(listener);
      },
    
      'removeListener': function(listener) {
          delete this.listeners[listener];
      },
    
      'notify': function(obj) {
          for(var i = 0; i < this.listeners.length; i++) {
              var listener = this.listeners[i];
              if (typeof listener !== 'undefined') {
                  listener.process(obj);
              }
          }
      }
    }; // 发布者
    
    function Subscriber() {
    
    }
    Subscriber.prototype = {
      'process': function(obj) {
          console.log(obj);
      }
    }; // 订阅者
    
    var publisher = new Publisher();
    publisher.addListener(new Subscriber());
    publisher.addListener(new Subscriber());
    publisher.notify({name: 'michaelqin', ageo: 30}); // 发布一个对象到所有订阅者
    publisher.notify('2 subscribers will both perform process'); // 发布一个字符串到所有订阅者
    

网页性能优化

对普通的网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO。

  • 减少HTTP请求

    • 图片地图:一个图片上关联多个url
    • CSS sprites:多个图片合并
    • 内联图片:“data:url”,IE不支持、大小有限制,Base64编码增加图片大小使得整体下载量增加
    • 合并脚本和样式表
    • 初始首屏之外的图片资源按需加载
    • 静态资源延迟加载
    • JS、CSS源码压缩:精简js or 混淆;精简css(合并同类、移除不用的类等)
    • 图片大小控制合适
  • DNS查找缓存

  • 减少重定向(重定向:把用户从一个url重新路由到另一个url,最常见301,302;url结尾用\/)

  • 并行请求

  • 压缩:压缩组件用gzip,压缩HTML文档、样式表、脚本,减少响应数据量;Vary:Accept-Encoding
  • 缓存

    • 添加 Expires 头:web服务器使用 Expires 头告诉web客户端它可以使用一个组件的当前副本,直到指定时间为止,它在http响应中发送
    • 配置ETag
    • 缓存Ajax:变量保存AJAX请求结果,每次操作本地变量,不用请求,减少请求次数
    • 使用外部js和css文件以便缓存
  • 按需加载

  • 前端模块化

  • 样式表放在顶部,将脚本放在底部
  • 移除重复脚本
  • 图片预加载
  • 使用内容发布网络 (CDN)

    • 一组分布在不同地理位置的web服务器。CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。用于发布静态内容
    • 优点:缩短响应时间、备份、扩展存储能力、进行缓存、缓和web流量峰值压力
    • 缺点:响应时间受到其他网站影响(所有客户共享web服务器组);无法直接控制组件服务器
  • 代码层面:

    • 避免使用css表达式,避免使用高级选择器,通配选择器。
    • 少用全局变量、缓存DOM节点查找的结果。减少IO读取操作。
    • 用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能。
    • 当需要设置的样式很多时设置className而不是直接操作style,使用classList代替className(aDiv.classList.add()\remove()),classList的add和remove,浏览器会进行样式名是否存在的判断,以减少重复的rendering
    • 避免全局查询
    • 避免使用width(width会创建自己的作用域,会增加作用域链长度)

移动端性能优化:

尽量使用css3动画,开启硬件加速。适当使用touch事件代替click事件。避免使用css3渐变阴影效果。

减少 JavaScript 对性能的影响有以下几种方法:

  • 将所有的<script>标签放到页面底部,也就是</body>闭合标签之前,这能确保在脚本执行前页面已经完成了渲染。
  • 尽可能地合并脚本。页面中的<script>标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。

网站重构的理解?

网站重构:在不改变外部行为的前提下,简化结构、添加可读性,而在网站前端保持一致的行为。也就是说是在不改变UI的情况下,对网站进行优化,在扩展的同时保持一致的UI。

对于传统的网站来说重构通常是:

  • 表格(table)布局改为DIV+CSS
  • 使网站前端兼容于现代浏览器(针对于不合规范的CSS、如对IE6有效的)
  • 对于移动平台的优化
  • 针对于SEO进行优化

深层次的网站重构应该考虑的方面:

  • 减少代码间的耦合
  • 让代码保持弹性
  • 严格按规范编写代码
  • 设计可扩展的API
  • 代替旧有的框架、语言(如VB)
  • 增强用户体验

通常来说对于速度的优化也包含在重构中

  • 压缩JS、CSS、image等前端资源(通常是由服务器来解决)
  • 程序的性能优化(如数据读写)
  • 采用CDN来加速资源加载
  • 对于JS DOM的优化
  • HTTP服务器的文件缓存

浏览器怎么运作的(url之后,http,渲染)

1、浏览器会开启一个线程来处理这个请求,对 URL 分析判断如果是 http 协议就按照 Web 方式来处理;
2、调用浏览器内核中的对应方法,比如 WebView 中的 loadUrl 方法;
3、通过DNS解析获取网址的IP地址,设置 UA 等信息发出第二个GET请求;
4、进行HTTP协议会话,客户端发送报头(请求报头);
5、进入到web服务器上的 Web Server,如 Apache、Tomcat、Node.JS 等服务器;
6、进入部署好的后端应用,如 PHP、Java、JavaScript、Python 等,找到对应的请求处理;
7、处理结束回馈报头,此处如果浏览器访问过,缓存上有对应资源,会与服务器最后修改时间对比,一致则返回304;
8、浏览器开始下载html文档(响应报头,状态码200),同时使用缓存;
9、文档树建立,根据标记请求所需指定MIME类型的文件(比如css、js),同时设置了cookie;
10、页面开始渲染DOM,JS根据DOM API操作DOM,执行事件绑定等,页面显示完成。

简洁版:

  查找浏览器缓存
  DNS解析、查找该域名对应的IP地址、重定向(301)、发出第二个GET请求
  进行HTTP协议会话
  客户端发送报头(请求报头)
  服务器回馈报头(响应报头)
  html文档开始下载
  浏览器对加载到的资源(HTML、JS、CSS等)进行语法解析,建立相应的内部数据结构(如HTML的DOM)
  载入解析到的资源文件,渲染页面,完成。

输入url之后的连接

  • 浏览器从URL 中解析出服务器的主机名,并将服务器的主机名转换成服务器的IP 地址(先查找浏览器是否缓存了url,若有则不需要再DNS解析)
  • 浏览器将端口号(如果有的话)从URL 中解析出来;
  • 浏览器建立一条与Web 服务器的TCP 连接;
  • 浏览器向服务器发送一条HTTP 请求报文;

    • Web浏览器向Web服务器发送请求命令:一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令(例如:GET\/sample\/hello.jsp HTTP\/1.1)
    • Web浏览器发送请求头信息:浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
  • 服务器向浏览器回送一条HTTP 响应报文(包括状态码如HTTP\/1.1 200 OK 、头和响应体),处理结束回馈报头,200的HTTP响应状态表示一个正确的响应,下载html文档;此处如果浏览器访问过,缓存上有对应资源,会与服务器最后修改时间对比,一致则返回304;

  • Web服务器向浏览器发送数据:Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。

  • Web服务器关闭TCP连接:若connection 模式为close,则服务器主动关闭TCP 连接,客户端被动关闭连接,释放TCP 连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求

客户端向服务器发送http请求的过程?

  1. 建立TCP连接:在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP\/IP协议族,因此Internet又被称作是TCP\/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能进行更高层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80。
  2. Web浏览器向Web服务器发送请求命令:一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET\/sample\/hello.jsp HTTP\/1.1
  3. Web浏览器发送请求头信息:浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
  4. Web服务器应答:客户机向服务器发出请求后,服务器会客户机回送应答, HTTP\/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
  5. Web服务器发送应答头信息:正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档
  6. Web服务器向浏览器发送数据:Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
  7. Web服务器关闭TCP连接:若connection 模式为close,则服务器主动关闭TCP 连接,客户端被动关闭连接,释放TCP 连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求; TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

TCP 连接握手需要经过以下几个步骤。

  • (1) 请求新的TCP 连接时,客户端要向服务器发送一个小的TCP 分组(通常是40 ~60 个字节)。这个分组中设置了一个特殊的SYN 标记,说明这是一个连接请求。
  • (2) 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP 分组,这个分组中的SYN 和ACK 标记都被置位,说明连接请求已被接受
  • (3) 最后,客户端向服务器回送一条ACK确认信息,通知它连接已成功建立.现代的TCP 栈都允许客户端在这个确认分组中发送数据。

TCP四次挥手:

  • 第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
  • 第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
  • 第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
  • 第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

http状态码有那些?分别代表是什么意思?

  • 1字头:消息。这一类型的状态码,代表请求已被接受,需要继续处理。

    • 100 Continue 服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。
    • 101 Switching Protocols 服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。
  • 2字头:成功。这一类型的状态码,代表请求已成功被服务器接收、理解、并接受。

    • 200 OK 请求成功(其后是对GET和POST请求的应答文档。)
    • 201 Created 请求被创建完成,同时新的资源被创建。
    • 202 Accepted 供处理的请求已被接受,但是处理未完成。
    • 203 Non-authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝。
    • 204 No Content 没有新文档。浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
    • 205 Reset Content 没有新文档。但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容。
    • 206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完成了它。
  • 3字头:重定向。这类状态码代表需要客户端采取进一步的操作才能完成请求。

    • 300 Multiple Choices 多重选择。链接列表。用户可以选择某链接到达目的地。最多允许五个地址。
    • 301 Moved Permanently 所请求的页面已经转移至新的url。
    • 302 Found 所请求的页面已经临时转移至新的url。
    • 303 See Other 所请求的页面可在别的url下被找到。
    • 304 Not Modified 未按预期修改文档。客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
    • 305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取。
    • 306 Unused 此代码被用于前一版本。目前已不再使用,但是代码依然被保留。
    • 307 Temporary Redirect 被请求的页面已经临时移至新的url。
  • 4字头:客户端错误。这类状态码代表了客户端看起来可能发生错误,妨碍了服务器的处理。

    • 400 Bad Request 服务器未能理解请求。
    • 401 Unauthorized 被请求的页面需要用户名和密码。
    • 402 Payment Required 此代码尚无法使用。
    • 403 Forbidden 对被请求页面的访问被禁止。
    • 404 Not Found 服务器无法找到被请求的页面。
    • 405 Method Not Allowed 请求中指定的方法不被允许。
    • 406 Not Acceptable 服务器生成的响应无法被客户端所接受。
    • 407 Proxy Authentication Required 用户必须首先使用代理服务器进行验证,这样请求才会被处理。
    • 408 Request Timeout 请求超出了服务器的等待时间。
    • 409 Conflict 由于冲突,请求无法被完成。
    • 410 Gone 被请求的页面不可用。
    • 411 Length Required "Content-Length" 未被定义。如果无此内容,服务器不会接受请求。
    • 412 Precondition Failed 请求中的前提条件被服务器评估为失败。
    • 413 Request Entity Too Large 由于所请求的实体的太大,服务器不会接受请求。
    • 414 Request-url Too Long 由于url太长,服务器不会接受请求。当post请求被转换为带有很长的查询信息的get请求时,就会发生这种情况。
    • 415 Unsupported Media Type 由于媒介类型不被支持,服务器不会接受请求。
    • 416 服务器不能满足客户在请求中指定的Range头。
    • 417 Expectation Failed
  • 5字头:服务器错误。这类状态码代表了服务器在处理请求的过程中有错误或者异常状态发生

    • 500 Internal Server Error 请求未完成。服务器遇到不可预知的情况。
    • 501 Not Implemented 请求未完成。服务器不支持所请求的功能。
    • 502 Bad Gateway 请求未完成。服务器从上游服务器收到一个无效的响应。
    • 503 Service Unavailable 请求未完成。服务器临时过载或当机。
    • 504 Gateway Timeout 网关超时。
    • 505 HTTP Version Not Supported 服务器不支持请求中指明的HTTP协议版本。
  • 你能讲讲304缓存的原理吗

服务器首先产生ETag,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。

客户端请求一个页面(A)。 服务器返回页面A,并在给A加上一个ETag。 客户端展现该页面,并将页面连同ETag一起缓存。 客户再次请求页面A,并将上次请求时服务器返回的ETag一起传递给服务器。 服务器检查该ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304(未修改——Not Modified)和一个空的响应体。

当发送一个服务器请求时,浏览器首先会进行缓存过期判断。浏览器根据缓存过期时间判断缓存文件是否过期。

情景一:若没有过期,则不向服务器发送请求,直接使用缓存中的结果,此时我们在浏览器控制台中可以看到 200 OK(from cache) ,此时的情况就是完全使用缓存,浏览器和服务器没有任何交互的。

情景二:若已过期,则向服务器发送请求,此时请求中会带上①中设置的文件修改时间,和Etag

然后,进行资源更新判断。服务器根据浏览器传过来的文件修改时间,判断自浏览器上一次请求之后,文件是不是没有被修改过;根据Etag,判断文件内容自上一次请求之后,有没有发生变化

情形一:若两种判断的结论都是文件没有被修改过,则服务器就不给浏览器发index.html的内容了,直接告诉它,文件没有被修改过,你用你那边的缓存吧—— 304 Not Modified,此时浏览器就会从本地缓存中获取index.html的内容。此时的情况叫协议缓存,浏览器和服务器之间有一次请求交互。

情形二:若修改时间和文件内容判断有任意一个没有通过,则服务器会受理此次请求,之后的操作同①

① 只有get请求会被缓存,post请求不会

  • 对于文件的请求,有时候http的请求效率会很慢,怎么办呢?

    balabla 那就使用文件合并了,比如将多个小文件合并成大文件,或者将该嵌入的内容嵌入到html,css sprites等等,减少http请求数

ETag应用:

Etag由服务器端生成,客户端通过If-Match或者说If-None-Match这个条件判断请求来验证资源是否修改。常见的是使用If-None-Match。请求一个文件的流程可能如下:

====第一次请求===

1.客户端发起 HTTP GET 请求一个文件;

2.服务器处理请求,返回文件内容和一堆Header,当然包括Etag(例如"2e681a-6-5d044840")(假设服务器支持Etag生成和已经开启了Etag).状态码200 ====第二次请求===

客户端发起 HTTP GET 请求一个文件,注意这个时候客户端同时发送一个If-None-Match头,这个头的内容就是第一次请求时服务器返回的Etag:2e681a-6-5d0448402.服务器判断发送过来的Etag和计算出来的Etag匹配,因此If-None-Match为False,不返回200,返回304,客户端继续使用本地缓存;流程很简单,问题是,如果服务器又设置了Cache-Control:max-age和Expires呢,怎么办 答案是同时使用,也就是说在完全匹配If-Modified-Since和If-None-Match即检查完修改时间和Etag之后,服务器才能返回304.(不要陷入到底使用谁的问题怪圈)

为什么使用Etag请求头?Etag 主要为了解决 Last-Modified 无法解决的一些问题。

HTTP和HTTPS

HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。

默认HTTP的端口号为80,HTTPS的端口号为443。

为什么HTTPS安全

因为网络请求需要中间有很多的服务器路由器的转发。中间的节点都可能篡改信息,而如果使用HTTPS,密钥在你和终点站才有。https之所以比http安全,是因为他利用ssl\/tls协议传输。它包含证书,卸载,流量转发,负载均衡,页面适配,浏览器适配,refer传递等。保障了传输过程的安全性

  • 是否了解公钥加密和私钥加密。

一般情况下是指私钥用于对数据进行签名,公钥用于对签名进行验证; HTTP网站在浏览器端用公钥加密敏感数据,然后在服务器端再用私钥解密。

浏览器下载文档后如何显示文档

简单来说,浏览器会解析HTML生成DOM Tree,其次会根据CSS生成CSS Rule Tree,而javascript又可以根据DOM API操作DOM,执行事件绑定等,页面显示完成。

文档树建立,根据标记请求所需指定MIME类型的文件(比如css、js),同时设置了cookie。

  • 浏览器的内核分别是什么?

    • Trident(i发ai音)内核:IE,MaxThon,TT,The World,360,搜狗浏览器等。[又称MSHTML]
    • Gecko内核(给扣):Firefox,Netscape6及以上版本,MozillaSuite\/SeaMonkey等
    • Presto内核(普鲁斯特):Opera7及以上。 [Opera内核原为:Presto,现为:Blink;]
    • Webkit内核:Safari,Chrome等。 [ Chrome的:Blink(WebKit的分支)]
  • 介绍一下你对浏览器内核的理解?

通常所谓的浏览器内核也就是浏览器所采用的渲染引擎,渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息。负责取得网页的内容(HTML、XML、图像等等)、整理讯息(例如加入CSS等),以及计算网页的显示方式,然后会输出至显示器或打印机。浏览器内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。所有网页浏览器、电子邮件客户端以及其它需要编辑、显示网络内容的应用程序都需要内核。

  • 浏览器如何渲染网页的?

解析HTML;构建DOM树并解析css样式;DOM树与CSS样式进行附着构造渲染树;布局;绘制

渲染引擎一开始会从网络层获取请求文档的内容,内容的大小一般限制在 8000 个块以内。

渲染引擎将开始解析 HTML 文档,并将各标记逐个转化成“内容树”上的 DOM 节点。同时也会解析外部 CSS 文件以及样式元素中的样式数据。HTML 中这些带有视觉指令的样式信息将用于创建另一个树结构:渲染树。渲染树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。

渲染树构建完毕之后,进入“布局”处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。下一个阶段是绘制 —— 渲染引擎会遍历渲染树,由用户界面后端层将每个节点绘制出来。

需要着重指出的是,这是一个渐进的过程。为达到更好的用户体验,渲染引擎会力求尽快将内容显示在屏幕上。它不必等到整个 HTML 文档解析完毕之后,就会开始构建渲染树和设置布局。在不断接收和处理来自网络的其余内容的同时,渲染引擎会将部分内容解析并显示出来。

其实浏览器加载显示html的顺序是按下面的顺序进行的:

  1. IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的。
  2. 在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)。
  3. 如果遇到语义解释性的标签嵌入文件(JS脚本,CSS下载过程会启用单独连接进行下载。
  4. 并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载。
  5. 样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。
  6. JS、CSS中如有重定义,后定义函数将覆盖前定义函数。

Firefox处理下载和渲染顺序大体相同,只是在细微之处有些差别,例如:iframe的渲染。

JS的加载

不能并行下载和解析(阻塞下载)

网络的模型是同步的。网页作者希望解析器遇到<script>标记时立即解析并执行脚本。文档的解析将停止,直到脚本执行完毕。如果脚本是外部的,那么解析过程会停止,直到从网络同步抓取资源完成后再继续。此模型已经使用了多年,也在 HTML4 和 HTML5 规范中进行了指定。作者也可以将脚本标注为“defer”,这样它就不会停止文档解析,而是等到解析结束才执行。HTML5 增加了一个选项,可将脚本标记为异步,以便由其他线程解析和执行。

样式表

另一方面,样式表有着不同的模型。理论上来说,应用样式表不会更改 DOM 树,因此似乎没有必要等待样式表并停止文档解析。但这涉及到一个问题,就是脚本在文档解析阶段会请求样式信息。如果当时还没有加载和解析样式,脚本就会获得错误的回复,这样显然会产生很多问题。这看上去是一个非典型案例,但事实上非常普遍。Firefox 在样式表加载和解析的过程中,会禁止所有脚本。而对于 WebKit 而言,仅当脚本尝试访问的样式属性可能受尚未加载的样式表影响时,它才会禁止该脚本。

  • 渲染页面和js解析是同时进行吗?

页面加载的时候,遇到外部css文件,浏览器另外发出一个请求,来获取css文件;遇到图片资源,浏览器也会另外发出一个请求,获取图片资源;这是异步请求。并不会影响html文档的加载,但是当文档加载过程中遇到外部js文件,html文档会挂起渲染的线程,不仅要等js文件加载完成,还要等待解析执行完毕,才可以恢复html文档的渲染。

原因:执行js可能会修改DOM;这意味着在js执行完之前,后续所有资源的加载都是没有必要的,这是js阻塞后续资源加载的根本原因。

解决办法:①可以将js文件放在之前。②js文件可以用window.onload事件来触发

  • 样式表和脚本加载问题?

1)外部样式会阻塞后续脚本执行,直到外部样式加载并解析完毕。

2)外部样式不会阻塞后续外部脚本的加载,但会阻塞外部脚本的执行。

3)如果后续外部脚本含有async属性(IE下为defer),则外部样式不会阻塞该脚本的加载与执行

4)对于js动态创建的link标签不会阻塞其后动态创建的script的加载与执行,不管script标签是否具有async属性,但对于其他非动态创建的script,以上三条结论仍适用

http:\/\/www.html5rocks.com\/zh\/tutorials\/internals\/howbrowserswork\/#Themainflow

采用无阻塞下载 JavaScript 脚本的方法:

  1. 使用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5 以上版本),还有async属性
  2. 使用动态创建的<script>元素来下载并执行代码;
  3. 使用 XHR 对象下载 JavaScript 代码并注入页面中。下载之后可以不立即执行但是不能跨域。

.

  • 什么是渐进式渲染?
    • 尽快渲染展示内容的技术
    • 比如 lazy loading of images,当图片要展示的时候js再加载它而不是在页面加载的时候就把全部图片加载完
    • 比如 prioritizing visible content,仅包括页面需要的最少的必要css\/内容\/脚本 让页面尽快在浏览器渲染展示出来,然后利用 defer的js来加载其他资源和内容

网络分层七层和五层模型

  • 应用层:应用层、表示层、会话层(从上往下)(HTTP、FTP、SMTP、DNS)
    • 应用层:允许访问OSI环境的手段(应用协议数据单元APDU),提供为应用软件而设的界面,以设置与另一应用软件之间的通信。例如: HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。
    • 表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU),把数据转换为能与接收者的系统格式兼容并适合传输的格式。
    • 会话层:建立、管理和终止会话(会话协议数据单元SPDU),负责在数据传输中设置和维护电脑网络中两台电脑之间的通信连接。
    • 传输层(TCP和UDP):提供端到端的可靠报文传递和错误恢复(段Segment),把传输表头(TH)加至数据以形成数据包。传输表头包含了所使用的协议等发送信息。例如:传输控制协议义(TCP) 等。
    • 网络层(IP)物理和数据链路层(以太网):负责数据包从源到宿的传递和网际互连(包PackeT),决定数据的路径选择和转寄,它网络表头(NH)加至数据包,以形成分组。网络表头包含了网络数据。例如:互联网协议(IP) 等。
    • 数据链路层:将比特组装成帧和点到点的传递(帧Frame),负责网络寻址、错误侦测和改错。当表头和表尾被加至数据包时,会形成了帧。数据链表头(DLH)是包含了物理地址和错误侦测及改错的方法。数据链表尾(DLT)是一串指示数据包末端的字符串。例如以太网、无线局域网(Wi-Fi)和通用分组无线服务(GPRS)等。
    • 物理层:通过媒介传输比特,确定机械及电气规范(比特Bit),在局部局域网上传送帧,它负责管理电脑通信设备和网络媒体之间的互通。包括了针脚、电压、线缆规范、集线器、中继器、网卡、主机适配器等

各种协议

  • ICMP协议: 因特网控制报文协议。它是TCP\/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。
  • TFTP协议: 是TCP\/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。
  • HTTP协议: 超文本传输协议,是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。
  • DHCP协议: 动态主机配置协议,是一种让系统得以连接到网络上,并获取所需要的配置参数手段。