当前位置:主页 > 查看内容

面试克星!吐血整理前端面试要点难点基础版

发布时间:2021-04-07 00:00| 位朋友查看

简介:前言:恰逢准备找新工作,整理个人学习以及在大厂面试中汇总的基础要点难点,覆盖从计算机基础到框架上层应用,随着前端知识覆盖面越来越广,很有必要在面试前将基础打好,攻克层层面试,仍在更新中,进阶难点版后期更新新文章,祝各位收割 offer。 github……
前言:恰逢准备找新工作,整理个人学习以及在大厂面试中汇总的基础要点难点,覆盖从计算机基础到框架上层应用,随着前端知识覆盖面越来越广,很有必要在面试前将基础打好,攻克层层面试,仍在更新中,进阶难点版后期更新新文章,祝各位收割 offer。

github 可收藏查看

零.计算机基础

1.线程与进程的区别

进程是资源分配的单位,而线程是系统调度的单位。进程的引入大大地提高了资源的利用率和系统的吞吐量,而引入线程的目的是为了减少程序并发所付出的系统开销。

  1. 一个程序至少有一个进程,一个进程至少有一个线程
  2. 线程的划分尺度小于进程,使得多线程程序的并发性高
  3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率
  4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制
  5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别

操作系统中常见的进程调度策略有哪几种:

FCFS(先来先服务),优先级,时间片轮转,多队列、多级反馈队列。

进程间的通信如何实现?

现在最常见的进程间通信的方式有:信号,信号量,消息队列,共享内存,管道。

信号是使用信号处理器来进行的,信号量是使用P、V操作来实现的。消息队列是比较高级的一种进程间通信方法,因为它真的可以在进程间传送消息。

2.常见的设计模式

  • 单例模式

单例模式的定义是产生一个类的唯一实例,但js本身是一种“无类”语言。

如创建遮罩层:

var createMask = function(){
??var mask;
??return function(){
?????? return mask || ( mask = document.body.appendChild( document.createElement('div') ) )
??}
}()
  • 工厂模式

简单工厂模式是由一个方法来决定到底要创建哪个类的实例, 而这些实例经常都拥有相同的接口. 这种模式主要用在所实例化的类型在编译期并不能确定, 而是在执行期决定的情况

  • 观察者模式

观察者模式( 又叫发布者-订阅者模式 )应该是最常用的模式之一. 在很多语言里都得到大量应用. 包括我们平时接触的dom事件. 也是js和dom之间实现的一种观察者模式.观察者模式可以很好的实现2个模块之间的解耦。

应用: 事件总线EventBus

div.onclick??=??function click (){
?? alert ( ''click' )
}
  • 适配器模式

适配器模式的作用很像一个转接口. 本来iphone的充电器是不能直接插在电脑机箱上的, 而通过一个usb转接口就可以了.

在程序里适配器模式也经常用来适配2个接口, 比如你现在正在用一个自定义的js库. 里面有个根据id获取节点的方法$id(). 有天你觉得jquery里的$实现得更酷, 但你又不想让你的工程师去学习新的库和语法. 那一个适配器就能让你完成这件事情.

  • 装饰者模式

装饰者模式是一种为函数或类增添特性的技术,它可以让我们在不修改原来对象的基础上,为其增添新的能力和行为。它本质上也是一个函数。

场景:

  1. 如果你需要为类增添特性或职责,可是从类派生子类的解决方法并不太现实的情况下,就应该使用装饰者模式。
  2. 在例子中,我们并没有对原来的Bicycle基类进行修改,因此也不会对原有的代码产生副作用。我们只是在原有的基础上增添了一些功能。因此,如果想为对象增添特性又不想改变使用该对象的代码的话,则可以采用装饰者模式。

例子:

1.防抖节流函数

2.装饰者模式除了可以应用在类上之外,还可以应用在函数上(其实这就是高阶函数)。比如,我们想测量函数的执行时间,那么我可以写这么一个装饰器。

代理模式的定义是把对一个对象的访问, 交给另一个代理对象来操作.

  • 桥接模式

桥接模式的作用在于将实现部分和抽象部分分离开来, 以便两者可以独立的变化。在实现api的时候, 桥接模式特别有用。比如最开始的singleton的例子.

var singleton = function( fn ){
????var result;
????return function(){
????????return result || ( result = fn .apply( this, arguments ) );
????}
}
var createMask = singleton( function(){
  return document.body.appendChild( document.createElement('div') );
})

另外一个常见的例子就是forEach函数的实现, 用来迭代一个数组.
可以看到, forEach函数并不关心fn里面的具体实现. fn里面的逻辑也不会被forEach函数的改写影响.

forEach = function( ary, fn ){
??for ( var i = 0, l = ary.length; i < l; i++ ){
????var c = ary[ i ];
????if ( fn.call( c, i, c ) === false ){
??????return false;
????}
?? }
}
  • 外观模式

外观模式提供一个高层接口,这个接口使得客户端或子系统更加方便调用。

var stopEvent = function( e ){?? //同时阻止事件默认行为和冒泡
??e.stopPropagation();
??e.preventDefault();
}
  • 访问者模式

GOF官方定义: 访问者模式是表示一个作用于某个对象结构中的各元素的操作。它使可以在不改变各元素的类的前提下定义作用于这些元素的新操作。我们在使用一些操作对不同的对象进行处理时,往往会根据不同的对象选择不同的处理方法和过程。在实际的代码过程中,我们可以发现,如果让所有的操作分散到各个对象中,整个系统会变得难以维护和修改。且增加新的操作通常都要重新编译所有的类。因此,为了解决这个问题,我们可以将每一个类中的相关操作提取出来,包装成一个独立的对象,这个对象我们就称为访问者(Visitor)。利用访问者,对访问的元素进行某些操作时,只需将此对象作为参数传递给当前访问者,然后,访问者会依据被访问者的具体信息,进行相关的操作。

  • 策略模式

策略模式的意义是定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。

  • 中介者模式

中介者对象可以让各个对象之间不需要显示的相互引用,从而使其耦合松散,而且可以独立的改变它们之间的交互。

  • 迭代器模式

迭代器模式提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该方法中的内部表示。

js中我们经常会封装一个each函数用来实现迭代器。

  • 组合模式

组合模式又叫部分-整体模式,它将所有对象组合成树形结构。使得用户只需要操作最上层的接口,就可以对所有成员做相同的操作。

  • 备忘录模式

备忘录模式在js中经常用于数据缓存. 比如一个分页控件, 从服务器获得某一页的数据后可以存入缓存。以后再翻回这一页的时候,可以直接使用缓存里的数据而无需再次请求服务器。

3.HTTP/HTTPS,HTTP 状态码,HTTP缓存方案

HTTP

互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。

HTTPS

是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。

区别

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。

2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。

4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

HTTPS工作原理

客户端发起HTTPS请求->服务端配置(采用HTTPS协议的服务器必须要有一套自己的的数据证书) ->传送证书(相当于公钥) ->客户端解析证书(由客户端TLS完成,验证公钥是否有效,如没有问题就生成一个随机值,然后用证书加密) ->传送加密信息(传送用证书加密后的随机值,相当于客户端与服务端通信的钥匙)->服务端解密信息(服务端用私钥解密随机值,并对请求内容通过该值进行对称加密) ->传输加密后的信息->客户端解密信息(客户端用之前生成的私钥解密服务端传回来的信息)

建立连接过程

  • HTTP使用TCP三次握手建立连接,客户端和服务器需要交换3个包
  • HTTPS除了TCP的三个包,还要加上ssl握手需要的9个包,所以一共是12个包。

HTTPS 优点:

1.SEO方面

  • Google搜索引擎中对于采用HTTPS加密的网站在搜索结果的排名将会更高。

2.安全性

  • 尽管HTTPS并非绝对安全,掌握根证书的机构、掌握加密算法的组织同样可以进行中间人形式的攻击,但HTTPS仍是现行架构下最安全的解决方案,主要有以下几个好处:

(1)、使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;

(2)、HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。

(3)、HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。

HTTPS的缺点

1.SEO方面

  • 使用HTTPS协议会使页面的加载时间延长近50%,增加10%到20%的耗电,此外,HTTPS协议还会影响缓存,增加数据开销和功耗,甚至已有安全措施也会受到影响也会因此而受到影响。
  • 而且HTTPS协议的加密范围也比较有限,在黑客攻击、拒绝服务攻击、服务器劫持等方面几乎起不到什么作用。

2.经济方面

(1)、SSL证书需要钱,功能越强大的证书费用越高,个人网站、小网站没有必要一般不会用。

(2)、SSL证书通常需要绑定IP,不能在同一IP上绑定多个域名,IPv4资源不可能支撑这个消耗(SSL有扩展可以部分解决这个问题,但是比较麻烦,而且要求浏览器、操作系统支持,Windows XP就不支持这个扩展,考虑到XP的装机量,这个特性几乎没用)。

(3)、HTTPS连接缓存不如HTTP高效,大流量网站如非必要也不会采用,流量成本太高。

(4)、HTTPS连接服务器端资源占用高很多,支持访客稍多的网站需要投入更大的成本,如果全部采用HTTPS,基于大部分计算资源闲置的假设的VPS的平均成本会上去。

(5)、HTTPS协议握手阶段比较费时,对网站的相应速度有负面影响,如非必要,没有理由牺牲用户体验

4.HTTP 状态码

100? Continue? 继续,一般在发送post请求时,已发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息

200? OK? ?正常返回信息

201? Created? 请求成功并且服务器创建了新的资源

202? Accepted? 服务器已接受请求,但尚未处理

301? Moved Permanently? 请求的网页已永久移动到新位置。

302 Found? 临时性重定向。

303 See Other? 临时性重定向,且总是使用 GET 请求新的 URI。

304? Not Modified? 自从上次请求后,请求的网页未修改过。

400 Bad Request? 服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求。

401 Unauthorized? 请求未授权。

403 Forbidden? 禁止访问。

404 Not Found? 找不到如何与 URI 相匹配的资源。

500 Internal Server Error? 最常见的服务器端错误。

503 Service Unavailable 服务器端暂时无法处理请求(可能是过载或维护)。

5.HTTP缓存方案

关于缓存的使用这里介绍两套方案:expires/If-Modified-Since、Cache-Control/Etag;前者是HTTP1.0中的缓存方案,后者是HTTP1.1中缓存方案,若http头部中同时出现二者,后者的优先级更高。

Expires 和 max-age 都可以用来指定文档的过期时间,但是二者有一些细微差别

  1. Expires在HTTP/1.0中已经定义,Cache-Control:max-age在HTTP/1.1中才有定义,为了向下兼容,仅使用max-age不够
  2. Expires指定一个绝对的过期时间(GMT格式),这么做会导致至少2个问题:

2.1客户端和服务器时间不同步导致Expires的配置出现问题。

2.2很容易在配置后忘记具体的过期时间,导致过期来临出现浪涌现象

  1. max-age 指定的是从文档被访问后的存活时间,这个时间是个相对值(比如:3600s),相对的是文档第一次被请求时服务器记录的Request\_time(请求时间)
  2. Expires 指定的时间可以是相对文件的最后访问时间(Atime)或者修改时间(MTime),而max-age相对对的是文档的请求时间(Atime)
  3. 如果值为 no-cache,那么每次都会访问服务器。如果值为max-age,则在过期之前不会重复访问服务器。

它分为强缓存和协商缓存:

1)浏览器在加载资源时,先根据这个资源的一些http header判断它是否命中强缓存,强缓存如果命中,浏览器直接从自己的缓存中读取资源,不会发请求到服务器。比如某个css文件,如果浏览器在加载它所在的网页时,这个css文件的缓存配置命中了强缓存,浏览器就直接从缓存中加载这个css,连请求都不会发送到网页所在服务器;

2)当强缓存没有命中的时候,浏览器一定会发送一个请求到服务器,通过服务器端依据资源的另外一些http header验证这个资源是否命中协商缓存,如果协商缓存命中,服务器会将这个请求返回,但是不会返回这个资源的数据,而是告诉客户端可以直接从缓存中加载这个资源,于是浏览器就又会从自己的缓存中去加载这个资源;

3)强缓存与协商缓存的共同点是:如果命中,都是从客户端缓存中加载资源,而不是从服务器加载资源数据;区别是:强缓存不发请求到服务器,协商缓存会发请求到服务器。

4)当协商缓存也没有命中的时候,浏览器直接从服务器加载资源数据。

强缓存

是利用Expires或者Cache-Control这两个http response header实现的,它们都用来表示资源在客户端缓存的有效期。

缓存原理是:

1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Expires的header,如:

图片

2)浏览器在接收到这个资源后,会把这个资源连同所有response header一起缓存下来(所以缓存命中的请求返回的header并不是来自服务器,而是来自之前缓存的header);

3)浏览器再请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires指定的时间之前,就能命中缓存,否则就不行。

4)如果缓存没有命中,浏览器直接从服务器加载资源时,Expires Header在重新加载的时候会被更新。

Expires是较老的强缓存管理header,由于它是服务器返回的一个绝对时间,在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改下客户端时间,就能影响缓存命中的结果。所以在http1.1的时候,提出了一个新的header,就是Cache-Control,这是一个相对时间,在配置缓存的时候,以秒为单位,用数值表示,如:Cache-Control:max-age=315360000,它的缓存原理是:(与 expires类似)

Cache-Control描述的是一个相对时间,在进行缓存命中的时候,都是利用客户端时间进行判断,所以相比较Expires,Cache-Control的缓存管理更有效,安全一些。

这两个header可以只启用一个,也可以同时启用,当response header中,Expires和Cache-Control同时存在时,Cache-Control优先级高于Expires:

图片

强缓存的应用

强缓存是前端性能优化最有力的工具,没有之一,对于有大量静态资源的网页,一定要利用强缓存,提高响应速度。通常的做法是,为这些静态资源全部配置一个超时时间超长的Expires或Cache-Control,这样用户在访问网页时,只会在第一次加载时从服务器请求静态资源,其它时候只要缓存没有失效并且用户没有强制刷新的条件下都会从自己的缓存中加载,比如前面提到过的京东首页缓存的资源,它的缓存过期时间都设置到了2026年:

然而这种缓存配置方式会带来一个新的问题,就是发布时资源更新的问题,比如某一张图片,在用户访问第一个版本的时候已经缓存到了用户的电脑上,当网站发布新版本,替换了这个图片时,已经访问过第一个版本的用户由于缓存的设置,导致在默认的情况下不会请求服务器最新的图片资源,除非他清掉或禁用缓存或者强制刷新,否则就看不到最新的图片效果。

协商缓存

当浏览器对某个资源的请求没有命中强缓存,就会发一个请求到服务器,验证协商缓存是否命中,如果协商缓存命中,请求响应返回的http状态为304并且会显示一个Not Modified的字符串,比如你打开京东的首页,按f12打开开发者工具,再按f5刷新页面,查看network,可以看到有不少请求就是命中了协商缓存的:

图片

查看单个请求的Response Header,也能看到304的状态码和Not Modified的字符串,只要看到这个就可说明这个资源是命中了协商缓存,然后从客户端缓存中加载的,而不是服务器最新的资源:

图片

协商缓存是利用的是【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】这两对Header来管理的。

**【Last-Modified,If-Modified-Since】**的控制缓存的原理是:

1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上Last-Modified的header,这个header表示这个资源在服务器上的最后修改时间:

图片

2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-Modified-Since的header,这个header的值就是上一次请求时返回的Last-Modified的值:

图片

3)服务器再次收到资源请求时,根据浏览器传过来If-Modified-Since和资源在服务器上的最后修改时间判断资源是否有变化,如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。当服务器返回304 Not Modified的响应时,response header中不会再添加Last-Modified的header,因为既然资源没有变化,那么Last-Modified也就不会改变,这是服务器返回304时的response header:

图片

4)浏览器收到304的响应后,就会从缓存中加载资源。

5)如果协商缓存没有命中,浏览器直接从服务器加载资源时,Last-Modified Header在重新加载的时候会被更新,下次请求时,If-Modified-Since会启用上次返回的Last-Modified值。

【Last-Modified,If-Modified-Since】都是根据服务器时间返回的header,一般来说,在没有调整服务器时间和篡改客户端缓存的情况下,这两个header配合起来管理协商缓存是非常可靠的,但是有时候也会服务器上资源其实有变化,但是最后修改时间却没有变化的情况,而这种问题又很不容易被定位出来,而当这种情况出现的时候,就会影响协商缓存的可靠性。所以就有了另外一对header来管理协商缓存,这对header就是【ETag、If-None-Match】。它们的缓存管理的方式是:

1)浏览器第一次跟服务器请求一个资源,服务器在返回这个资源的同时,在respone的header加上ETag的header,这个header是服务器根据当前请求的资源生成的一个唯一标识,这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间没有关系,所以能很好的补充Last-Modified的问题:

图片

2)浏览器再次跟服务器请求这个资源时,在request的header上加上If-None-Match的header,这个header的值就是上一次请求时返回的ETag的值:

图片

3)服务器再次收到资源请求时,根据浏览器传过来If-None-Match和然后再根据资源生成一个新的ETag,如果这两个值相同就说明资源没有变化,否则就是有变化;如果没有变化则返回304 Not Modified,但是不会返回资源内容;如果有变化,就正常返回资源内容。与Last-Modified不一样的是,当服务器返回304 Not Modified的响应时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag跟之前的没有变化:

图片

4)浏览器收到304的响应后,就会从缓存中加载资源。

协商缓存的管理

协商缓存跟强缓存不一样,强缓存不发请求到服务器,所以有时候资源更新了浏览器还不知道,但是协商缓存会发请求到服务器,所以资源是否更新,服务器肯定知道。大部分web服务器都默认开启协商缓存,而且是同时启用【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】,比如apache:

【Last-Modified,If-Modified-Since】和【ETag、If-None-Match】一般都是同时启用,这是为了处理Last-Modified不可靠的情况。有一种场景需要注意:

分布式系统里多台机器间文件的Last-Modified必须保持一致,以免负载均衡到不同机器导致比对失败;

分布式系统尽量关闭掉ETag(每台机器生成的ETag都会不一样);

协商缓存需要配合强缓存使用,你看前面这个截图中,除了Last-Modified这个header,还有强缓存的相关header,因为如果不启用强缓存的话,协商缓存根本没有意义。

其他:

1)当ctrl+f5强制刷新网页时,直接从服务器加载,跳过强缓存和协商缓存;

2)当f5刷新网页时,跳过强缓存,但是会检查协商缓存;

公司的静态资源优化方案,基本上要实现这么几个东西:

  1. 配置超长时间的本地缓存 —— 节省带宽,提高性能
  2. 采用内容摘要作为缓存更新依据 —— 精确的缓存控制
  3. 静态资源CDN部署 —— 优化网络请求
  4. 更资源发布路径实现非覆盖式发布 —— 平滑升级

6.TCP/UDP,有什么区别,说说三次握手四次挥手

IP是Internet Protocol的简称,是网络层的主要协议,作用是提供不可靠、无连接的数据报传送。

TCP**:**可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。TCP的缺点: 慢,效率低,占用系统资源高,易被攻击

UDP**:**快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。缺点:不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包

常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输

UDP: 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP

TCP传输的三次握手四次挥手策略

为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。用TCP协议把数据包送出去后,TCP不会对传送? ? 后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志:SYN和ACK。

发送端首先发送一个带SYN标志的数据包给对方。接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。

若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。

图片

  1. 服务端进程准备好接收来自外部的 TCP 连接。然后服务端进程处于LISTEN状态,等待客户端连接请求。
  2. 客户端向服务器发出连接请求,请求中首部同步位 SYN = 1,同时选择一个初始序号 sequence ,简写 seq = x。SYN 报文段不允许携带数据,只消耗一个序号。此时,客户端进入SYN-SEND状态。
  3. 服务器收到客户端连接后,,需要确认客户端的报文段。在确认报文段中,把 SYN 和 ACK 位都置为 1 。确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。请注意,这个报文段也不能携带数据,但同样要消耗掉一个序号。此时,TCP 服务器进入SYN-RECEIVED(同步收到)状态。
  4. 客户端在收到服务器发出的响应后,还需要给出确认连接。确认连接中的 ACK 置为 1 ,序号为 seq = x + 1,确认号为 ack = y + 1。TCP 规定,这个报文段可以携带数据也可以不携带数据,如果不携带数据,那么下一个数据报文段的序号仍是 seq = x + 1。这时,客户端进入ESTABLISHED (已连接)状态
  5. 服务器收到客户的确认后,也进入ESTABLISHED状态。

这样三次握手建立连接的阶段就完成了,双方可以直接通信了。

断开一个TCP连接则需要“四次握手”:

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

7.linux 的一些基本命令

企鹅厂比较喜欢问,考察认识的广度。

8.OSI 七层模型

应用层:应用层、表示层、会话层(从上往下)(HTTP、FTP、SMTP、DNS)

传输层(TCP和UDP)

网络层(IP)

物理和数据链路层(以太网)

每一层的作用如下:

物理层:通过媒介传输比特,确定机械及电气规范(比特Bit)RJ45 、 CLOCK 、 IEEE802.3 (中继器,集线器,网关

数据链路层:将比特组装成帧和点到点的传递(帧Frame)PPP 、 FR 、 HDLC 、 VLAN 、 MAC (网桥,交换机)

网络层:负责数据包从源到宿的传递和网际互连(包PackeT)IP 、 ICMP 、 ARP 、 RARP 、 OSPF 、 IPX 、 RIP 、 IGRP 、 (路由器)

传输层:提供端到端的可靠报文传递和错误恢复(段Segment)TCP 、 UDP 、 SPX

会话层:建立、管理和终止会话(会话协议数据单元SPDU)NFS 、 SQL 、 NETBIOS 、 RPC

表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU)JPEG 、 MPEG 、 ASII

应用层:允许访问OSI环境的手段(应用协议数据单元APDU)FTP 、 DNS 、 Telnet 、 SMTP 、 HTTP 、 WWW 、 NFS

图片

各种协议

ICMP协议: 因特网控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。

TFTP协议: 是TCP/IP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议,提供不复杂、开销不大的文件传输服务。

HTTP协议: 超文本传输协议,是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。

DHCP协议: 动态主机配置协议,是一种让系统得以连接到网络上,并获取所需要的配置参数手段。

9.Http2.0

  • 二进制分帧(Binary Format)

流是连接中的一个虚拟信道,可以承载双向消息传输。每个流有唯一整数标识符。为了防止两端流ID冲突,客户端发起的流具有奇数ID,服务器端发起的流具有偶数ID。

在二进制分帧层上,http2.0会将所有传输信息分割为更小的消息和帧,并对它们采用二进制格式的编码将其封装,新增的二进制分帧层同时也能够保证http的各种动词,方法,首部都不受影响,兼容上一代http标准。其中,http1.X中的首部信息header封装到Headers帧中,而request body将被封装到Data帧中。

  • 服务端推ServerPush)

服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。

正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。不过与之相比,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。

当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH\_PROMISE 的 Frame,里面带了 PUSH 需要新建的 Stream ID。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH\_PROMISE 类型,便会准备接收服务端要推送的流。

  • 多路复用 (Multiplexing)

在http1.1中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量的限制,超过限制数目的请求会被阻塞。这也是为何一些站点会有多个静态资源 CDN 域名的原因之一。

而http2.0中的多路复用优化了这一性能。多路复用允许同时通过单一的http/2 连接发起多重的请求-响应消息。有了新的分帧机制后,http/2 不再依赖多个TCP连接去实现多流并行了。每个数据流都拆分成很多互不依赖的帧,而这些帧可以交错(乱序发送),还可以分优先级,最后再在另一端把它们重新组合起来。

http 2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接(每个域名一个连接)即可。http2连接可以承载数十或数百个流的复用,多路复用意味着来自很多流的数据包能够混合在一起通过同样连接传输。当到达终点时,再根据不同帧首部的流标识符重新连接将不同的数据流进行组装。

  • 头部压缩(HeaderC****ompression)

http1.x的头带有大量信息,而且每次都要重复发送。http/2使用encoder来减少需要传输的header大小,通讯双方各自缓存一份头部字段表,既避免了重复header的传输,又减小了需要传输的大小。

事实上,如果请求中不包含首部(例如对同一资源的轮询请求),那么,首部开销就是零字节,此时所有首部都自动使用之前请求发送的首部。

如果首部发生了变化,则只需将变化的部分加入到header帧中,改变的部分会加入到头部字段表中,首部表在 http 2.0 的连接存续期内始终存在,由客户端和服务器共同渐进地更新。

http/2使用的是专门为首部压缩而设计的HPACK②算法。(http/2的HPACK算法使用一份索引表来定义常用的http Header,把常用的 http Header 存放在表里,请求的时候便只需要发送在表里的索引位置即可。)

  • 请求优先级(Request Priorities)

把http消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。每个流都可以带有一个31比特的优先值:0 表示最高优先级;2的31次方-1 表示最低优先级。

●优先级最高:主要的html

●优先级高:CSS文件

●优先级中:js文件

●优先级低:图片

性能瓶颈

启用http2.0后会给性能带来很大的提升,但同时也会带来新的性能瓶颈。因为现在所有的压力集中在底层一个TCP连接之上,TCP很可能就是下一个性能瓶颈,比如TCP分组的队首阻塞问题,单个TCP packet丢失导致整个连接阻塞,无法逃避,此时所有消息都会受到影响。未来,服务器端针对http 2.0下的TCP配置优化至关重要。

一.JS

1.js 继承

常用六种方法:构造函数继承、原型链继承、组合继承、原型式继承、寄生式继承

  • 1构造函数继承

解决了原型继承中的问题,可以实现多继承.

缺点:

实例并不是父类的实例,只是子类的实例。只能继承父类的实例属性和方法,不能继承原型属性/方法。

无法实现函数复用,每个子类都有父类实例函数的副本,影响性能。

// 使用 call 或 apply 方法,将父对象的构造函数绑定在子对象上。
function Animal(){
  this.species = "动物";
}
function Cat(name,color){
  Animal.apply(this, arguments);
  this.name = name;
  this.color = color;
 }
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物
  • 2原型链继承

缺点:

来自原型对象的引用类属性是所有实例共享的、

创建子类实例时,无法向父类构造函数传参。

Cat.prototype = new Animal(); 
// 将Cat的prototype对象指向一个Animal的实例
Cat.prototype.constructor = Cat; 
// 原本指向Cat的构造函数,受上一句的影响Cat.prototype.constructor指向Animal,而且每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性,这样会导致继承链的絮乱,因此需要手动纠正,将constructor改回为Cat.
var cat1 = new Cat("大毛","黄色");
alert(cat1.species); // 动物

js 原型、原型链概念:原型对象也是普通对象,是对象一个自带的隐式 proto 属性,原型也有可能有自己的原型,如果一个原型对象不为 null 的话,则称之为原型链。原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。

  • 3组合继承
解决上面的问题,**但调用了两次父类构造函数,生成了两份实例,消耗内存**.
function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
function Child (name, age) {
    Parent.call(this, name);

    this.age = age;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child1 = new Child('kevin', '18');
  • 4原型式继承

就是 ES5 Object.create 的模拟实现,将传入的对象作为创建的对象的原型。

缺点:

包含引用类型的属性值始终都会共享相应的值,这点跟原型链继承一样。

function createObj(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
  • 5寄生式继承

缺点:跟借用构造函数模式一样,每次创建对象都会创建一遍方法。

function createObj (o) {
    var clone = Object.create(o);
    clone.sayName = function () {
        console.log('hi');
    }
    return clone;
}
  • 6寄生组合式继承

这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

function Parent (name) {
    this.name = name;
    this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
    console.log(this.name)
}
function Child (name, age) {
    Parent.call(this, name);
    this.age = age;
}
// 关键的三步
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
var child1 = new Child('kevin', '18');
console.log(child1);
// 封装后
function prototype(child, parent) {
    var pt = Object.create(parent.prototype);
    pt.constructor = child;
    child.prototype = pt;
}
// 当我们使用的时候:
prototype(Child, Parent);
  • class 类继承
class Animal {
? ? constructor(name) {
? ? ? ? this.name = name
? ? }?
? ? getName() {
? ? ? ? return this.name
? ? }
}
class Dog extends Animal {
? ? constructor(name, age) {
? ? ? ? super(name)
? ? ? ? this.age = age
? ? }
}

2.new 操作符具体干了什么,关于 this 的理解及指向问题

this 是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。

通常来说,作用域一共有两种主要的工作模型。

  • 词法作用域
  • 动态作用域

而 JavaScript 就是采用的词法作用域,也就是在编程阶段,作用域就已经明确下来了。

在 JavaScript 中,影响 this 指向的绑定规则有四种:

  • 默认绑定
  • 隐式绑定
  • 显式绑定
  • new 绑定

1.默认绑定,这是最直接的一种方式,就是不加任何的修饰符直接调用函数

图片

2.隐式绑定,这种情况会发生在调用位置存在「上下文对象」的情况,如:图片

3.显示绑定

这种就是使用 Function.prototype 中的三个方法 call(), apply(), bind() 了。这三个函数,都可以改变函数的 this 指向到指定的对象,不同之处在于,call() 和 apply() 是立即执行函数,并且接受的参数的形式不同:

bind 与 call、apply 区别:bind返回的是一个函数,需要调用才能执行,call、apply 直接执行

  • call(this, arg1, arg2, …)
  • apply(this, [arg1, arg2, …]) 而 bind() 则是创建一个新的包装函数,并且返回,而不是立刻执行。
  • bind(this, arg1, arg2, …) apply() 接收参数的形式,有助于函数嵌套函数的时候,把 arguments 变量传递到下一层函数中。 4.new 绑定 在 JavaScript 中,所有的函数都可以被 new 调用,这时候这个函数一般会被称为「构造函数」,实际上并不存在所谓「构造函数」,更确切的理解应该是对于函数的「构造调用」。

图片

4.new 绑定

在 JavaScript 中,所有的函数都可以被 new 调用,这时候这个函数一般会被称为「构造函数」,实际上并不存在所谓「构造函数」,更确切的理解应该是对于函数的「构造调用」。

使用 new 来调用函数,会自动执行下面操作:

  1. 创建一个全新的对象。
  2. 将这个对象的\_\_proto\_\_属性指向constructor函数的prototype属性。。
  3. 这个新对象会绑定到函数调用的 this,执行constructor函数。
  4. 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。

图片

**一个例外情况:**当在构造函数返回一个对象时,内部创建出来的新对象就会被返回的对象所覆盖。

function Test(name) {
? this.name = name
? console.log(this) // Test { name: 'yck' }
? return { age: 26 }
}
const t = new Test('yck')
console.log(t) // { age: 26 }
console.log(t.name) // 'undefined'

优先级顺序为:

「new 绑定」 > 「显式绑定」 > 「隐式绑定」 > 「默认绑定。」

所以,this 到底是什么

this?并不是在编写的时候绑定的,而是在运行时绑定的。它的上下文取决于函数调用时的各种条件。

this?的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

当一个函数被调用时,会创建一个「执行上下文」,这个上下文会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this?就是这个记录的一个属性,会在函数执行的过程中用到。

3.ajax 是怎么创建的,优缺点,如何解决跨域问题,解释下 js 同源策略为什么要有,jsonp 怎么防止接口被滥用及安全性问题。

  • ajax创建过程:
  1. 创建 XMLHttpRequest 对象,也就是创建一个异步调用对象
  2. 创建一个新的 HTTP 请求,并指定该 HTTP 请求的方法、URL 及验证信息
  3. 设置响应 HTTP 请求状态变化的函数
  4. 发送 HTTP 请求
  5. 获取异步调用返回的数据
  6. 使用 JavaScript和 DOM 实现局部刷新
var xhr;
if (window.XMLHttpRequest) {
? //? IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
? xhr = new XMLHttpRequest();
}
else {
? // IE6, IE5 浏览器执行代码
? xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.onreadystatechange = function () {
? if (xhr.readyState == 4 && xhr.status == 200) {
? ? document.getElementById("myDiv").innerHTML = xhr.responseText;
? }
}
xhr.open("GET", "/try/ajax/ajax_info.txt", true);
xhr.send();
  • XHR enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码

    • application/x-www-form-urlencodedcontentType:'application/x-www-form-urlencoded;charset=UTF-8' data: {username: "John", password: "Boston" }
    • multipart/form-dataprocessData:false, //是否自动将 data 转换为字符串。 contentType:false, // 发送信息至服务器时内容编码类型,通过设置 false 跳过设置默认值。 var formData = new FormData(document.querySelector("#data2")); (form id=“#data2”) data: formData
    • application/jsoncontentType:'application/json;charset=UTF-8', data: JSON.stringify({username: "John", password: "Boston" })
    • text/plaincontentType:'text/plain;charset=UTF-8', processData:false, //是否自动将 data 转换为字符串。 data: "我是一个纯正的文本功能!\r\n我第二行"
    • text/xmlcontentType:'text/xml;charset=UTF-8',"
      <h1>我是标签
      </h1>我是内容
      "
  • ajax 优点

    • 页面局部刷新,用户体验好。
    • 异步通信,更加快的响应能力。
    • 减少冗余请求,减轻了服务器负担;按需获取数据,节约带宽资源。
    • 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序
  • 缺点

    • ajax干掉了back按钮和加入收藏书签功能,即对浏览器后退机制的破坏。
    • 存在一定的安全问题,AJAX 暴露了与服务器交互的细节。
    • 对搜索引擎的支持比较弱。
    • 破坏了程序的异常机制。
    • 无法用URL直接访问
  • 跨域

1.jsonp 需要目标服务器配合一个callback函数。

2.window.name+iframe 需要目标服务器响应window.name。

3.window.location.hash+iframe 同样需要目标服务器作处理。

4.html5的 postMessage+ifrme 这个也是需要目标服务器或者说是目标页面写一个postMessage,主要侧重于前端通讯。

5.CORS需要服务器设置header :Access-Control-Allow-Origin。

6.nginx反向代理?这个方法一般很少有人提及,但是他可以不用目标服务器配合,不过需要你搭建一个中转nginx服务器,用于转发请求。

  • jsonp

图片

  • js 同源策略:

同源策略是客户端脚本(尤其是Javascript)的重要的安全度量标准。它最早出自Netscape Navigator2.0,其目的是防止某个文档或脚本从多个不同源装载。

这里的同源策略指的是:协议,域名,端口相同,同源策略是一种安全协议,指一段脚本只能读取来自同一来源的窗口和文档的属性。

为什么要有同源限制:

我们举例说明:比如一个黑客程序,他利用Iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。

get post 区别

  • get参数通过url传递,post放在request body中。
  • get请求在url中传递的参数是有长度限制的,而post没有。
  • get比post更不安全,因为参数直接暴露在url中,所以不能用来传递敏感信息。

    • get请求只能进行url编码,而post支持多种编码方式
    • get请求会浏览器主动cache,而post支持多种编码方式。
    • get请求参数会被完整保留在浏览历史记录里,而post中的参数不会被保留。

对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);

而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。

4. js块级作用域在ES5/ES6中的不同实现,js 闭包的作用缺陷

从作用域链讲起

首先明确几个概念:

  • JavaScript有函数级作用域,但没有块级作用域。(ES6之前)
  • 当要使用一个变量时,会沿着作用域链一步一步向上查找。

闭包的特性

闭包的主要特性就是可以从外部访问函数内部的属性和方法。

闭包的用途

利用闭包强大的特性,最方便的用途就是实现私有变量和私有方法;另外,因为使用闭包会在内存中保存函数作用域,因此也能保存变量的值。

闭包导致的问题

  • 因为闭包会使得函数中的变量都保存在内存中,如不能及时释放会对性能造成影响。
  • 在IE9以下的浏览器会有内存泄漏的问题。

js setTimeout 闭包问题,连续打印出12345

图片

5.js 垃圾回收机制,什么情况下会发生内存泄漏

与JS中的垃圾回收机制有关,有两种策略来实现垃圾回收:标记清除引用计数。

引用计数:

**早期的回收算法,就是先记下某个变量被引用的总次数,然后当被引用次数为0时,就表示可回收。**问题在于对于根部不可访问即脱离环境的仍存在相互引用的情况,它们的引用次数不为 0,就不能被回收。

标记清除

  • 垃圾回收器获取根并**“标记”**(记住)它们。
  • 然后它访问并“标记”所有来自它们的引用。
  • 然后它访问标记的对象并标记它们的引用。所有被访问的对象都被记住,以便以后不再访问同一个对象两次。
  • 以此类推,直到有未访问的引用(可以从根访问)为止。
  • 除标记的对象外,所有对象都被删除。

一些优化:

  • 分代回收——对象分为两组:“新对象”和“旧对象”。许多对象出现,完成它们的工作并迅速结 ,它们很快就会被清理干净。那些活得足够久的对象,会变“老”,并且很少接受检查。
  • 增量回收——如果有很多对象,并且我们试图一次遍历并标记整个对象集,那么可能会花费一些时间,并在执行中会有一定的延迟。因此,引擎试图将垃圾回收分解为多个部分。然后,各个部分分别执行。这需要额外的标记来跟踪变化,这样有很多微小的延迟,而不是很大的延迟。
  • 空闲时间收集——垃圾回收器只在 CPU 空闲时运行,以减少对执行的可能影响。

什么是垃圾?

一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。

6.ES6 有哪些新特性,箭头函数和 function 有什么区别

JS箭头函数和function的区别:

  • 箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 箭头函数不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 箭头函数不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用Rest参数代替。
  • 不可以使用yield命令,因此箭头函数不能用作Generator函数。

因为箭头函数没有 this,所以一切试图改变箭头函数this指向都会是无效的,箭头函数的this只取决于定义时的环境。

7.Promise 概念,手写 Promise

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

  • Promise有三种状态pendingresolvedrejected。状态转换只能是pendingresolved或者pendingrejected;状态一旦转换完成,不能再次转换。
  • Promise拥有一个then方法,用以处理resolvedrejected状态下的值。

实现

class Promise {
? // 定义Promise状态变量,初始值为pending
? status = 'pending';
? // 因为在then方法中需要处理Promise成功或失败时的值,所以需要一个全局变量存储这个值
? data = '';
? // Promise resolve时的回调函数集
? onResolvedCallback = [];
? // Promise reject时的回调函数集
? onRejectedCallback = [];
? // Promise构造函数,传入参数为一个可执行的函数
? constructor(executor) {
? ? // resolve函数负责把状态转换为resolved
? ? function resolve(value) {
? ? ? this.status = 'resolved';
? ? ? this.data = value;
? ? ? for (const func of this.onResolvedCallback) {
? ? ? ? func(this.data);
? ? ? }
? ? }
? ? // reject函数负责把状态转换为rejected
? ? function reject(reason) {
? ? ? this.status = 'rejected';
? ? ? this.data = reason;
? ? ? for (const func of this.onRejectedCallback) {
? ? ? ? func(this.data);
? ? ? }
? ? }
? ? // 直接执行executor函数,参数为处理函数resolve, reject。因为executor执行过程有可能会出错,错误情况需要执行reject
? ? try {
? ? ? executor(resolve, reject);
? ? } catch(e) {
? ? ? reject(e)
? ? }
? }
? /**
? ? * 拥有一个then方法
? ? * then方法提供:状态为resolved时的回调函数onResolved,状态为rejected时的回调函数onRejected
? ? * 返回一个新的Promise
? */
? then(onResolved, onRejected) {
? ? // 设置then的默认参数,默认参数实现Promise的值的穿透
? ? onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return e };
? ? onRejected = typeof onRejected === 'function' ? onRejected : function(e) { throw e };
? ? let promise2;
? ? promise2 =? new Promise((resolve, reject) => {
? ? ? // 如果状态为resolved,则执行onResolved
? ? ? if (this.status === 'resolved') {
? ? ? ? setTimeout(() => {
? ? ? ? ? try {
? ? ? ? ? ? // onResolved/onRejected有返回值则把返回值定义为x
? ? ? ? ? ? const x = onResolved(this.data);
? ? ? ? ? ? // 执行[[Resolve]](promise2, x)
? ? ? ? ? ? this.resolvePromise(promise2, x, resolve, reject);
? ? ? ? ? } catch (e) {
? ? ? ? ? ? reject(e);
? ? ? ? ? }
? ? ? ? }, 0);
? ? ? }
? ? ? // 如果状态为rejected,则执行onRejected
? ? ? if (this.status === 'rejected') {
? ? ? ? setTimeout(() => {
? ? ? ? ? try {
? ? ? ? ? ? const x = onRejected(this.data);
? ? ? ? ? ? this.resolvePromise(promise2, x, resolve, reject);
? ? ? ? ? } catch (e) {
? ? ? ? ? ? reject(e);
? ? ? ? ? }
? ? ? ? }, 0);
? ? ? }
? ? ? // 如果状态为pending,则把处理函数进行存储
? ? ? if (this.status = 'pending') {
? ? ? ? this.onResolvedCallback.push(() => {
? ? ? ? ? setTimeout(() => {
? ? ? ? ? ? try {
? ? ? ? ? ? ? const x = onResolved(this.data);
? ? ? ? ? ? ? this.resolvePromise(promise2, x, resolve, reject);
? ? ? ? ? ? } catch (e) {
? ? ? ? ? ? ? reject(e);
? ? ? ? ? ? }
? ? ? ? ? }, 0);
? ? ? ? });
? ? ? ? this.onRejectedCallback.push(() => {
? ? ? ? ? setTimeout(() => {
? ? ? ? ? ? try {
? ? ? ? ? ? ? const x = onRejected(this.data);
? ? ? ? ? ? ? this.resolvePromise(promise2, x, resolve, reject);
? ? ? ? ? ? } catch (e) {
? ? ? ? ? ? ? reject(e);
? ? ? ? ? ? }
? ? ? ? ? }, 0);
? ? ? ? });
? ? ? }
? ? });
? ? return promise2;
? }
? // [[Resolve]](promise2, x)函数
? resolvePromise(promise2, x, resolve, reject) {
?  ?...
   //简易版
   // 如果x仍然为Promise的情况
    if (x instanceof Promise) {
      // 如果x的状态还没有确定,那么它是有可能被一个thenable决定最终状态和值,所以需要继续调用resolvePromise
      if (x.status === 'pending') {
        x.then(function(value) {
          resolvePromise(promise2, value, resolve, reject)
        }, reject)
      } else { 
        // 如果x状态已经确定了,直接取它的状态
        x.then(resolve, reject)
      }
      return
    } else {
      resolve(x)
    }
    ...?
? }

}

核心极简板20行

function Promise(fn) {
? this.cbs = [];
? const resolve = (value) => {
? ? setTimeout(() => {
? ? ? this.data = value;
? ? ? this.cbs.forEach((cb) => cb(value));
? ? });
? }
? fn(resolve);
}
Promise.prototype.then = function (onResolved) {
? return new Promise((resolve) => {
? ? this.cbs.push(() => {
? ? ? const res = onResolved(this.data);
? ? ? if (res instanceof Promise) {
? ? ? ? res.then(resolve);
? ? ? } else {
? ? ? ? resolve(res);
? ? ? }
? ? });
? });
};

Promise的一些静态方法
Promise.deferredPromise.allPromise.racePromise.resolvePromise.reject

  • Promise.all方法,Promise.all方法接收一个promise数组,返回一个新promise2,并发执行数组中的全部promise,所有promise状态都为resolved时,promise2状态为resolved并返回全部promise结果,结果顺序和promise数组顺序一致。如果有一个promiserejected状态,则整个promise2进入rejected状态。
  • Promise.race方法,Promise.race方法接收一个promise数组, 返回一个新promise2,顺序执行数组中的promise,有一个promise状态确定,promise2状态即确定,并且同这个promise的状态一致。
  • Promise.resolve方法/Promise.reject,Promise.resolve用来生成一个rejected完成态的promisePromise.reject用来生成一个rejected失败态的promise
// Promise.all
Promise.all = function(promiseArr) {
? ? let index = 0, result = []
? ? return new Promise((resolve, reject) => {
? ? ? ? promiseArr.forEach((p, i) => {
? ? ? ? ? ? Promise.resolve(p).then(val => {
? ? ? ? ? ? ? ? index++
? ? ? ? ? ? ? ? result[i] = val
? ? ? ? ? ? ? ? if (index === promiseArr.length) {
? ? ? ? ? ? ? ? ? ? resolve(result)
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }, err => {
? ? ? ? ? ? ? ? reject(err)
? ? ? ? ? ? })
? ? ? ? })
? ? })
}

Promise存在哪些缺点。

1、无法取消Promise,一旦新建它就会立即执行,无法中途取消。

2、如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

3、吞掉错误或异常,错误只能顺序处理,即便在Promise链最后添加catch方法,依然可能存在无法捕捉的错误(catch内部可能会出现错误)

4、阅读代码不是一眼可以看懂,你只会看到一堆then,必须自己在then的回调函数里面理清逻辑。

使用Promise进行顺序(sequence)处理。

1、使用async函数配合await或者使用generator函数配合yield

2、使用promise.then通过for循环或者Array.prototype.reduce实现。

function sequenceTasks(tasks) {
? ? function recordValue(results, value) {
? ? ? ? results.push(value);
? ? ? ? return results;
? ? }
? ? var pushValue = recordValue.bind(null, []);
? ? return tasks.reduce(function (promise, task) {
? ? ? ? return promise.then(() => task).then(pushValue);
? ? }, Promise.resolve());
}

Promise链上返回的最后一个Promise出错了怎么办?
catchpromise链式调用的末尾调用,用于捕获链条中的错误信息,但是catch方法内部也可能出现错误,所以有些promise实现中增加了一个方法donedone相当于提供了一个不会出错的catch方法,并且不再返回一个promise,一般用来结束一个promise链。

8.js 事件流模型 ,事件委托

事件代理:就是利用事件冒泡的原理,只指定一个事件处理程序来管理某一类型的所有事件。目的:减少DOM操作,提高性能。原理:事件冒泡中,事件是从最深的节点开始,然后逐步向上传播事件,那么我们给最外面的节点添加事件,那么子节点被触发时(如:cilck)都会冒泡到最外层的节点上,所以都会触发。

事件流****模型:**捕获型事件和冒泡型事件。**调用事件处理阶段-》捕获-》目标-》冒泡

捕获性事件应用场景:适用于作全局范围内的监听,这里的全局是相对的全局,相对于某个顶层结点与该结点所有子孙结点形成的集合范围。图片

当事件捕获和事件冒泡一起存在的情况,事件又是如何触发呢

  • 对于非target节点则先执行捕获在执行冒泡
  • 对于target节点则是先执行先注册的事件,无论冒泡还是捕获

9.requestAnimationFrame、setTimeout、setInterval

requestAnimationFrame 方法让我们可以在下一帧开始时调用指定函数。

以往JS控制的动画大多使用 setInterval 或者 setTimeout 每隔一段时间刷新元素的位置,来达到动画的效果,但是这种方式并不能准确地控制动画帧率,尽管主流的浏览器对于这两个函数实现的动画都有一定的优化,但是这依然无法弥补它们性能问题。主要原因是因为JavaScript的单线程机制使得其可能在有阻塞的情况下无法精确到毫秒触发。

requestAnimationFrame()方法正是为了满足高性能动画的需求而提供的API,通过setInterval方法控制的动画其调用的间隔由程序员设置,而requestAnimationFrame()无须设置调用间隔, 它自动紧跟浏览器的绘制的帧率(一般浏览器的显示帧率是60fps,差不多每帧间隔16.7ms)

在实际开发中, 当然尽量使用css动画, 毕竟css动画性能更优。但是对于一些复杂的动画,比如有暂停,继续等复杂交互等动画还是需要requestAnimationFrame

requestAnimationFrame 不管理回调函数队列,而滚动、触摸这类高触发频率事件的回调可能会在同一帧内触发多次。所以正确使用 requestAnimationFrame 的姿势是,在同一帧内可能调用多次 requestAnimationFrame时,要管理回调函数,防止重复绘制动画。

10 JS 数据类型和判断

基本数据类型:number, string, boolean, undefined, BigInt, Symbol, null

引用数据类型:Object

对象类型: Array, Function, Map, Set, WeakMap, WeakSet

特殊对象: RegExp, Date

BigInt 类型可以用任意精度表示整数,可以安全地存储和操作大整数

Symbol 符号,符号类型是唯一的并且是不可修改的,可以用来作为 Object 的 key 值。

判断:

typeof

typeof返回一个表示数据类型的字符串,返回结果包括:number、string、boolean、object、undefined、function。typeof可以对基本类型number、string??、boolean、undefined做出准确的判断(null除外,typeof null===“object”)而对于引用类型,除了function之外返回的都是object。但当我们需要知道某个对象的具体类型时,typeof就显得有些力不从心了。

instanceof

当我们需要知道某个对象的具体类型时,可以用运算符?instanceof,instanceof操作符判断左操作数对象的原型链上是否有右边这个构造函数的prototype属性,也就是说指定对象是否是某个构造函数的实例,最后返回布尔值。 检测的我们用一段伪代码来模拟instanceof内部执行过程.

function instanceOf(left, right) {
? ? let proto = left.__proto__
? ? while (true) {
? ? ? ? if (proto === null) return false
? ? ? ? if (proto === right.prototype) {
? ? ? ? ? ? return true
? ? ? ? }
? ? ? ? proto = proto.__proto__
? ? }
}

constructor
constructor属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。但是?constructor 属性易变,不可信赖,这个主要体现在自定义对象上,当开发者重写prototype后,原有的constructor会丢失。

Object.prototype.toString

toString是Object原型对象上的一个方法,该方法默认返回其调用者的具体类型,更严格的讲,是 toString运行时this指向的对象类型, 返回的类型格式为[object,xxx],xxx是具体的数据类型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有对象的类型都可以通过这个方法获取到。

类型转换

  • 强制转换

**转布尔值规则:**undefined、null、false、NaN、''、0、-0都会转换为 false;

其他所有制都转为 true,包括所有对象。

**转数字规则:**true 为1,false、null为0,undefined 为 NaN, symbol 报错,

字符串如果是数字或者进制值就转为数字,否则就 NaN

  • 隐式转换

四则运算中:

只有当加法运算中,其中一方是字符串类型,就会把另一个也转换为字符串类型

其他运算中,只要其中一方是数字,那么另一方就转化为数字。

常见

[] == ![]; // true
5 + '1' = '51'
5 - '1' = 4

二.CSS

1.CSS 选择符有哪些,那些属性可以继承,优先级。CSS3 新增伪类。

可继承的样式:

1.font-size 2.font-family

3.color 4.text-indent

不可继承的样式:

1.border 2.padding

3.margin 4.width 5.height

优先级算法:

1.优先级就近原则,同权重情况下样式定义最近者为准;

2.载入样式以最后载入的定位为准;

3.!important > ?id > class > tag

4.important 比 内联优先级高,但内联比 id 要高

CSS3新增伪类举例:

p:first-of-type 选择属于其父元素的首个

元素的每个

元素。

p:last-of-type ?选择属于其父元素的最后

元素的每个

元素。

p:only-of-type ?选择属于其父元素唯一的

元素的每个

元素。

p:only-child ? ?选择属于其父元素的唯一子元素的每个

元素。

p:nth-child(2) ?选择属于其父元素的第二个子元素的每个

元素。

:enabled :disabled 控制表单控件的禁用状态。

:checked ? ? ? ?单选框或复选框被选中

2.CSS3 那些新特性

  1. CSS3实现圆角(border-radius),阴影(box-shadow),
  2. 对文字加特效(text-shadow、),线性渐变(gradient),旋转(transform)
  3. transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);// 旋转,缩放,定位,倾斜
  4. 增加了更多的CSS选择器 ?多背景 rgba
  5. 在CSS3中唯一引入的伪元素是 ::selection.
  6. 媒体查询,多栏布局
  7. border-image

3.对 BFC/IFC 规范的理解,清除浮动的几种方法

BFC,块级格式化上下文,一个创建了新的BFC的盒子是独立布局的,盒子里面的子元素的样式不会影响到外面的元素。在同一个 BFC 中的两个毗邻的块级盒在垂直方向(和布局方向有关系)的 margin 会发生折叠。

它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。当涉及到可视化布局的时候,Block Formatting Context提供了一个环境,HTML元素在这个环境中按照一定规则进行布局。一个环境中的元素不会影响到其它环境中的布局。比如浮动元素会形成BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。也可以说BFC就是一个作用范围。

IFC(Inline Formatting Context)即行内格式化上下文

如何产生BFC

当一个HTML元素满足下面条件的任何一点,都可以产生Block Formatting Context:

a、float的值不为none

b、overflow的值不为visible

c、display的值为table-cell, table-caption, inline-block中的任何一个

d、position的值不为relative和static

CSS3触发BFC方式则可以简单描述为:在元素定位非static,relative的情况下触发,float也是一种定位方式。

(3)、BFC的作用与特点

a、不和浮动元素重叠,清除外部浮动,阻止浮动元素覆盖

如果一个浮动元素后面跟着一个非浮动的元素,那么就会产生一个重叠的现象。常规流(也称标准流、普通流)是一个文档在被显示时最常见的布局形态,当float不为none时,position为absolute、fixed时元素将脱离标准流。

4.CSS 尺寸单位,说说 em 与 rem 的区别

  • 绝对长度单位包括:cm厘米(Centimeters)mm毫米(Millimeters)q1/4毫米(quarter-millimeters)in英寸(Inches)pt点、磅(Points)pc派卡(Picas),相当于我国新四号铅字的尺寸px
  • 相对长度单位包括:em,ex,ch,rem,vw相对于视口的宽度,视口被均分为100单位的vwvh相对于视口的高度。视口被均分为100单位的vhvmax,vmin

em 与 rem的区别

  • rem

rem是CSS3新增的一个相对单位(root em,根em),相对于根元素(即html元素)font-size计算值的倍数,只相对于根元素的大小?。rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。

作用:利用rem可以实现简单的响应式布局,可以利用html元素中字体的大小与屏幕间的比值设置font-size的值实现当屏幕分辨率变化时让元素也变化,以前的天猫tmall就使用这种办法

  • em

文本相对长度单位。相对于当前对象内文本的字体尺寸。如当前对行内文本的字体尺寸未被人为设置,则相对于浏览器的默认字体尺寸(默认16px)。(相对父元素的字体大小倍数)

em(font size of the element)是指相对于父元素的字体大小的单位。它与rem之间其实很相似,区别在。(相对是的HTML元素的字体大,默认16px)

em与rem的重要区别: 它们计算的规则一个是依赖父元素另一个是依赖根元素计算

5.说说 box-sizing 属性的用法

  • box-sizing:content-box: padding和border不被包含在定义的width和height之内。对象的实际宽度等于设置的width值和border、padding之和,即 ( Element width = width + border + padding,但占有页面位置还要加上margin ) 此属性表现为标准模式下的盒模型。
  • box-sizing:border-box: padding和border被包含在定义的width和height之内。对象的实际宽度就等于设置的width值,即使定义有border和padding也不会改变对象的实际宽度,即 ( Element width = width ) 此属性表现为怪异模式下的盒模型。

6.说说对定位 positon 的理解

使用css布局position非常重要,语法如下:

position:static | relative | absolute | fixed | center | page | sticky

默认值:static,center、page、sticky是CSS3中新增加的值。

  • static

可以认为静态的,默认元素都是静态的定位,对象遵循常规流。此时4个定位偏移属性不会被应用,也就是使用left,right,bottom,top将不会生效。

  • relative

相对定位,对象遵循常规流,并且参照自身在常规流中的位置通过top,right,bottom,left这4个定位偏移属性进行偏移时不会影响常规流中的任何元素。

  • absolute

a、绝对定位,对象脱离常规流,此时偏移属性参照的是离自身最近的定位祖先元素,如果没有定位的祖先元素,则一直回溯到body元素。盒子的偏移位置不影响常规流中的任何元素,其margin不与其他任何margin折叠。

b、元素定位参考的是离自身最近的定位祖先元素,要满足两个条件,第一个是自己的祖先元素,可以是父元素也可以是父元素的父元素,一直找,如果没有则选择body为对照对象。第二个条件是要求祖先元素必须定位,通俗说就是position的属性值为非static都行。

  • fixed

固定定位,与absolute一致,但偏移定位是以窗口为参考。当出现滚动条时,对象不会随着滚动。

  • center

与absolute一致,但偏移定位是以定位祖先元素的中心点为参考。盒子在其包含容器垂直水平居中。(CSS3)

  • page

与absolute一致。元素在分页媒体或者区域块内,元素的包含块始终是初始包含块,否则取决于每个absolute模式。(CSS3)

  • sticky

对象在常态时遵循常规流。它就像是relative和fixed的合体,当在屏幕中时按常规流排版,当卷动到屏幕外时则表现如fixed。该属性的表现是现实中你见到的吸附效果。(CSS3)

7.垂直水平居中的几种方法

水平居中

  • 行内元素: text-align:center;
  • flex 布局: justify-content: center;
  • margin(width已设置):margin: 0 auto;

垂直居中

  • height: 100px; line-height: 100px;
  • table-cell, vertical-align:middle;
  • flex: align-items: center;

垂直水平居中

  • 设定行高 line-height为height一样数值,仅适用行内元素
.div0{
? width:200px;
? height:150px;
? border:1px solid #000;
? line-height:150px;
? text-align:center;
}
.redbox{
? display:inline-block;
? width:30px;
? height:30px;
? background:#c00;
}
  • 添加伪类元素:CSS里头vertical-align这个属性,这个属性虽然是垂直居中,不过却是指在元素内的所有元素垂直位置互相居中,并不是相对于外框的高度垂直居中。因此,如果有一个方块变成了高度100%,那么其他的方块就会真正的垂直居中。利用::before和::after添加div进到杠杠内,让这个“伪”div的高度100%
.div0::before{
? content:'';
? width:0;
? height:100%;
? display:inline-block;
? position:relative;
? vertical-align:middle;
? background:#f00;
}
  • calc 动态计算:我们只要让要居中的div的top属性,与上方的距离是“50%的外框高度+ 50%的div高度”,就可以做到垂直居中,至于为什么不用margin-top,因为margin相对的是水平宽度,必须要用top才会正确。
.redbox{ 
  width:30px;
  height:30px; 
  background:#c00; 
  float:left; 
  top:calc(50% - 15px); 
  margin-left:calc(50% - 45px); 
}
  • 使用表格或假装表格:在表格这个HTML里面常用的DOM里头,要实现垂直居中是相当容易的,只需要下一行vertical-align:middle就可以
  • transform:利用transform里头的translateY(改变垂直的位移,如果使用百分比为单位,则是以元素本身的长宽为基准),搭配元素本身的top属性,就可以做出垂直居中的效果,比较需要注意的地方是,子元素必须要加上position:relative
  • 绝对定位: 绝对定位就是CSS里头的position:absolute,利用绝对位置来指定,但垂直居中的做法又和我们正统的绝对位置不太相同,是要将上下左右的数值都设为0,再搭配一个margin:auto,就可以办到垂直居中,不过要特别注意的是,设定绝对定位的子元素,其父元素的position必须要指定为relative
.use-absolute{
? ? position: relative;
? ? width:200px;
? ? height:150px;
? ? border:1px solid #000;
}
.use-absolute div{
? ? position: absolute;
? ? width:100px;
? ? height:50px;
? ? top:0;
? ? right:0;
? ? bottom:0;
? ? left:0;
? ? margin:auto;
? ? background:#f60;
}

flexbox: 使用align-items或align-content的属性

8.常见的页面布局的方式有哪些

方式:双飞翼、多栏、弹性、流式、瀑布流、响应式布局

  • 双飞翼布局

经典三列布局,也叫做圣杯布局【Holy Grail of Layouts】是Kevin Cornell在2006年提出的一个布局模型概念,在国内最早是由淘宝UED的工程师传播开来,在中国也有叫法是双飞翼布局,它的布局要求有几点:

a、三列布局,中间宽度自适应,两边定宽;

b、中间栏要在浏览器中优先展示渲染;

c、允许任意列的高度最高;

d、要求只用一个额外的DIV标签;

e、要求用最简单的CSS、最少的HACK语句;

在不增加额外标签的情况下,圣杯布局已经非常完美,圣杯布局使用了相对定位,以后布局是有局限性的,而且宽度控制要改的地方也多。在淘宝UED(User Experience Design)探讨下,增加多一个div就可以不用相对布局了,只用到了浮动和负边距,这就是我们所说的双飞翼布局。

  • 多栏布局

a、栏栅格系统:就是利用浮动实现的多栏布局,在bootstrap中用的非常多。

b、多列布局:栅格系统并没有真正实现分栏效果(如word中的分栏),CSS3为了满足这个要求增加了多列布局模块

  • 弹性布局(Flexbox)

CSS3引入了一种新的布局模式——Flexbox布局,即伸缩布局盒模型(Flexible Box),用来提供一个更加有效的方式制定、调整和分布一个容器里项目布局,即使它们的大小是未知或者动态的,这里简称为Flex。Flexbox布局常用于设计比较复杂的页面,可以轻松的实现屏幕和浏览器窗口大小发生变化时保持元素的相对位置和大小不变,同时减少了依赖于浮动布局实现元素位置的定义以及重置元素的大小。

Flexbox布局在定义伸缩项目大小时伸缩容器会预留一些可用空间,让你可以调节伸缩项目的相对大小和位置。例如,你可以确保伸缩容器中的多余空间平均分配多个伸缩项目,当然,如果你的伸缩容器没有足够大的空间放置伸缩项目时,浏览器会根据一定的比例减少伸缩项目的大小,使其不溢出伸缩容器。

综合而言,Flexbox布局功能主要具有以下几点:

a、屏幕和浏览器窗口大小发生改变也可以灵活调整布局;

b、可以指定伸缩项目沿着主轴或侧轴按比例分配额外空间(伸缩容器额外空间),从而调整伸缩项目的大小;

c、可以指定伸缩项目沿着主轴或侧轴将伸缩容器额外空间,分配到伸缩项目之前、之后或之间;

d、可以指定如何将垂直于元素布局轴的额外空间分布到该元素的周围;

e、可以控制元素在页面上的布局方向;

f、可以按照不同于文档对象模型(DOM)所指定排序方式对屏幕上的元素重新排序。也就是说可以在浏览器渲染中不按照文档流先后顺序重排伸缩项目顺序。

  • 瀑布流布局

瀑布流布局是流式布局的一种。是当下比较流行的一种网站页面布局,视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。最早采用此布局的网站是Pinterest,逐渐在国内流行开来。

优点

a、有效的降低了界面复杂度,节省了空间:我们不再需要臃肿复杂的页码导航链接或按钮了。

b、对触屏设备来说,交互方式更符合直觉:在移动应用的交互环境当中,通过向上滑动进行滚屏的操作已经成为最基本的用户习惯,而且所需要的操作精准程度远远低于点击链接或按钮。

c、更高的参与度:以上两点所带来的交互便捷性可以使用户将注意力更多的集中在内容而不是操作上,从而让他们更乐于沉浸在探索与浏览当中。

缺点

a、有限的用例:无限滚动的方式只适用于某些特定类型产品当中一部分特定类型的内容。 例如,在电商网站当中,用户时常需要在商品列表与详情页面之间切换,这种情况下,传统的、带有页码导航的方式可以帮助用户更稳妥和准确的回到某个特定的列表页面当中。

b、额外的复杂度:那些用来打造无限滚动的JS库虽然都自称很容易使用,但你总会需要在自己的产品中进行不同程度的定制化处理,以满足你们自己的需求;另外这些JS库在浏览器和设备兼容性等方面的表现也参差不齐,你必须做好充分的测试与调整工作。

c、再见了,页脚:如果使用了比较典型的无限滚动加载模式,这就意味着你可以和页脚说拜拜了。 最好考虑一下页脚对于你的网站,特别是用户的重要性;如果其中确实有比较重要的内容或链接,那么最好换一种更传统和稳妥的方式。千万不要耍弄你的用户,当他们一次次的浏览到页面底部,看到页脚,却因为自动加载的内容突然出现而无论如何都无法点击页脚中的链接时,他们会变的越发愤怒。

d、集中在一页当中动态加载数据,与一页一页的输出相比,究竟那种方式更利于SEO,这是你必须考虑的问题。对于某些以类型网站来说,在这方面进行冒险是很不划算的。

e、关于页面数量的印象:其实站在用户的角度来看,这一点并非负面;不过,如果对于你的网站来说,通过更多的内容页面展示更多的相关信息(包括广告)是很重要的策略,那么单页无限滚动的方式对你并不适用。

  • 流式布局(Fluid)

固定布局和流式布局在网页设计中最常用的两种布局方式。固定布局能呈现网页的原始设计效果,流式布局则不受窗口宽度影响,流式布局使用百分比宽度来限定布局元素,这样可以根据客户端分辨率的大小来进行合理的显示。

  • 响应式布局

响应式布局是Ethan Marcotte在2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。这个概念是为解决移动互联网浏览而诞生的。响应式布局可以为不同终端的用户提供更加舒适的界面和更好的用户体验,而且随着目前大屏幕移动设备的普及,用“大势所趋”来形容也不为过。随着越来越多的设计师采用这个技术,我们不仅看到很多的创新,还看到了一些成形的模式。

优点

a、面对不同分辨率设备灵活性强

b、能够快捷解决多设备显示适应问题

缺点

a、兼容各种设备工作量大,效率低下

b、代码累赘,会出现隐藏无用的元素,加载时间加长

c、其实这是一种折中性质的设计解决方案,多方面因素影响而达不到最佳效果

d、一定程度上改变了网站原有的布局结构,会出现用户混淆的情况

两栏布局,左侧定宽,右侧自适应

<div id="wrapper1">
? <div class="left">
? ? 左边固定宽度,高度不固定 </br> </br></br></br>高度有可能会很小,也可能很大。
? </div>
? <div class="main">
? ? 这里的内容可能比左侧高,也可能比左侧低。宽度需要自适应。</br>
? ? 基本的样式是,两个div相距20px, 左侧div宽 120px
? </div>
</div>

1.双 inline-block 方法

缺点**:**

  • 需要知道左侧盒子的宽度,两个盒子的距离,还要设置各个元素的box-sizing
  • 需要消除空格字符的影响
  • 需要设置vertical-align: top满足顶端对齐。
#wrapper1 {
? box-sizing: content-box;
? border: 1px solid red;
? padding: 15px 20px;
? font-size: 0; /*消除空格影响*/
}
#wrapper1 .left, .main {
? box-sizing: border-box;
? border: 1px solid blue;
? display: inline-block;
? font-size: 14px;
? vertical-align: top; /* 顶端对齐*/
}
#wrapper1 .left {
? width: 200px;
}
#wrapper1 .main {
? width: calc(100% - 200px);
}

2.双 float 方法
缺点**:**

  • 需要知道左侧盒子的宽度,两个盒子的距离,还要设置各个元素的box-sizing。
  • 父元素需要清除浮动。
#wrapper1 {
? box-sizing: content-box;
? border: 1px solid red;
? padding: 15px 20px;

? overflow: auto;
}
#wrapper1 .left, .main {
? box-sizing: border-box;
? border: 1px solid blue;

? float: left;
}
#wrapper1 .left {
? width: 200px;
}
#wrapper1 .main {
? width: calc(100% - 200px);
}

3.使用 float + margin-left 的方法

#wrapper1 {
? box-sizing: border-box;
? border: 1px solid red;
? padding: 15px 20px;

  overflow: hidden;
}
#wrapper1 .left, .main {
? box-sizing: border-box;
? border: 1px solid blue;
}
#wrapper1 .left {
 ? float: left;
}
#wrapper1 .main {
 ? margin-left: 242px
}

4.使用 float + BFC 的方法
缺点**:**

  • 父元素需要清除浮动

正常流中建立新的块格式化上下文的元素(例如除'visible'之外的'overflow'的元素)不能与元素本身相同的块格式上下文中的任何浮点的边界框重叠。

#wrapper1 {
? box-sizing: content-box;
? border: 1px solid red;
? padding: 15px 20px;

? overflow: auto;
}
#wrapper1 .left, .main {
? box-sizing: border-box;
? border: 1px solid blue;
}
#wrapper1 .left {
? float: left;
}
#wrapper1 .main {
? overflow: auto;
}

5.使用 flex 方法
需要注意的是,flex容器的一个默认属性值:align-items: stretch;。这个属性导致了列等高的效果。 为了让两个盒子高度自动,需要设置:?align-items: flex-start;

#wrapper1 {
? box-sizing: content-box;
? border: 1px solid red;
? padding: 15px 20px;

? display: flex;
? align-items: flex-start;
}
#wrapper1 .left, .main {
? box-sizing: border-box;
? border: 1px solid blue;
}
#wrapper1 .left {
 ? flex: 0 0 auto; ? //[ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
}
#wrapper1 .main {
 ? flex: 1 1 auto;
}

6.使用 absulute + margin-left 的方法
缺点**:**

  • 使用了绝对定位,若是用在某个div中,需要更改父容器的position。
  • 没有清除浮动的方法,若左侧盒子高于右侧盒子,就会超出父容器的高度。因此只能通过设置父容器的min-height来放置这种情况。
#wrapper1 {
? box-sizing: border-box;
? border: 1px solid red;
? padding: 15px 20px;

? min-height: 300px;
}
#wrapper1 .left, .main {
? box-sizing: border-box;
? border: 1px solid blue;
}
#wrapper1 .left {
 ? position: absolute;
}
#wrapper1 .main {
 ? margin-left: 242px
}

9.Sass 有什么特性

  • 变量:允许使用变量,所有变量以$开头,如果变量需要镶嵌在字符串之中,就必须需要写在#{}之中。
  • 计算功能:允许代码中直接使用算式
  • 嵌套:允许直接在选择器中嵌套
  • &嵌套:嵌套代码中可以使用 & 直接引用父元素
  • @extend. 继承:允许一个选择器继承另一个选择器
  • @mixin: 可定义可重用代码块,后续可以通过 @include 复用
  • @import: 可以插入外部文件
  • 提供注释:标准 /comment/ 回报率到编译后的文件;单行注释 //comment, 只保留在sass源文件中,编译后删除
  1. css 动画,Animation特性

animation: name duration timing-function delay iteration-count direction fill-mode play-state;

( 名称、长度、如何完成一周期、延迟、次数、是否轮流反向播放、不播放时样式、动画播放过程中可能会突然停止指定动画是否正在运行或暂停)

  • animation-name 规定需要绑定到选择器的 keyframe 名称
  • animation-duration 规定完成动画所花费的时间,以秒或毫秒计
  • animation-timing-function 规定动画的速度曲线
  • animation-delay 规定在动画开始之前的延迟
  • animation-iteration-count 规定动画应该播放的次数
  • animation-direction 规定是否应该轮流反向播放动画

我们还需要用keyframes关键字

@keyframes rainbow {
  0% { background: #c00; }
  50% { background: orange; }
  100% { background: yellowgreen; }
}

11 css 实现 0.5px border

大致原理是:通过 css3插入一个伪元素,该元素宽度为父级2倍,高度为1px,然后通过css3缩放将其缩小一倍,从而实现一个 0.5 px 的边框。

.content:before {
    content: ‘’;
    position:absolute;
    width:200%;
    height: 1px;
    border-bottom: 1px solid #000;
    transform-origin: 0, 0; -webkit-transform-origin
    transform: scale(.5,.5); -webkit-transform
    box-sizing: border-box; -webkit-boxsizing
}

12 css 实现三角形

图片#triangle-up {
width: 0;
height: 0;
border-left: 50px solid transparent;
border-right: 50px solid transparent;
border-bottom: 100px solid red;
}
图片#triangle-topleft {
width: 0;
height: 0;
border-top: 100px solid red;
border-right: 100px solid transparent;
}

13 如何获取和设置盒子的宽高

//第一种
dom.style.width/height //只能获取内联样式的元素宽高
//第二种
dom.currentStyle.width/height //只有IE浏览器支持
//第三种
dom.getComputedStyle(Dom).width/height //只有浏览器渲染后才能获取 兼容好
//第四种
dom.getBoundingClientRect().width/height //计算一个元素的绝对位置(相对于视窗左上角) 能拿到元素的left、right、width、height

三.HTML

1.浏览器存储 cookie、session、sessionStorage、localStorage

Cookie

  • 每个特定的域名下最多生成20个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.有些状态不可能保存在客户端。例如,为了防止重复提交表单,我们需要在服务器端保存一个计数器。如果我们把这个计数器保存在客户端,那么它起不到任何作用。

属性:

name| string : 该Cookie的名称。Cookie一旦创建,名称便不可更改

value| object :该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码

maxAge| int : 该Cookie失效的时间,单位秒。如果为正数,则该Cookie在maxAge秒之后失效。如果为负数,该Cookie为临时Cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该Cookie。如果为0,表示删除该Cookie。默认为–1

secure| boolean : 该Cookie是否仅被使用安全协议传输。https,只在使用SSL安全连接的情况下才会把cookie发送到服务器

**path |string:**该Cookie的使用路径。请求URL中包含这个路径才会把cookie发送到服务器。如果设置为“/sessionWeb/”,则只有contextPath为“/sessionWeb”的程序可以访问该Cookie。如果设置为“/”,则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为“/”

domain| string : 可以访问该Cookie的域名。如果设置为“.google.com”,则所有以“google.com”结尾的域名都可以访问该Cookie。注意第一个字符必须为“.”

comment| string : 该Cookie的用处说明。

version| int : 该Cookie使用的版本号。0表示遵循Netscape的Cookie规范,1表示遵循W3C的RFC 2109规范

**httponly |? boolean :**表示此cookie必须用于http或https传输。这意味着,浏览器脚本,比如javascript中,是不允许访问操作此cookie的。以防范跨站脚本攻击(XSS)。

操作:

1.Cookie并不提供修改、删除操作。如果要修改某个Cookie,只需要新建一个同名的Cookie,添加到response中覆盖原来的Cookie。

2.如果要删除某个Cookie,只需要新建一个同名的Cookie,并将maxAge设置为0,并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。

  • 永久登录案例:

    • 一种方案是把密码加密后保存到Cookie中,下次访问时解密并与数据库比较。(高风险)
    • 如果不希望保存密码,还可以把登录的时间戳保存到Cookie与数据库中,到时只验证用户名与登录时间戳就可以了。
    • 实现方式是把账号按照一定的规则加密后,连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可。本例把账号保存到名为account的Cookie中,把账号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。

LocalStorage

**+**永久存储

+单个域名存储量比较大(推荐5MB,各浏览器不同)

**+**总体数量无限制

SessionStorage

+只在Session内有效

**+**存储量更大(推荐没有限制,但是实际上各浏览器也不同)

2.HTML 5 有哪些新特性

主要是关于图像、位置、存储、多任务等功能的增加。

  • 拖拽释放 API(Drag and drop)
  • 语义化更好的内容标签(header,nav,footer,aside,article,section)
  • 音频、视频API(audio,video)
  • 画布 API (Canvas)
  • 地理 API (Geolocation)
  • 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
  • sessionStorage 的数据在浏览器关闭后自动删除
  • 表单控件,calendar、date、time、email、url、search
  • 新的技术 webworker, websocket, Geolocation

移除的元素:

  1. 纯表现的元素:basefont,big,center,font, s,strike,tt,u;
  2. 对可用性产生负面影响的元素:frame,frameset,noframes;

3.iframe 的优缺点

  • 优点
  1. 解决加载缓慢的第三方内容如图表和广告等的加载问题
  2. Security sandbox
  3. 并行加载脚本
  • 缺点
  1. iframe 会阻塞主页面的 onload 事件
  2. 搜索引擎的检索程序无法解读这种页面,不利于 SEO
  3. iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,所以会影响页面的并行加载

避免手段 如果使用 iframe,最好通过 js 动态给 iframe 添加 src 属性值。

4.Canvas 与 SVG 的区别

CanvasSVG
Canvas是基于像素的位图,依赖分辨率SVG却是基于矢量图形,不依赖分辨率
Canvas是基于HTML canvas标签,通过宿主提供的Javascript API对整个画布进行操作的SVG则是基于XML元素的,历史久远
Canvas没有图层的概念,所有的修改整个画布都要重新渲染SVG则可以对单独的标签进行修改
关于动画,Canvas更适合做基于位图的动画而SVG则适合图表的展示
不能被引擎抓取可以被引擎抓取
Canvas 能够以 .png 或 .jpg 格式保存结果图像;能够引入 .png 或 .jpg格式的图片SVG 不能以 .png 或 .jpg 格式保存结果图像;不能引入 .png 或 .jpg格式的图片
Canvas 不支持事件处理器(一旦图形被绘制完成,它就不会继续得到浏览器的关注。如果其位置发生变化,那么整个场景也需要重新绘制,包括任何或许已被图形覆盖的对象。)SVG 支持事件处理器(SVG DOM 中的每个元素都是可用的。您可以为某个元素附加 JavaScript 事件处理器。每个被绘制的图形均被视为对象。如果 SVG 对象的属性发生变化,那么浏览器能够自动重现图形。)
最适合图像密集型的游戏,其中的许多对象会被频繁重绘不适合游戏应用

5.js 脚本延迟加载方式有哪些

  1. defer和async
  2. 动态创建DOM方式(创建script,插入到DOM中,加载完毕后callBack)
  3. 按需异步载入js

script 加载中的 async 与 defer 的区别

  • async 与 defer 共同点是采用异步的下载方式,不会阻塞 html 的解析
  • 不同点在于 async 下载完还是会立即执行,defer 则是会在 html 解析完去执行,而 js 的执行还是会阻塞html 的解析的。
  • 缺点:带有async 或者 defer属性的脚本执行也不一定按照顺序执行,有风险,因此确保两者之间互不依赖非常重要。(HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoad时间触发前执行,因此最好只包含一个延迟脚本。)

图片

DOMContentLoaded, onload

  • DOMContentLoaded 当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。我们可以在这个阶段使用 JS 去访问元素。 async 的脚本可能还没有执行。

    • 带有 defer 的脚本会在页面加载和解析完毕后执行,刚好在 DOMContentLoaded 之前执行。
    • 图片及其他资源文件可能还在下载中。
  • load 应该仅用于检测一个完全加载的页面 当一个资源及其依赖资源已完成加载时,将触发load事件。
  • beforeunload 在用户即将离开页面时触发,它返回一个字符串,浏览器会向用户展示并询问这个字符串以确定是否离开。
  • unload 在用户已经离开时触发,我们在这个阶段仅可以做一些没有延迟的操作,由于种种限制,很少被使用。
  • document.readyState 表征页面的加载状态,可以在 readystatechange 中追踪页面的变化状态:

    • loading —— 页面正在加载中。
    • interactive —— 页面解析完毕,时间上和 DOMContentLoaded 同时发生,不过顺序在它之前。
    • complete —— 页面上的资源都已加载完毕,时间上和 window.onload 同时发生,不过顺序在他之前。

6.对于 web 性能优化的方法及理解(雅虎网站性能优化 34 条守则)

一个http请求绝大多数的时间消耗在了建立连接跟等待的时间,优化的方法是减少http请求。

  1. 尽可能的减少 HTTP 的请求数 content
  2. 使用 CDN(Content Delivery Network)内容分发网络 server
  3. 添加 Expires 头(或者 Cache-control ) server
  4. Gzip 组件 server
  5. 将 CSS 样式放在页面的上方 css
  6. 将脚本移动到底部(包括内联的) javascript
  7. 避免使用 CSS 中的 Expressions css
  8. 将 JavaScript 和 CSS 独立成外部文件 javascript css
  9. 减少 DNS 查询 content
  10. 压缩 JavaScript 和 CSS (包括内联的) javascript css
  11. 避免重定向 server
  12. 移除重复的脚本 javascript
  13. 配置实体标签(ETags) css
  14. 使 AJAX 缓存

雅虎团队经验:网站页面性能优化的34条黄金守则

1、尽量减少HTTP请求次数

终端用户响应的时间中,有80%用于下载各项内容。这部分时间包括下载页面中的图像、样式表、脚本、Flash等。通过减少页面中的元素可以减少HTTP请求的次数。这是提高网页速度的关键步骤。

减少页面组件的方法其实就是简化页面设计。那么有没有一种方法既能保持页面内容的丰富性又能达到加快响应时间的目的呢?这里有几条减少HTTP请求次数同时又可能保持页面内容丰富的技术。

合并文件是通过把所有的脚本放到一个文件中来减少HTTP请求的方法,如可以简单地把所有的CSS文件都放入一个样式表中。当脚本或者样式表在不同页面中使用时需要做不同的修改,这可能会相对麻烦点,但即便如此也要把这个方法作为改善页面性能的重要一步。

CSS Sprites是减少图像请求的有效方法。把所有的背景图像都放到一个图片文件中,然后通过CSS的background-image和background-position属性来显示图片的不同部分;

图片地图是把多张图片整合到一张图片中。虽然文件的总体大小不会改变,但是可以减少HTTP请求次数。图片地图只有在图片的所有组成部分在页面中是紧挨在一起的时候才能使用,如导航栏。确定图片的坐标和可能会比较繁琐且容易出错,同时使用图片地图导航也不具有可读性,因此不推荐这种方法;

内联图像是使用data:URL scheme的方法把图像数据加载页面中。这可能会增加页面的大小。把内联图像放到样式表(可缓存)中可以减少HTTP请求同时又避免增加页面文件的大小。但是内联图像现在还没有得到主流浏览器的支持。

减少页面的HTTP请求次数是你首先要做的一步。这是改进首次访问用户等待时间的最重要的方法。如同Tenni Theurer的他的博客Browser Cahe Usage - Exposed!中所说,HTTP请求在无缓存情况下占去了40%到60%的响应时间。让那些初次访问你网站的人获得更加快速的体验吧!

2、减少DNS查找次数

域名系统(DNS)提供了域名和IP的对应关系,就像电话本中人名和他们的电话号码的关系一样。当你在浏览器地址栏中输入www.dudo.org时,DNS解析服务器就会返回这个域名对应的IP地址。DNS解析的过程同样也是需要时间的。一般情况下返回给定域名对应的IP地址会花费20到120毫秒的时间。而且在这个过程中浏览器什么都不会做直到DNS查找完毕。

缓存DNS查找可以改善页面性能。这种缓存需要一个特定的缓存服务器,这种服务器一般属于用户的ISP提供商或者本地局域网控制,但是它同样会在用户使用的计算机上产生缓存。DNS信息会保留在操作系统的DNS缓存中(微软Windows系统中DNS Client Service)。大多数浏览器有独立于操作系统以外的自己的缓存。由于浏览器有自己的缓存记录,因此在一次请求中它不会受到操作系统的影响。

Internet Explorer默认情况下对DNS查找记录的缓存时间为30分钟,它在注册表中的键值为DnsCacheTimeout。Firefox对DNS的查找记录缓存时间为1分钟,它在配置文件中的选项为network.dnsCacheExpiration(Fasterfox把这个选项改为了1小时)。

当客户端中的DNS缓存都为空时(浏览器和操作系统都为空),DNS查找的次数和页面中主机名的数量相同。这其中包括页面中URL、图片、脚本文件、样式表、Flash对象等包含的主机名。减少主机名的数量可以减少DNS查找次数。

减少主机名的数量还可以减少页面中并行下载的数量。减少DNS查找次数可以节省响应时间,但是减少并行下载却会增加响应时间。我的指导原则是把这些页面中的内容分割成至少两部分但不超过四部分。这种结果就是在减少DNS查找次数和保持较高程度并行下载两者之间的权衡了。

3、避免跳转

跳转是使用301和302代码实现的。下面是一个响应代码为301的HTTP头:

HTTP/1.1 301 Moved Permanently

Location:http://example.com/newuri

Content-Type: text/html

浏览器会把用户指向到Location中指定的URL。头文件中的所有信息在一次跳转中都是必需的,内容部分可以为空。不管他们的名称,301和302响应都不会被缓存除非增加一个额外的头选项,如Expires或者Cache-Control来指定它缓存。元素的刷新标签和JavaScript也可以实现URL的跳转,但是如果你必须要跳转的时候,最好的方法就是使用标准的3XXHTTP状态代码,这主要是为了确保“后退”按钮可以正确地使用。



但是要记住跳转会降低用户体验。在用户和HTML文档中间增加一个跳转,会拖延页面中所有元素的显示,因为在HTML文件被加载前任何文件(图像、Flash等)都不会被下载。

有一种经常被网页开发者忽略却往往十分浪费响应时间的跳转现象。这种现象发生在当URL本该有斜杠(/)却被忽略掉时。例如,当我们要访问http://astrology.yahoo.com/astrology时,实际上返回的是一个包含301代码的跳转,它指向的是http://astrology.yahoo.com/astrology/(注意末尾的斜杠)。在Apache服务器中可以使用Alias 或者 mod\_rewrite或者the DirectorySlash来避免。

连接新网站和旧网站是跳转功能经常被用到的另一种情况。这种情况下往往要连接网站的不同内容然后根据用户的不同类型(如浏览器类型、用户账号所属类型)来进行跳转。使用跳转来实现两个网站的切换十分简单,需要的代码量也不多。尽管使用这种方法对于开发者来说可以降低复杂程度,但是它同样降低用户体验。一个可替代方法就是如果两者在同一台服务器上时使用Alias和mod\_rewrite和实现。如果是因为域名的不同而采用跳转,那么可以通过使用Alias或者mod\_rewirte建立CNAME(保存一个域名和另外一个域名之间关系的DNS记录)来替代。

4、可缓存的AJAX

Ajax经常被提及的一个好处就是由于其从后台服务器传输信息的异步性而为用户带来的反馈的即时性。但是,使用Ajax并不能保证用户不会在等待异步的JavaScript和XML响应上花费时间。在很多应用中,用户是否需要等待响应取决于Ajax如何来使用。例如,在一个基于Web的Email客户端中,用户必须等待Ajax返回符合他们条件的邮件查询结果。记住一点,“异步”并不异味着“即时”,这很重要。

为了提高性能,优化Ajax响应是很重要的。提高Ajxa性能的措施中最重要的方法就是使响应具有可缓存性,具体的讨论可以查看Add an Expires or a Cache-Control Header。其它的几条规则也同样适用于Ajax:

Gizp压缩文件

减少DNS查找次数

精简JavaScript

避免跳转

配置ETags

让我们来看一个例子:一个Web2.0的Email客户端会使用Ajax来自动完成对用户地址薄的下载。如果用户在上次使用过Email web应用程序后没有对地址薄作任何的修改,而且Ajax响应通过Expire或者Cacke-Control头来实现缓存,那么就可以直接从上一次的缓存中读取地址薄了。必须告知浏览器是使用缓存中的地址薄还是发送一个新的请求。这可以通过为读取地址薄的Ajax URL增加一个含有上次编辑时间的时间戳来实现,例如,&t=11900241612等。如果地址薄在上次下载后没有被编辑过,时间戳就不变,则从浏览器的缓存中加载从而减少了一次HTTP请求过程。如果用户修改过地址薄,时间戳就会用来确定新的URL和缓存响应并不匹配,浏览器就会重要请求更新地址薄。

即使你的Ajxa响应是动态生成的,哪怕它只适用于一个用户,那么它也应该被缓存起来。这样做可以使你的Web2.0应用程序更加快捷。

5、推迟加载内容

你可以仔细看一下你的网页,问问自己“哪些内容是页面呈现时所必需首先加载的?哪些内容和结构可以稍后再加载?

把整个过程按照onload事件分隔成两部分,JavaScript是一个理想的选择。例如,如果你有用于实现拖放和动画的JavaScript,那么它就以等待稍后加载,因为页面上的拖放元素是在初始化呈现之后才发生的。其它的例如隐藏部分的内容(用户操作之后才显现的内容)和处于折叠部分的图像也可以推迟加载

工具可以节省你的工作量:YUI Image Loader可以帮你推迟加载折叠部分的图片,YUI Get utility是包含JS和 CSS的便捷方法。比如你可以打开Firebug的Net选项卡看一下Yahoo的首页。

当性能目标和其它网站开发实践一致时就会相得益彰。这种情况下,通过程序提高网站性能的方法告诉我们,在支持JavaScript的情况下,可以先去除用户体验,不过这要保证你的网站在没有JavaScript也可以正常运行。在确定页面运行正常后,再加载脚本来实现如拖放和动画等更加花哨的效果。

6、预加载

预加载和后加载看起来似乎恰恰相反,但实际上预加载是为了实现另外一种目标。预加载是在浏览器空闲时请求将来可能会用到的页面内容(如图像、样式表和脚本)。使用这种方法,当用户要访问下一个页面时,页面中的内容大部分已经加载到缓存中了,因此可以大大改善访问速度。

下面提供了几种预加载方法:

无条件加载:触发onload事件时,直接加载额外的页面内容。以Google.com为例,你可以看一下它的spirit image图像是怎样在onload中加载的。这个spirit image图像在google.com主页中是不需要的,但是却可以在搜索结果页面中用到它。

有条件加载:根据用户的操作来有根据地判断用户下面可能去往的页面并相应的预加载页面内容。在search.yahoo.com中你可以看到如何在你输入内容时加载额外的页面内容。

有预期的加载:载入重新设计过的页面时使用预加载。这种情况经常出现在页面经过重新设计后用户抱怨“新的页面看起来很酷,但是却比以前慢”。问题可能出在用户对于你的旧站点建立了完整的缓存,而对于新站点却没有任何缓存内容。因此你可以在访问新站之前就加载一部内容来避免这种结果的出现。在你的旧站中利用浏览器的空余时间加载新站中用到的图像的和脚本来提高访问速度。

7、减少DOM元素数量

一个复杂的页面意味着需要下载更多数据,同时也意味着JavaScript遍历DOM的效率越慢。比如当你增加一个事件句柄时在500和5000个DOM元素中循环效果肯定是不一样的。

大量的DOM元素的存在意味着页面中有可以不用移除内容只需要替换元素标签就可以精简的部分。你在页面布局中使用表格了吗?你有没有仅仅为了布局而引入更多的
元素呢?也许会存在一个适合或者在语意是更贴切的标签可以供你使用。

YUI CSS utilities可以给你的布局带来巨大帮助:grids.css可以帮你实现整体布局,font.css和reset.css可以帮助你移除浏览器默认格式。它提供了一个重新审视你页面中标签的机会,比如只有在语意上有意义时才使用
,而不是因为它具有换行效果才使用它。

DOM元素数量很容易计算出来,只需要在Firebug的控制台内输入:

document.getElementsByTagName('*').length

那么多少个DOM元素算是多呢?这可以对照有很好标记使用的类似页面。比如Yahoo!主页是一个内容非常多的页面,但是它只使用了700个元素(HTML标签)。

8、根据域名划分页面内容

把页面内容划分成若干部分可以使你最大限度地实现平行下载。由于DNS查找带来的影响你首先要确保你使用的域名数量在2个到4个之间。例如,你可以把用到的HTML内容和动态内容放在www.example.org上,而把页面各种组件(图片、脚本、CSS)分别存放在statics1.example.orgstatics.example.org上。

你可在Tenni Theurer和Patty Chi合写的文章Maximizing Parallel Downloads in the Carpool Lane找到更多相关信息。

9、使iframe的数量最小

ifrmae元素可以在父文档中插入一个新的HTML文档。了解iframe的工作理然后才能更加有效地使用它,这一点很重要。


本文转自网络,版权归原作者所有,原文链接:https://segmentfault.com/a/1190000039781941
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文


随机推荐