面试笔试题目

__ proto __和prototype的区别:

 function hello(){ }
 function bye(){}
 
 var a=new hello();
console.log(a.prototype);//undefined
console.log(a.__proto__);
console.log(a.__proto__==hello.prototype);//true
console.log(a.prototype==a.__proto__);//false
        
console.log(hello.prototype);
console.log(hello.prototype.__proto__==Object.prototype);//true
console.log(hello.prototype.__proto__==bye.prototype.__proto__);//true
console.log(hello.prototype.constructor==hello);//true
console.log(hello.__proto__);
console.log(hello.__proto__==Function.prototype);//true
console.log(hello.prototype==hello.__proto__);//false
关于最后为什么不相等,因为左边的prototype对象的constructor指向hello函数,而右边就是Function.prototype对象,他的constructor指向的是一个function,不一样

可见,只有函数才有prototype这个属性,所以继承依靠的是__proto__,
她指的是构造函数的原型对象

在这里插入图片描述
1.在JS里,万物皆对象。方法(Function)是对象,方法的原型(Function.prototype)是对象。因此,它们都会具有对象共有的特点。
即:对象具有属性__proto__,可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。

2.方法(Function)
方法这个特殊的对象,除了和其他对象一样有上述_proto_属性之外,还有自己特有的属性——原型属性(prototype),这个属性是一个指针,指向一个对象,这个对象的用途就是包含所有实例共享的属性和方法(我们把这个对象叫做原型对象)。原型对象也有一个属性,叫做constructor,这个属性包含了一个指针,指回原构造函数。

1.构造函数Foo()
构造函数的原型属性Foo.prototype指向了原型对象,在原型对象里有共有的方法,所有构造函数声明的实例(这里是f1,f2)都可以共享这个方法。

2.原型对象Foo.prototype
Foo.prototype保存着实例共享的方法,有一个指针constructor指回构造函数。

3.实例
f1和f2是Foo这个对象的两个实例,这两个对象也有属性__proto__,指向构造函数的原型对象,这样子就可以像上面1所说的访问原型对象的所有方法啦。

另外:
构造函数Foo()除了是方法,也是对象啊,它也有__proto__属性,指向谁呢?
指向它的构造函数的原型对象呗。函数的构造函数不就是Function嘛,因此这里的__proto__指向了Function.prototype。
其实除了Foo(),Function(), Object()也是一样的道理。

原型对象也是对象啊,它的__proto__属性,又指向谁呢?
同理,指向它的构造函数的原型对象呗。这里是Object.prototype.

最后,Object.prototype的__proto__属性指向null。

1.对象有属性__proto__,指向该对象的构造函数的原型对象。
2.方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。

所以:

var F=function(){};
Object.prototype.a=function(){};
Function.prototype .b=function(){};
var f=new F();

这个问题涉及到js的原型继承

  1. f._proto _ === f[的构造函数].prototype === F.prototype
  2. F.prototype._proto _ === (F.prototype)[的构造函数].prototype === Object.prototype (所以a能够 通过f.a访问)
  3. f.constructor === F
  4. F._proto _ === F[的构造函数].prototype === Function.prototype (所以b可以通过, f.constructor.b访问到)

注意:
(F.prototype)[的构造函数] === Object
F[的构造函数] === Function

多啰嗦一句( js 的继承靠的是__proto__ ,并不是prototype)
__ proto __是隐式原型,prototype是显式原型

var a=b=3 相当于 var a = 3;b = 3;b是全局的

'abc'.match(/a/);
 RegExpObject.compile(regexp,modifier) 
    //modifier	规定匹配的类型。"g" 用于全局匹配,"i" 表示不区分大小写,"gi" 用于全局不区分大小写的匹配。

undefined和null与任何有意义的值比较返回的都是false,但是null与undefined之间互相比较返回的是true。

false==null //false
null==undefined //true
undefined == undefined //true
null == null //true

只要 协议 、 域名 、 端口 有任何一个 不同, 都被当作是 不同 的域。

关于Javascript中数字的部分知识总结:
1.Javascript中,由于其变量内容不同,变量被分为基本数据类型变量和引用数据类型变量。基本类型变量用八字节内存,存储基本数据类型(数值、布尔值、null和未定义)的值,引用类型变量则只保存对对象、数组和函数等引用类型的值的引用(即内存地址)。
2. JS中的数字是不分类型的,也就是没有byte/int/float/double等的差异。

行内元素
a - 锚点

br - 换行

  • font - 字体设定 ( 不推荐 )
  • img - 图片
  • input - 输入框
  • label - 表格标签
  • textarea - 多行文本输入框

(2)块元素 (block element)

  • address - 地址
  • center - 居中对齐块
  • dl - 定义列表

form - 交互表单

  • h1 - 大标题
  • hr - 水平分隔线
  • ol - 排序表单
  • p - 段落

table - 表格

  • ul - 非排序列表

可变元素
可变元素为根据上下文语境决定该元素为块元素或者内联元素。

  • applet - java applet
  • button - 按钮
  • del - 删除文本
  • iframe - inline frame
  • ins - 插入的文本
  • map - 图片区块 (map)
  • object - object对象
  • script - 客户端脚本

@import 属于 CSS 范畴,只能加载 CSS

<style type="text/css">
@import url(CSS文件路径地址);
</style>

link 支持使用 Javascript 控制 DOM 去改变样式;而 @import 不支持。

<link href="CSSurl路径" rel="stylesheet" type="text/css" />
  1. link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS
  2. import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题
  3. link支持使用js控制DOM去改变样式,而@import不支持
  4. link引入的css同时加载,import引入css将在页面加载完毕后被加载

关于prototype和__proto__

1.Object.prototype是所有对象的爸爸
函数:有个属性prototype,它是一个对象,
该对象的__proto__指向Object.protype,
该对象的constructor指向自己的构造函数
(所有函数都是特殊的对象,是new Function()的实例)

定义对象生成一个__proto__指向这个对象的构造函数的prototype

2.函数的prototype对象可以看成是Object实例的对象,构造函数是Object()

3.如果Object.prototype作为实例对象的话,其原型对象是null,这也是typeof null的结果是object的原因

Object.prototype.__proto__===null;//true

4.如果Function.prototype作为实例对象,他可以看成是new Object()的实例对象,所以Function.prototype的原型对象是Object.prototype

在这里插入图片描述

所有对象都有__proto__属性,函数这个特殊对象除了具有__proto__属性,还有特有的原型属性prototype。prototype对象默认有两个属性,constructor属性和__proto__属性。prototype属性可以给函数和对象添加可共享(继承)的方法、属性,而__proto__是查找某函数或对象的原型链方式。constructor,这个属性包含了一个指针,指回原构造函数。

子类的__proto__表示构造函数的继承,父类
子类的prototype属性的__proto__指向父类的prototype
null没有原型,原型链最后一个环节
子类实例的__proto__的__proto__指向父类实例的__proto__

关于响应式布局

1.布局以及设置meta标签

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">

2.通过媒体查询来设置样式

 @media screen and (max-width:980px){
         #head { … }
         #content { … }
         #footer { … }
    }
@media screen and (min-width:600px) and (max-width:900px){

	}
大的px排前面,小的排后面

实例:
https://blog.csdn.net/yuyuyuyu_/article/details/80324605

流式布局和弹性布局及配合媒体查询 是 响应性布局的最好方式

在现代浏览器中(包括IE8+)中要实现图片随着流动布局相应缩放非常简单,只需要在css中加上这么一句代码:

img { max-width:100%; }

含义是:确保图片的最大宽度不会超过浏览器的窗口或其容器可视部分的宽度,所以当窗口或容器的可视部分变窄时,图片的最大宽度值也会相应的变小,图片本身永远不会覆盖容器。

inline(行内元素):
1.使元素变成行内元素,拥有行内元素的特性,即可以与其他行内元素共享一行,不会独占一行.
2.不能更改元素的height,width的值,大小由内容撑开.
3.可以使用padding上下左右都有效,margin只有left和right产生边距效果,但是top和bottom就不行

block(块级元素):
1.使元素变成块级元素,独占一行,在不设置自己的宽度的情况下,块级元素会默认填满父级元素的宽度.
2.能够改变元素的height,width的值.
3.可以设置padding,margin的各个属性值,top,left,bottom,right都能够产生边距效果.
inline-block(融合行内于块级):
1.使元素变成行内元素,拥有行内元素的特性,即可以与其他行内元素共享一行,不会独占一行.
2.能够改变元素的height,width的值.
3.可以设置padding,margin的各个属性值,top,left,bottom,right都能够产生边距效果.

inline-block布局与float布局相似,但是:
对元素设置display:inline-block ,元素不会脱离文本流,而float就会使得元素脱离文本流,且还有父元素高度坍塌的效果

关于tp框架中的MVC模式

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
  通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
  通常视图是依据模型数据创建的。
Controller(控制器)是应用程序中处理用户交互的部分。
  通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
在这里插入图片描述
客户端向服务器发送请求-----服务器根据请求访问控制器里的方法------通过方法找v层找模板、M层找数据-----找到后通过控制器解析返回客户端

注意:tp框架访问的是方法,不是文件

HTTP 与 HTTPS

HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。

HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。

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

HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。

场景:逛电商商场用户需要使用的时间比较长,需要对用户一段时间的HTTP通信状态进行保存,比如执行一次登陆操作,在30分钟内所有的请求都不需要再次登陆。

1.通过Cookie/Session技术
2.HTTP/1.1持久连接(HTTP keep-alive)方法,只要任意一端没有明确提出断开连接,则保持TCP连接状态,在请求首部字段中的Connection: keep-alive即为表明使用了持久连接

HTTPS有如下特点:
1.内容加密:采用混合加密技术,中间者无法直接查看明文内容
2.验证身份:通过证书认证客户端访问的是自己的服务器
3.保护数据完整性:防止传输的内容被中间人冒充或者篡改

  1. 混合加密:结合非对称加密和对称加密技术。客户端使用对称加密生成密钥对传输数据进行加密,然后使用非对称加密的公钥再对秘钥进行加密,所以网络上传输的数据是被秘钥加密的密文和用公钥加密后的秘密秘钥,因此即使被黑客截取,由于没有私钥,无法获取到加密明文的秘钥,便无法获取到明文数据。

  2. 数字摘要:通过单向hash函数对原文进行哈希,将需加密的明文“摘要”成一串固定长度(如128bit)的密文,不同的明文摘要成的密文其结果总是不相同,同样的明文其摘要必定一致,并且即使知道了摘要也不能反推出明文。

  3. 数字签名技术:数字签名建立在公钥加密体制基础上,是公钥加密技术的另一类应用。它把公钥加密技术和数字摘要结合起来,形成了实用的数字签名技术。
    收方能够证实发送方的真实身份;
    发送方事后不能否认所发送过的报文;
    收方或非法者不能伪造、篡改报文。

数字签名:
1.A先对这封Email执行哈希运算得到hash值简称“数字摘要”,取名h1
2.然后用自己私钥对摘要加密,生成的东西叫“数字签名”
3.把数字签名加在Email正文后面,一起发送给B
(当然,为了防止邮件被窃听你可以用继续公钥加密,这个不属于数字签名范畴)
4.B收到邮件后用A的公钥对数字签名解密,成功则代表Email确实来自A,失败说明有人冒充
5.B对邮件正文执行哈希运算得到hash值,取名h2
6.B 会对比第4步数字签名的hash值h1和自己运算得到的h2,一致则说明邮件未被篡改。

    <meta content="telephone=no,email=no" name="format-detection" />

我们的代码有类似于电话这样的数字的时候,因为有的手机上它会自动转换成可以拨打电话,所以我们加上这句就不会了。

web前端性能优化

  1. 减少http请求
    80%~90%时间花在了下载页面中的所有组件进行的HTTP请求上
    在可以大量使用字体图标的地方我们可以尽可能使用字体图标,字体图标可以减少很多图片的使用,从而减少http请求,字体图标还可以通过CSS来设置颜色、大小等样式
  2. 添加Expires头
    页面的初次访问者会进行很多HTTP请求,但是通过使用一个长久的Expires头,可以使这些组件被缓存,下次访问的时候,就可以减少不必要的HTTP请求,从而提高加载速度。
  3. Web客户端可以通过HTTP请求中的Accept-Encoding头来表示对压缩的支持
    Accept-Encoding: gzip,deflate

如果Web服务器看到请求中有这个头,就会使用客户端列出来的方法中的一种来进行压缩。Web服务器通过响应中的Content-Encoding来通知 Web客户端。

  1. 将样式表放在头部,将脚本放在底部
    为了避免当样式变化时重绘页面元素,浏览器会阻塞内容逐步呈现,造成“白屏”。这源自浏览器的行为:如果样式表仍在加载,构建呈现树就是一种浪费
    js的下载和执行会阻塞Dom树的构建(严谨地说是中断了Dom树的更新),所以script标签放在首屏范围内的HTML代码段里会截断首屏的内容

  2. 避免CSS表达式
    css表达式是不被人推荐的网页性能杀手,没有了解的必要

  3. 使用外部的JavaScript和CSS
    1、每个用户产生的页面浏览量越少,内联脚本和样式的论据越强势。譬如一个用户每个月只访问你的网站一两次,那么这种情况下内联将会更好。而如果该用户能够产生很多页面浏览量,那么缓存的样式和脚本将会极大减少下载的时间,提交页面加载速度。

    2、如果你的网站不同的页面之间使用的组件大致相同,那么使用外部文件可以提高这些组件的重用率。

  4. 减少DNS查找,避免重定向
    DNS也是开销,通常浏览器查找一个给定域名的IP地址要花费20~120毫秒,在完成域名解析之前,浏览器不能从服务器加载到任何东西。那么如何减少域名解析时间,加快页面加载速度呢?

    当客户端DNS缓存(浏览器和操作系统)缓存为空时,DNS查找的数量与要加载的Web页面中唯一主机名的数量相同,包括页面URL、脚本、样式表、图片、Flash对象等的主机名。减少主机名(就是IP)的 数量就可以减少DNS查找的数量。

  5. 避免重定向
    重定向用于将用户从一个URL重新路由到另一个URL

    常用重定向的类型

    301:永久重定向,主要用于当网站的域名发生变更之后,告诉搜索引擎域名已经变更了,应该把旧域名的的数据和链接数转移到新域名下,从而不会让网站的排名因域名变更而受到影响。

    302:临时重定向,主要实现post请求后告知浏览器转移到新的URL。

    304:Not Modified,主要用于当浏览器在其缓存中保留了组件的一个副本,同时组件已经过期了,这是浏览器就会生成一个条件GET请求,如果服务器的组件并没有修改过,则会返回304状态码,同时不携带主体,告知浏览器可以重用这个副本,减少响应大小。

    当页面发生了重定向,就会延迟整个HTML文档的传输。在HTML文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载。

  6. 使Ajax可缓存
    什么样的AJAX请求可以被缓存?

    POST的请求,是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200。(可以在服务器端对数据进行缓存,以便提高处理速度)

    GET的请求,是可以(而且默认)在客户端进行缓存的,除非指定了不同的地址,否则同一个地址的AJAX请求,不会重复在服务器执行,而是返回304。

  7. 惰性加载图片
    这条策略实际上并不一定能减少 HTTP请求数,但是却能在某些条件下或者页面刚加载时减少 HTTP请求数。对于图片而言,在页面刚加载的时候可以只加载第一屏,当用户继续往后滚屏的时候才加载后续的图片。这样一来,假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了。 有啊首页 曾经的做法是在加载的时候把第一屏之后的图片地址缓存在 Textarea标签中,待用户往下滚屏的时候才 “惰性” 加载。

  8. 减少dom操作
    (1)在脚本中 document.images、document.forms 、getElementsByTagName()返回的都是 HTMLCollection类型的集合,不过在访问性能上则比数组要差很多,原因是这个集合并不是一个静态的结果,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个查询从而更新查询结果;

    (2)减少考虑浏览器的 Reflow和Repaint,对于DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow;当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为repaint

    (3)慎用 with,with(obj){ p = 1}; 代码块的行为实际上是修改了代码块中的 执行环境 ,将obj放在了其作用域链的最前端,在 with代码块中访问非局部变量是都是先从 obj上开始查找,如果没有再依次按作用域链向上查找,因此使用 with相当于增加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。

    (4)避免使用 eval和 Function,每次 eval 或 Function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作 —— 通常比简单的函数调用慢 100倍以上。
      eval 函数效率特别低,由于事先无法知晓传给 eval 的字符串中的内容,eval在其上下文中解释要处理的代码,也就是说编译器无法优化上下文,因此只能有浏览器在运行时解释代码。这对性能影响很大。

    (5)减少作用域链的查找
    一可以用局部变量缓存全局变量 ,二要减少作用域链查找还应该减少闭包的使用。
    (6)字符串拼接
    在 Javascript中使用"+" 号来拼接字符串效率是比较低的,因为每次运行都会开辟新的内存并生成新的字符串变量,然后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的 join方法,即将需要拼接的字符串放在数组中最后调用其 join方法得到结果。不过由于使用数组也有一定的开销,因此当需要拼接的字符串较多的时候可以考虑用此方法。

  9. 添加Expires 或Cache-Control报文头
    对于静态内容添加Expires,将静态内容设为永不过期,或者很长时间以后。
    对于动态内容应用合适的Cache-Control,让浏览器根据条件来发送请求。

  10. 删除重复脚本
    在团队开发一个项目时,由于不同开发者之间都可能会向页面中添加页面或组件,因此可能相同的脚本会被添加多次。
    重复的脚本会造成不必要的HTTP请求(如果没有缓存该脚本的话),并且执行多余的JavaScript浪费时间,还有可能造成错误。

  11. 配置ETag
    实体标签(EntityTag)是唯一标识了一个组件的一个特定版本的字符串,是web服务器用于确认缓存组件的有效性的一种机制,通常可以使用组件的某些属性来构造它。
    ETag提供了另外一种方式,用于检测浏览器缓存中的组件与原始服务器上的组件是否匹配。

当我们在地址栏输入地址回车后的过程

也可以问成(发一个ajax请求到后台,那么会经过哪些阶段才会重新回到前端)其实都是去服务器请求资源
域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

也有一些以上被省略的步骤

  1. 建立TCP连接

  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连接
    一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive

TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

域名解析:
首先搜索浏览器自身的DNS缓存,然后再搜索操作系统自身的DNS缓存,然后再尝试读取hosts文件,然后再去本地配置的首选DNS服务器(一般是电信运营商提供的,也可以使用像Google提供的DNS服务器)发起域名解析请求

建立TCP连接后发起http请求
HTTP请求报文的方法是get方式,如果浏览器存储了该域名下的Cookies,那么会把Cookies放入HTTP请求头里发给服务器。

浏览器解析html代码,并请求html代码中的资源
浏览器拿到index.html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这个时候就用上keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里的顺序,但是由于每个资源大小不一样,而浏览器又多线程请求资源,所以从下图看出,这里显示的顺序并不一定是代码里面的顺序。

浏览器在请求静态资源时(在未过期的情况下),向服务器端发起一个http请求(询问自从上一次修改时间到现在有没有对资源进行修改),如果服务器端返回304状态码(告诉浏览器服务器端没有修改),那么浏览器会直接读取本地的该资源的缓存文件。

什么是reflow和repaint

浏览器解析分为四个步骤:

1、解析HTML以构建DOM树:渲染引擎开始解析HTML文档,转换树中的html标签或js生成的标签到DOM节点,它被称为 – 内容树。

2、构建渲染树:解析CSS(包括外部CSS文件和样式元素以及js生成的样式),一般来说,浏览器会先查找内联样式,然后应用CSS文件中定义的样式,最后再是应用浏览器默认样式。根据CSS选择器计算出节点的样式,创建另一个树 —- 渲染树。

3、布局渲染树reflow: 从根节点递归调用,计算每一个元素的大小、位置等,给每个节点所应该出现在屏幕上的精确坐标。

4、绘制渲染树:repaint 遍历渲染树,每个节点将使用UI后端层来绘制。

5、浏览器将各层的信息发送给GPU,GPU将各层合成,显示在屏幕上。

对于DOM结构中的各个元素都有自己的盒子(模型),这些都需要浏览器根据各种样式(浏览器的、开发人员定义的等)来计算并根据计算结果将元素放到它该出现的位置,这个过程称之为reflow;当各种盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来后,浏览器于是便把这些元素都按照各自的特性绘制了一遍,于是页面的内容出现了,这个过程称之为repaint。

reflow : 只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲染。通常我们无法预估浏览器到底会 reflow 哪一部分的代码,它们彼此相互影响。

repaint : 如果只是改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性,将只会引起浏览器 repaint(重绘)。repaint 的速度明显快于 reflow

reflow一定引起repaint,而repaint不一定要reflow。reflow的成本比repaint高很多,DOM tree里每个结点的reflow很可能触发其子结点、祖先结点、兄弟结点的reflow。reflow(回流)是导致DOM脚本执行低效的关键因素之一。

当一个元素的外观的可见性visibility发生改变的时候,重绘(repaint)也随之发生,但是不影响布局。类似的例子包括:outline, visibility, or background color。根据Opera浏览器,重绘的代价是高昂的,因为浏览器必须验证DOM树上其他节点元素的可见性。而回流更是性能的关键因为其变化涉及到部分页面(或是整个页面)的布局。一个元素的回流导致了其所有子元素以及DOM中紧随其后的祖先元素的随后的回流。

引起reflow和repaint的操作

Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。

如果渲染树的结点发生了结构性变化,例如宽度、高度或者位置上的变化时,那么会触发Reflow(回流)的逻辑。我们第一次进入一个页面时便会至少触发一次这个逻辑。
如果渲染树结点发生了非结构性变化,例如背景色等的变化时,那么会触发Repaint(重绘)的逻辑。

增加、删除、修改DOM结点
使用display:none;的方式隐藏一个结点会导致repaint与reflow,使用visibility:hidden;进行dom隐藏仅仅导致repaint (没有结构性变化,仅仅看不见而已)
移动dom或着该dom进行动画
添加新的样式,或者修改某个样式
用户的一些操作诸如改变浏览器窗口大小,调整字体大小,滚动等等

新的CSS style修改CSSOM,会重新渲染页面 ----CSS文件应放在头部,缩短首次渲染时间

遇到在这里插入图片描述会发出请求,但不会阻塞,服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;(最好图片都设置尺寸,避免重新渲染)

遇到 < script > 标签,会立即执行js代码,阻塞渲染。(script最好放置页面最下面)

js修改DOM会重新渲染。 (页面初始化样式不要使用js控制)

引发reflow的操作:

导致reflow:

1.调整窗口大小(Resizing the window)

2.改变字体(Changing the font)

3.增加或者移除样式表(Adding or removing a stylesheet)

4.内容变化,比如用户在input框中输入文字

5.激活 CSS 伪类,比如 :hover (IE 中为兄弟结点伪类的激活)

6.操作 class 属性(Manipulating the class attribute)

7.脚本操作 DOM(A script manipulating the DOM)

8.计算 offsetWidth 和 offsetHeight 属性(Calculating offsetWidth and offsetHeight)

9.设置 style 属性的值 (Setting a property of the style attribute)

如何减少reflow和repaint

  1. 避免在document上直接进行频繁的DOM操作,如果确实需要可以采用以下几种:

(1). 先将元素从document中删除,完成修改后再把元素放回原来的位置

(2). 将元素的display设置为”none”(只发生一次reflow),完成修改后再把display修改为原来的值,期间不触发reflow和repaint事件

(3). 如果需要创建多个DOM节点,可以使用DocumentFragment创建完后一次性的加入document

(4)clone 一个 DOM 节点到内存里,然后想怎么改就怎么改,改完后,和在线的那个的交换一下。

  1. 集中修改样式

(1). 尽可能少的修改元素style上的属性

(2). 尽量通过修改className来修改样式

  1. 缓存Layout属性值

对于Layout属性中非引用类型的值(数字型),如果需要多次访问则可以在一次访问时先存储到局部变量中,之后都使用局部变量,这样可以避免每次读取属性时造成浏览器的渲染。

  1. 设置元素的position为absolute或fixed

在元素的position为static和relative时,元素处于DOM树结构当中,当对元素的某个操作需要重新渲染时,浏览器会渲染整个页面。将元素的position设置为absolute和fixed可以使元素从DOM树结构中脱离出来独立的存在,而浏览器在需要渲染时只需要渲染该元素以及位于该元素下方的元素,从而在某种程度上缩短浏览器渲染时间,这在当今越来越多的Javascript动画方面尤其值得考虑。

5.不要把 DOM 节点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性。

6.尽可能的修改层级比较低的 DOM节点。当然,改变层级比较底的 DOM节点有可能会造成大面积的 reflow,但是也可能影响范围很小。

7.千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。

8.减少不必要的 DOM 层级(DOM depth)。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面。

9.避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU。

10.script 标签的位置很重要。实际使用时,可以遵循下面两个原则:

  1. CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。

  2. JavaScript 应尽量少影响 DOM 的构建。

关于position属性详解

1.static定位是HTML元素的默认值,即没有定位,元素出现在正常的流中,因此,这种定位就不会收到top,bottom,left,right的影响。

2.fixed定位是指元素的位置相对于浏览器窗口是固定位置,即使窗口是滚动的它也不会滚动,脱离文档流,且fixed定位使元素的位置与文档流无关,因此不占据空间,且它会和其他元素发生重叠。(应用场景:登陆框)

3.relative定位元素的定位是相对它自己的正常位置的定位,其正常位置的空间会一直为他保留着,相对移动之后,不会对下面的其他元素造成影响,不像absolute会让后面的元素后来居上

4.absolute定位的元素相对于最近的已定位的父元素,如果元素没有已定位的父元素,那么它的位置相对于 < html >。
当某个absolute定位元素的父元素具有position:relative/absolute/fixed时,定位元素都会依据父元素而定位,而父元素没有设置position属性或者设置了默认属性,那么定位属性会依据html元素来定位。

5.重叠的元素–z-index属性:z-index只能在position属性值为relative或absolute或fixed的元素上有效。z-index的值可以控制定位元素在垂直于显示屏幕方向(z轴)上的堆叠顺序(stack order),值大的元素发生重叠时会在值小的元素上面

CSS实现水平垂直居中

水平居中
1.对于行内元素
父div设置 text - align : center,里面的li元素设置display :inline-block

2.浮动实现水平居中

  		 .mydiv{
            float: left;
            width: 100%;
            overflow: hidden; 
            position: relative;
        }

        .mydiv ul{
            clear: left; 
            float: left; 
            position: relative;
             left: 50%;/*整个分页向右边移动宽度的50%*/ 
            text-align: center;
        }

        .mydiv li{
            display: block; //去掉默认li样式
            float: left; 
            position: relative; 
            right: 50%;/*将每个分页项向左边移动宽度的50%*/
            line-height: 25px;
            margin: 0 5px;
        }

这种方式的关键:
	 .parent{
            position: relative;
            float:left;
            width: 100%;
        }
        .child{
            position: relative;
            float: left;
            left: 50%;/*整个分页向右边移动宽度的50%*/ 
        }
        父和子div浮动之后宽度都会变成wrap content,
        不会像之前那样子一个div独占一行
        所以需要设计父width为100%

3.绝对定位实现水平居中(最常见的方式)

		.mydiv{
           position: relative;
        }

        .mydiv ul{
            position: absolute;
            left: 50%;/*整个分页向右边移动宽度的50%*/ 
            
        }

        .mydiv li{
            float: left;
            display: block;
            position: relative;/*注意,这里不能是absolute,大家懂的*/ 
            right: 50%;
            line-height: 25px;
            margin: 0 5px;
        }

或者:

.content{

	position: absolute;
	
	left: 50%;
	
	transform: translateX(-50%); /* 移动元素本身50% */
	

}

4.CSS3的fit-content配合左右margin为auto实现水平居中方法

.content{

		width: fit-content;
		
		margin-left: auto;
		
		margin-right: auto;
(这种方法对ul有效,对div无效,因为ul本身里面自带内容,div里面是无内容的)
}

5.使用flex布局

 <div class="parent">
        <div class="children">我怀念的</div>
    </div>

	 .parent{
           display: flex;
           flex-direction: column;
            align-items: center;
        }
        .children{
			 width: 20px;
             background-color: aquamarine;
        }

6.当知道子元素宽度时,可以使用table-cell

 .parent{
           display: table-cell;
  }
   
   .children{
           margin: 0 auto;
           width: 80px;   //children必须有width
   }

7.绝对定位配合0

	 .children{     
           width: 80px;  //一定要设置width才能生效
           position: absolute;
           left: 0;
           right:0;
           margin: auto;
        }
        //这个方法在垂直居中那里也有一模一样的

垂直居中
1.使用table-cell

div{
 			display: table-cell;
            vertical-align: middle;
            text-align: center;
}    

   <div>
       <span>垂直居中</span>
    </div>

2.绝对定位和负边距(负边距这个和transform的效果几乎一样)

		div{
            position:relative;    //父元素相对定位
        }
        div span{
            position: absolute;
            top:50%;
            left:50%;
            // left实现的是水平居中 需要根据情况加上translateX
            或者margin-left或者margin-top设置为负边距
            transform: translateY(-50%);
        }

3.绝对定位和0

div span{
            margin: auto; 
            position: absolute; 
            top: 0; left: 0; bottom: 0; right: 0; 
            
            //前提需要设置top和bottom为0
             height: 40px;/*有设置高度就可以垂直居中*/ 
             
             //前提需要设置left和right为0
             width:80px;/*有设置宽度就可以水平居中 */
             line-height: 40px; //这里只是为了实现元素内部文字垂直居中
        }

插个额外的知识点,overflow设置为auto表示只有在需要时元素才会出现滚动条

4.绝对定位结合margin: auto(其实3和4是一样的,top和bottom设置为0可以实现垂直居中,left和right设置为0可以实现水平居中)

<div id="box">
        <div id="child">垂直居中</div>
    </div> 

  		#box {
            position: relative;
        }
        #child {
            height: 100px;  //这个方法的关键在于必须设置高度
            position: absolute;
            top: 0;
            bottom: 0;
            margin: auto; 
        }

5.使用padding实现子元素的垂直居中

#box {
 	不能设置height不然一定会出错!!
 	
    padding: 100px 0;//此处实现的关键是父div不能设置高度height
}
#child {
   	子元素不设置宽高也完全ok
}

6.还有一种在前面已经见到过很多次的方式就是使用 line-height 对单行文本进行垂直居中

#box{
    height: 300px;
    line-height: 300px;//此处line-height不能为百分比
    //因为line-height的百分比是相对font-size的
}

7.使用flex布局

 .parent{
           display:flex;
           flex-direction: column;
           justify-content: center;
  }

CSS可以继承的属性和不能继承的属性

不可继承的:

1.display

2、文本属性:

vertical-align:垂直文本对齐

text-decoration:规定添加到文本的装饰

text-shadow:文本阴影效果

3、盒子模型的属性:width、height、margin 、border 、padding

4、背景属性:background、background-color、background-image等等

5、定位属性:float、clear、position、top、right、bottom、left等

6、生成内容属性:content、counter-reset、counter-increment

有继承性的属性
1、字体系列属性

font:组合字体

font-family:规定元素的字体系列

font-weight:设置字体的粗细

font-size:设置字体的尺寸

font-style:定义字体的风格

2、文本系列属性

text-indent:文本缩进

color:文本颜色

text-align:文本水平对齐

line-height:行高

text-transform:控制文本大小写

direction:规定文本的书写方向

3、元素可见性:visibility

4、表格布局属性:caption-side、border-collapse、border-spacing、empty-cells、table-layout

5、列表布局属性:list-style-type、list-style-image、list-style-position、list-style

6、光标属性:cursor

关于可继承的属性:
所有元素可继承:visibility和cursor。
内联元素可继承:letter-spacing、word-spacing、white-space、line-height、color、font、font-family、font-size、font-style、font-variant、font-weight、text-decoration、text-transform、direction。
终端块状元素可继承:text-indent和text-align。
列表元素可继承:list-style、list-style-type、list-style-position、list-style-image。

改变阻塞模式:defer 与 async

defer

<script src="app1.js" defer></script>

defer属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。

defer不会改变 script 中代码的执行顺序,示例代码会按照 1、2、3 的顺序执行。所以,defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。

async

<script src="app.js" async></script>

async属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。

从上一段也能推出,多个 async-script 的执行顺序是不确定的。值得注意的是,向 document 动态添加 script 标签时,async 属性默认是 true,使用 document.createElement 创建的 script 默认是异步的

关于js是单线程语言

进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位)

JS为什么是单线程?
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

JS单线程又是如何实现异步的呢?

js引擎执行异步代码而不用等待,是因有为有 消息队列和事件循环。

异步过程的回调函数,一定不在当前这一轮事件循环中执行。

是通过的事件循环(event loop):

JS的执行机制是(并不准确的一种判断方式)
1.首先判断JS是同步还是异步,同步就进入主进程,异步就进入event table
2.异步任务在event table中注册函数,当满足触发条件后,被推入event queue
3.同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主进程中
以上三步循环执行,这就是event loop

但实际上,上面的说法并不完全准确,准确的划分方式是:

macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick

JS的执行机制是
1.执行一个宏任务,过程中如果遇到微任务,就将其放到微任务的【事件队列】里
2.当前宏任务执行完成后,会查看微任务的【事件队列】,并将里面全部的微任务依次执行完

JS中分为两种任务类型:macrotask和microtask,在ECMAScript中,microtask称为jobs,macrotask可称为task
它们的定义?区别?简单点可以按如下理解:

macrotask(又称之为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)
每一个task会从头到尾将这个任务执行完毕,不会执行其它
浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染
(task->渲染->task->…)

microtask(又称为微任务),可以理解是在当前 task 执行结束后立即执行的任务
也就是说,在当前task任务后,下一个task之前,在渲染之前
所以它的响应速度相比setTimeout(setTimeout是task)会更快,因为无需等渲染
也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的所有microtask都执行完毕(在渲染前)

分别很么样的场景会形成macrotask和microtask呢?
macroTask: 主代码块, setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering(可以看到,事件队列中的每一个事件都是一个macrotask)
microTask: process.nextTick, Promise, Object.observe, MutationObserver
补充:在node环境下,process.nextTick的优先级高于Promise,也就是可以简单理解为:在宏任务结束后会先执行微任务队列中的nextTickQueue部分,然后才会执行微任务中的Promise部分。process.nextTick方法可以在当前”执行栈”的尾部—-下一次Event Loop(主线程读取”任务队列”)之前—-触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。setImmediate方法则是在当前”任务队列”的尾部添加事件,也就是说,它指定的任务总是在下一次Event Loop时执行,这与setTimeout(fn, 0)很像。
如果有多个process.nextTick语句(不管它们是否嵌套),将全部在当前”执行栈”执行,所以一旦嵌套nextTick将会很恐怖的一直执行不停止

另外,setImmediate则是规定:在下一次Event Loop(宏任务)时触发(所以它是属于优先级较高的宏任务),(Node.js文档中称,setImmediate指定的回调函数,总是排在setTimeout前面),所以setImmediate如果嵌套的话,是需要经过多个Loop才能完成的,而不会像process.nextTick一样没完没了。

Node.js也是单线程的Event Loop,但是它的运行机制不同于浏览器环境。

一道笔试题目:

console.log('main1');

process.nextTick(function() {
    console.log('process.nextTick1');
});

setTimeout(function() {
    console.log('setTimeout');
    process.nextTick(function() {
        console.log('process.nextTick2');
    });
}, 0);

new Promise(function(resolve, reject) {
    console.log('promise');
    resolve();
}).then(function() {
    console.log('promise then');
});

console.log('main2');

main1
promise//这里会是promise的原因是因为Promise实例创建了之后就会立刻执行
main2
process.nextTick1
promise then
setTimeout
process.nextTick2

关于event loop的补充说明:
主线程运行的时候,产生堆和栈,栈中的代码调用各种外部API,异步操作执行完成后,就在消息队列中排队。只要栈中的代码执行完毕,主线程就会去读取消息队列,依次执行那些异步任务所对应的回调函数
在这里插入图片描述
详细步骤如下:
1、所有同步任务都在主线程上执行,形成一个执行栈

2、主线程之外,还存在一个"消息队列"。只要异步操作执行完成,就到消息队列中排队

3、一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取消息队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行

4、主线程不断重复上面的第三步

从代码执行顺序的角度来看,程序最开始是按代码顺序执行代码的,遇到同步任务,立刻执行;遇到异步任务,则只是调用异步函数发起异步请求。此时,异步任务开始执行异步操作,执行完成后到消息队列中排队。程序按照代码顺序执行完毕后,查询消息队列中是否有等待的消息。如果有,则按照次序从消息队列中把消息放到执行栈中执行。执行完毕后,再从消息队列中获取消息,再执行,不断重复。
在这里插入图片描述

js判断数组类型的方法:

1.使用instanceof方法
var arr=[];
console.log(arr instanceof Array) //返回true

2.使用constructor方法
console.log([].constructor == Array); //true

3.使用Object.prototype.toString.call(arr) === '[object Array]'方法

4.ES6新增了Array.isArray:
Array.isArray([]) //true

typeof不具备判断功能
(typeof [] === “object”)

BOM和DOM的区别:

BOM:

  1. BOM是Browser Object Model的缩写,即浏览器对象模型。

  2. BOM没有相关标准。

  3. BOM的最根本对象是window。

DOM

  1. DOM是Document Object Model的缩写,即文档对象模型。

  2. DOM是W3C的标准。

  3. DOM最根本对象是document(实际上是window.document)

DOM的最根本的对象是BOM的window对象的子对象.
DOM的window对象在BOM角度为js访问浏览器提供接口,而在global角度为window对象充当global对象

TCP三次握手和四次挥手

序列号seq:序列号seq就是这个报文段中的第一个字节的数据编号。

确认号ack:期待收到对方下一个报文段的第一个数据字节的序号;序列号表示报文段携带数据的第一个字节的编号;而确认号指的是期望接收到下一个字节的编号;因此当前报文段最后一个字节的编号+1即为确认号。

确认ACK:占1位,仅当ACK=1时,确认号字段才有效。ACK=0时,确认号无效

同步SYN:连接建立时用于同步序号。当SYN=1,ACK=0时表示:这是一个连接请求报文段。若同意连接,则在响应报文段中使得SYN=1,ACK=1。因此,SYN=1表示这是一个连接请求,或连接接受报文。SYN这个标志位只有在TCP建产连接时才会被置1,握手完成后SYN标志位被置0。

终止FIN:用来释放一个连接。FIN=1表示:此报文段的发送方的数据已经发送完毕,并要求释放运输连接
在这里插入图片描述

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

在这里插入图片描述

1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。

2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。

4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。

5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

(请注意FIN-WAIT、CLOSE-WAIT、TIME-WAIT的位置)

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

【问题3】为什么不能用两次握手进行连接?

答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

URI和URL

URI强调的是给资源标记命名,比如张三,URL强调的是给资源定位,比如张三的地址,但是你会发现,URL显然比URI包含信息更多,我通过URL也可以知道张三是总经理,并且我还知道了他的地址,所以大多数情况下大家觉得给一个网络资源分别命名和给出地址太麻烦,干脆就用地址既当地址用,又当标记名用,所以,URL也充当了WWW万维网里面URI的角色,但是他比URI多了一层意义,我不光知道你叫什么,我还知道你在哪里。我们在浏览器输入的都是URL,因为我们输入的目的是为了找到某一个资源,当然你输入的是URI也是没错的,因为URL也是URI。

总结:URI标记了一个网络资源,仅此而已; URL标记了一个WWW互联网资源(用地址标记),并给出了他的访问地址。(URI是Uniform Resource Identifier,表示是一个资源; URL是Uniform Resource Locator,表示是一个地址,光看英文缩写确实难懂)

HTTP状态码

1xx 消息
服务器收到请求,需要请求者继续执行操作,表示临时响应并需要请求者继续执行操作的状态代码。

100(继续)
请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分。客户端应该继续请求
101(切换协议)
切换协议。请求者已要求服务器切换协议,服务器已确认并准备切换。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
102
服务器已经收到请求并正在处理
103
恢复终止的PUT或POST请求
122
URI长度超过2083个字符

2xx 成功,操作被成功接收并处理

200(成功)
请求成功,正常响应,一般用于GET与POST请求

201(已创建)
请求已完成,新的资源被创建
202(已接受)
已经接受请求,但未处理完成
203
非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204(无内容)
无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档

205
重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206(部分内容)
服务器成功处理了部分 GET 请求
207
XML消息,可以包含一系列独立的响应

3xx 重定向,需要进一步的操作以完成请求

300
客户请求的文档可以在多个位置找到
301(永久重定向)
客户请求的文档已经被永久移动到其他地方,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替

302
客户请求的文档被临时移动到其他地方,客户端应继续使用原有URI
服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
303
对应当前请求的响应可以在另一个URI上被找到
请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
304
请求资源从上次请求以来未被修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源

305
客户请求的文档应该通过指定的代理服务器提取,所请求的资源必须通过代理访问
306
在最新版的规范中,306状态码已经不再被使用

307
请求的资源现在临时从不同的URI响应请求,与302类似
服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。

4xx 请求错误,请求包含语法错误或无法完成请求

400
请求包含语法错误
401(未授权)
访问被拒绝,客户试图未经授权访问受密码保护的页面,请求要求用户的身份认证
402
该状态码是为了将来可能的需求而预留的
403(forbidden)
资源不可用,服务器理解请求客户端的请求,但是拒绝执行此请求

404(Not found)
请求的资源在服务器上不存在,服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面

405(方法禁用)
请求方法(GET、POST、HEAD等)对指定的资源不适用,客户端请求中的方法被禁止
406(不接受)
请求资源的MIME类型和客户在Accpet头中所指定的不兼容,无法使用请求的内容特性响应请求的网页
407
要求进行代理身份验证
408(请求超时)
在服务器许可的等待时间内,客户一直没有发出任何请求,服务器等待客户端发送的请求时间过长,超时
409(冲突)
由于请求和资源的当前状态相冲突,因此请求不能成功
410(已删除)
请求资源不可用,410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411(需要有效长度)
请求没有指定被请求资源的长度,服务器无法处理客户端发送的不带Content-Length的请求信息
412
请求头中指定的一些前提条件失败,服务器未满足请求者在请求中设置的其中一个前提条件
413
目标文档的大小超过服务器当前愿意处理的大小
414
URI长度超过服务器能够处理长度的上限
415
不支持的MIME媒体类型
416
服务器不能处理客户在请求中指定的Range头,客户端请求的范围无效
417
在请求头Expect中指定的预期内容无法满足服务器
422
语义错误,无法响应请求
423
请求资源被锁定
424
由于之前的某个请求发生的错误,导致当前请求失败
426
客户端应当切换到TLS/1.0

5xx 服务器错误,服务器在处理请求的过程中发生了错误

500
服务器内部错误,遇到了意料不到的情况,不能完成客户的请求

501
服务器不支持实现请求所需要的功能
502(错误网关)
从上游服务器接收到无效的响应,作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503(服务不可用)
服务不可用,由于超载或系统维护,服务器暂时的无法处理客户端的请求,当然只是暂时。延时的长度可包含在服务器的Retry-After头信息中
504(网关超时)
网关不能及时地从远程服务器获得应答
505
服务器不支持请求中使用的HTTP版本
507
服务器无法存储完成请求所必须的内容
508
服务器处理请求时检测到一个无限循环
509
超过带宽的限制 服务器暂时无法提供服务
511
客户端需要进行身份验证以获得网络访问

JS垃圾回收机制

1.标记清除(mark and sweep)
这是javascript中最常用的垃圾回收方式。当变量进入执行环境是,就标记这个变量为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到他们。当变量离开环境时,则将其标记为“离开环境”。
  垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

2.引用计数(reference counting)
在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

什么时候触发垃圾回收

垃圾回收器周期性运行,如果分配的内存非常多,那么回收工作也会很艰巨,确定垃圾回收时间间隔就变成了一个值得思考的问题。IE6的垃圾回收是根据内存分配量运行的,当环境中存在256个变量、4096个对象、64k的字符串任意一种情况的时候就会触发垃圾回收器工作,看起来很科学,不用按一段时间就调用一次,有时候会没必要,这样按需调用不是很好吗?但是如果环境中就是有这么多变量等一直存在,现在脚本如此复杂,很正常,那么结果就是垃圾回收器一直在工作,这样浏览器就没法儿玩儿了。

微软在IE7中做了调整,触发条件不再是固定的,而是动态修改的,初始值和IE6相同,如果垃圾回收器回收的内存分配量低于程序占用内存的15%,说明大部分内存不可被回收,设的垃圾回收触发条件过于敏感,这时候把临街条件翻倍,如果回收的内存高于85%,说明大部分内存早就该清理了,这时候把触发条件置回。这样就使垃圾回收工作职能了很多。

var a = "before";
var b = "override a";
var a = b; //重写a

这段代码运行之后,“before”这个字符串失去了引用(之前是被a引用),系统检测到这个事实之后,就会释放该字符串的存储空间以便这些空间可以被再利用。

我们知道,IE中有一部分对象并不是原生JavaScript对象。例如,其BOM和DOM中的对象就是使用C++以COM(Component Object
Model,组件对象)对象的形式实现的,而COM对象的垃圾回收器就是采用的引用计数的策略。因此,即使IE的Javascript引擎使用标记清除的策略来实现的,但JavaScript访问的COM对象依然是基于引用计数的策略的。说白了,只要IE中涉及COM对象,就会存在循环引用的问题。

减少JavaScript中的垃圾回收
1、对象object优化
为了最大限度的实现对象的重用,应该像避使用new语句一样避免使用{}来新建对象。遍历此对象的所有属性,并逐个删除,最终将对象清理为一个空对象。

cr.wipe(obj)方法就是为此功能而生,有些时候,你可以使用cr.wipe(obj)方法清理对象,再为obj添加新的属性,就可以达到重复利用对象的目的。虽然通过清空一个对象来获取“新对象”的做法,比简单的通过{}来创建对象要耗时一些,但是在实时性要求很高的代码中,这一点短暂的时间消耗,将会有效的减少垃圾堆积,并且最终避免垃圾回收暂停,这是非常值得的!

2、数组array优化
  将[]赋值给一个数组对象,是清空数组的捷径(例如: arr = [];),但是需要注意的是,这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!实际上,将数组长度赋值为0(arr.length = 0)也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。

3、方法function优化

setTimeout(
    (function(self) {                    
      return function () {
              self.tick();
    };
})(this), 16)

每一次调用都返回了一个新的方法对象,这就导致了大量的方法对象垃圾!

// at startup
this.tickFunc = (
    function(self) {
      return function() {
                self.tick();
      };
    }
)(this);

// in the tick() function
setTimeout(this.tickFunc, 16);

JavaScript有内置的垃圾回收机制,主要是使用算法实现的,基本的垃圾回收算法有四种:
(1).标记-清除算法(mark-sweep)。
(2).标记-压缩算法(mark-compact)。
(3).复制算法(copying)。
(4).引用计数算法(reference counting)。
当前浏览器的垃圾回收机制通常是由上述几种算法混合而成

JS闭包

这种即使离开函数作用域的情况下仍然能够通过引用调用内部函数的事实,意味着只要存在调用内部函数的可能,JavaScript就需要保留被引用的函数。而且JavaScript运行时需要跟踪引用这个内部函数的所有变量,直到最后一个变量废弃,JavaScript的垃圾收集器才能释放相应的内存空间。

闭包是指有权限访问另一个函数作用域的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数

函数被执行时(executed)使用的作用域链(scope chain)是被定义时的scope chain,而不是执行时的scope chain,就可以很容易的理解闭包的行为了。

JS什么情况下要用到闭包
闭包经典使用场景一:通过循环给页面上多个dom节点绑定事件

场景描述:假如页面上有5个button,要给button绑定onclick事件,点击的时候,弹出对应button的索引编号。

Tip: 在js中,没有块级作用域 ,只有函数作用域。可以采用“立即执行函数Immediately-Invoked Function Expression (IIFE)”的方式创建作用域。

for(var i = 0, len = btns.length; i < len; i++) {
    (function(i) {
        btns[i].onclick = function() {
            alert(i);
        }
    })(i);
}

闭包使用场景二:封装变量

闭包可以将一些不希望暴露在全局的变量封装成“私有变量”。

假如有一个计算乘积的函数,mult函数接收一些number类型的参数,并返回乘积结果。为了提高函数性能,我们增加缓存机制,将之前计算过的结果缓存起来,下次遇到同样的参数,就可以直接返回结果,而不需要参与运算。这里,存放缓存结果的变量不需要暴露给外界,并且需要在函数运行结束后,仍然保存,所以可以采用闭包。

var mult = (function(){
    var cache = {};
    var calculate = function() {
        var a = 1;
        for(var i = 0, len = arguments.length; i < len; i++) {
            a = a * arguments[i];
        }
        return a;
    }
    
    return function() {
        var args = Array.prototype.join.call(arguments, ',');
        if(args in cache) {
            return cache[args];
        }
        
        return cache[args] = calculate.apply(null, arguments);
    }
}())

闭包使用场景三:延续局部变量的寿命

img对象经常用于数据上报,如下:

var report = function(src) {
    var img = new Image();
    img.src = src;
}
report('http://xxx.com/getUserInfo');

这段代码在运行时,发现在一些低版本浏览器上存在bug,会丢失部分数据上报,原因是img是report函数中的局部变量,当report函数调用结束后,img对象随即被销毁,而此时可能还没来得及发出http请求,所以此次请求就会丢失。
因此,我们使用闭包把img对象封闭起来,就可以解决数据丢失的问题:

var report = (function() {
    var imgs = [];
    return function(src) {
        var img = new Image();
        imgs.push(img);
        img.src = src;
    }
}())

不同html页面之间传值共享数据的方式

1.get方式,url中携带参数

2.特定的网页之间是可以共享cookie中的数据。

3.window.open和window.opener之间传值(不能跨域)
window.open可以打开一个新的页面,在新的页面中可以通过window.opener获取父页面的窗口对象,从而可以获取父窗口中的参数。

4.h5技术,window.localStorage存储数据
在HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。此方法类似cookie,将数据存在一个公共的地方,实现页面之间传值。

关于Flex布局

设为 Flex 布局以后,子元素的float、clear和vertical-align属性将失效

具体参考阮一峰
http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
http://www.ruanyifeng.com/blog/2015/07/flex-examples.html
关于flow属性:
https://www.cnblogs.com/grey-zhou/p/5736223.html

CSS3 box-sizing 属性

box-sizing: content-box|border-box|inherit;

content-box
宽度和高度分别应用到元素的内容框。
在宽度和高度之外绘制元素的内边距和边框。

border-box
就是说,为元素指定的任何内边距和边框都将在已设定的宽度和高度内进行绘制。
通过从已设定的宽度和高度分别减去边框和内边距才能得到内容的宽度和高度。

(通常我们设置好了width和height属性不希望再被padding和border影响时这个属性帮助很大)

inherit 规定应从父元素继承 box-sizing 属性的值。

使用纯css实现三角形

https://blog.csdn.net/weixin_37580235/article/details/80803479
除了这种方法之外,还可以使用iconfont(到阿里巴巴矢量图标库去下载)

清除CSS浮动的几种方法

1.给父级div设置高度,解决父级div因浮动导致的高度坍塌的问题

2.结尾处添加一个空div,利用css提高的clear:both清除浮动,让父级div能自动获取到高度

3.父级div定义 伪类:after,配合zoom使用

		.clearfloat {zoom:1}
        
        .clearfloat:after {
            
            /* 这三句必须写 */
            display:block;
            clear:both;
            /*必须有这样写*/
            content:"";
        }
        
    </style>

    <div id="box" class="clearfloat">
        <div id="div1">1</div>
        <div id="div2">2</div>
    </div>

4,父级div定义 overflow:hidden

		 #box {
            /*解决问题*/
            overflow: hidden;
        }

5.父级div定义 overflow:auto
跟设置 overflow: hidden;效果差不多

缺点:内部float的div宽高超过父级div时,会出现滚动条,hidden就不会有这个问题。

6.父级div定义 display:table,但是必须同时设置父级div的width

7.结尾处加 br标签 clear:both

为什么行内元素设置了float之后就可以设置高度
float就是隐性的把内联元素转化为块元素,这是对内部的特性就是有物理特性,但是他不占据一行。对外是内联元素的属性。他有个坏处就是会影响兄弟元素。相当于:display:inline-block;

web页面加载缓慢,如何优化?

1.图片优化:使用css精灵

2.使用第三方打包压缩工具

3.代码优化
按需加载,把统计、分享等 js 在页面 onload 后再进行加载,可以提高访问速度;
优化 cookie ,减少 cookie 体积;
尽量避免设置图片大小,多次重设图片大小会引发图片的多次重绘,影响性能;

合理使用display属性:
a.display:inline后不应该再使用width、height、margin、padding以及float
b.display:inline-block后不应该再使用float
c.display:block后不应该再使用vertical-align
d.display:table-*后不应该再使用margin或者float

HTML头部的JavaScript和写在HTML标签中的Style会阻塞页面的渲染,因此CSS放在页面头部并使用Link方式引入,JavaScript的引入放在页面尾;
使用 ajax 异步加载部分请求;

HTTPS与HTTP的区别

1)HTTPS的服务器需要到CA申请证书,以证明自己服务器的用途;

2)HTTP信息是明文传输,HTTPS信息是密文传输;

3)HTTP与HTTPS的端口不同,一个是80端口,一个是443端口;

可以说HTTP与HTTPS是完全不同的连接方式,HTTPS集合了加密传输,身份认证,更加的安全。

https中服务器与浏览器的校验过程

STEP 1: 客户端发起HTTPS 请求

SSL 连接总是由客户端启动,在 SSL 会话开始时,执行 SSL 握手。用户在浏览器里输入一个https 网址,然后连接到server 的443 端口。
客户端发送以下:

列出客户端密支持的加密方式列表(以客户端首选项顺序排序),如 SSL 的版本、客户端支持的加密算法和客户端支持的数据压缩方法(Hash 算法)。
包含 28 字节的随机数,client_random

STEP 2: 服务端的配置

采用HTTPS 协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用受信任的公司申请的证书则不会弹出提示页面。这套证书其实就是一对公钥和私钥。

STEP 3: 传送证书

服务器端返回以下:

服务器端选出的一套加密算法和 Hash 算法
服务器生成的随机数 server_rand om
SSL 数字证书(服务器使用带有 SSL 的 X.509 V3 数字证书),这个证书包含网站地址,公钥 public_key ,证书的颁发机构,过期时间等等。

STEP 4: 客户端解析证书

这部分工作是由客户端的TLS 来完成的。

首先会验证证书是否有效,这是对服务端的一种认证,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。
如果证书没有问题,那么浏览器根据步骤 3 的 server_random 生成一个随机值 premaster_secret (前 2 个字节是协议版本号,后 46 字节是用在对称加密密钥的随机数字)和 master_secret 。 master_secret 的生成需要 premaster_key ,并需要 client_random 和 server_random 作为种子

现在,各方面已经有了主密钥 master_secret ,根据协议约定,我们需要利用PRF 生成这个会话中所需要的各种密钥,称之为“密钥块”(Key Block ):

这是由系列 Hash 值组成,它将作为数据加解密相关的Key Material ,包含六部分内容,分别是用于校验一致性的密钥,用于对称内容加解密的密钥,以及初始化向量,客户端和服务器端各一份。其中,write MAC key ,就是session secret 或者说是session key 。Client write MAC key 是客户端发数据的session secret ,Server write MAC secret 是服务端发送数据的session key 。MAC(Message Authentication Code) ,是一个数字签名,用来验证数据的完整性,可以检测到数据是否被串改。然后用证书公钥 public_key 对该随机值进行加密。就好像上面说的,把随机值用锁头锁起来,这样除非有钥匙,不然看不到被锁住的内容。

Hash 握手信息,用第3步返回约定好的 Hash 算法对握手信息取 Hash 值,然后用随机数加密“握手消息+握手消息 Hash 值(签名)”

STEP 5: 传送加密信息

客户端发送以下:

客户端发送公钥 public_key 加密的 premaster secret 。目的就是让服务端得到这个随机值,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。

STEP 6: 服务端解密信息

服务端用私钥 private_key 解密后,得到了客户端传过来的随机值 premaster_secret(私钥),又由于服务器在步骤 1 中收到的 client_random ,所以服务器根据相同的生成算法,在相同输入参数的情况下,得到相同的 master_secret 。然后把内容通过该值进行对称加密。
所谓对称加密就是,将信息和私钥通过某种算法混合在一起,这样除非知道私钥,不然无法获取内容,而正好客户端和服务端都知道这个私钥,所以只要加密算法够彪悍,私钥够复杂,数据就够安全。

STEP 7: 传输加密后的信息

服务器端返回以下:
将被 premaster_key 对称加密的信息返回客户端,客户端可还原

STEP 8: 客户端解密信息

客户端用之前生成的私钥解密服务段传过来的信息,于是获取了解密后的内容。整个过程第三方即使监听到了数据,也束手无策。

加密方式

加密算法一般分为对称加密与非对称加密。

对称加密

客户端与服务器使用相同的密钥对消息进行加密
优点:
加密强度高,很难被破解
计算量小,仅为非对称加密计算量的 0.1%

缺点:
无法安全的生成和管理密钥
服务器管理大量客户端密钥复杂

非对称加密

非对称指加密与解密的密钥为两种密钥。服务器提供公钥,客户端通过公钥对消息进行加密,并由服务器端的私钥对密文进行解密。

优点:安全

缺点

性能低下,CPU 计算资源消耗巨大,一次完全的 TLS 握手,密钥交换时的非对称加密解密占了整个握手过程的 90% 以上。而对称加密的计算量只相当于非对称加密的 0.1%,因此如果对应用层使用非对称加密,性能开销过大,无法承受。
非对称加密对加密内容长度有限制,不能超过公钥的长度。比如现在常用的公钥长度是 2048 位,意味着被加密消息内容不能超过 256 字节。

JS跨域的几种方法

首先,同源策略限制以下几种行为:
1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送

如果是简单单向通信,新建< img>,< script>,< link>,< iframe>元素,通过src,href属性设置为目标url。实现跨域请求

如果请求json数据,使用< script>进行jsonp请求

现代浏览器中多窗口通信使用HTML5规范的postMessage

1.通过jsonp跨域
jsonp的原理就很清楚了,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。

知道jsonp跨域的原理后我们就可以用js动态生成script标签来进行跨域操作了,而不用特意的手动的书写那些script标签。如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了。
在这里插入图片描述
原理是一样的,只不过我们不需要手动的插入script标签以及定义回掉函数。jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

2、通过修改document.domain来跨子域
浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。有一点需要说明,不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但蛋疼的是你却不能使用获取到的window对象的属性和方法(html5中的postMessage方法是一个例外,还有些浏览器比如ie6也可以使用top、parent等少数几个属性),总之,你可以当做是只能获取到一个几乎无用的window对象。比如,有一个页面,它的地址是 http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是 http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的

这个时候,document.domain就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html 这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的document.domain 可以设成 a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成 baidu.com,因为主域已经不相同了。

3、使用window.name来进行跨域
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置

比如有一个 www.example.com/a.html 页面,需要通过a.html页面里的js来获取另一个位于不同域上的页面 www.cnblogs.com/data.html 里的数据。

data.html页面里的代码很简单,就是给当前的window.name设置一个a.html页面想要得到的数据值。data.html里的代码:
在这里插入图片描述
在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去获取data.html的数据,然后a.html再去得到iframe获取到的数据。

充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为 www.cnblogs.com/data.html 就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行,不然根据前面讲的同源策略,a.html是不能访问到iframe里的window.name属性的。这就是整个跨域过程。

看下a.html页面的代码:
在这里插入图片描述
4、使用HTML5中新引进的window.postMessage方法来跨域传送数据
调用postMessage方法的window对象是指要发送消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。

需要接收消息的window对象,可通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
在这里插入图片描述
在这里插入图片描述
5.CORS
服务端设置access-control-allow-origin就可以开启CORS,该属性表示哪些域名可以访问资源

例如:网站http://localhost:63342/ 页面要请求http://localhost:3000/users/userlist 页面,userlist页面返回json字符串格{name: ‘Mr.Cao’, gender: ‘male’, career: ‘IT Education’}

//在服务器端设置同源策略地址
 router.get("/userlist", function (req, res, next) {        
 var user = {
 name: 'Mr.Cao', gender: 'male', career: 'IT Education'};       

res.writeHeader(200,{"Access-Control-Allow-Origin":'http://localhost:63342'});       

res.write(JSON.stringify(user));       
res.end();  
 })


6.WebSocket
WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用 Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

//前端代码:
 <div>user input:<input type="text"></div>

 <script src="./socket.io.js"></script>

 <script>
 var socket = io('http://www.domain2.com:8080'); // 连接成功处理 
 socket.on('connect', function() {     // 监听服务端消息     

 socket.on('message', function(msg) {         
 console.log('data from server: ---> ' + msg);     
  });  // 监听服务端关闭
      
   socket.on('disconnect', function() {         
    console.log('Server socket has closed.');      
   });

 });


document.getElementsByTagName('input')[0].onblur = function() {     
socket.send(this.value);
 }; 
 </script>

//Nodejs socket后台: 
var http = require('http'); 
var socket = require('socket.io'); // 启http服务 

var server = http.createServer(function(req, res) {     
res.writeHead(200, { 'Content-type': 'text/html' });     
res.end(); 
}); 

server.listen('8080'); 
console.log('Server is running at port 8080...'); // 监听socket连接 
socket.listen(server).on('connection', function(client) {     // 接收信息     

client.on('message', function(msg) {         
client.send('hello:' + msg);         
console.log('data from client: ---> ' + msg);     
});     // 断开处理  
   
client.on('disconnect', function() {         
console.log('Client socket has closed.');      
});

});


可以看一下这一篇:
https://www.cnblogs.com/vajoy/p/4295825.html

解决异步的几种方式

1.回调函数(定时器)。

​ 2.事件监听。

​ 3.发布/订阅。

​ 4.Promise对象。(将执行代码和处理结果分开)
(promise可以解决回调地狱,一层又一层的嵌套回调函数)

​ 5.Generator。

​ 6.ES7的async/await。

关于CDN缓存原理

CDN就可以理解为分布在每个县城的火车票代售点,用户在浏览网站的时候,CDN会选择一个离用户最近的CDN边缘节点来响应用户的请求,这样海南移动用户的请求就不会千里迢迢跑到北京电信机房的服务器(假设源站部署在北京电信机房)上了。

CDN的优势很明显:(1)CDN节点解决了跨运营商和跨地域访问的问题,访问延时大大降低;(2)大部分请求在CDN边缘节点完成,CDN起到了分流作用,减轻了源站的负载。

客户端浏览器先检查是否有本地缓存是否过期,如果过期,则向CDN边缘节点发起请求,CDN边缘节点会检测用户请求数据的缓存是否过期,如果没有过期,则直接响应用户请求,此时一个完成http请求结束;如果数据已经过期,那么CDN还需要向源站发出回源请求(back to the source request),来拉取最新的数据。

ETag是一个文件的唯一标志符。就像一个哈希或者指纹,每个文件都有一个单独的标志,只要这个文件发生了改变,这个标志就会发生变化。
如同 Last-modified 一样,ETag 解决了文件版本比较的问题。只不过 ETag 的级别比 Last-Modified 高一些。

cache-control 里面有一个max-age (比如60s),这个优先级最高。如果这个没过期,后面几个参数直接不用看。

last-modified 和 if-modified-since 都是用于记录页面是否过期的http头信息,只是last-modified 是由服务器发送给客户端的,而if-modified-since 是从客户端发送给服务器端。

CDN流程

1.用户向浏览器输入www.web.com这个域名,浏览器第一次发现本地没有dns缓存,则向网站的DNS服务器请求;

2.网站的DNS域名解析器设置了CNAME,指向了 www.web.51cdn.com ,请求指向了CDN网络中的智能DNS负载均衡系统;

3.智能DNS负载均衡系统解析域名,把对用户响应速度最快的IP节点返回给用户;

4.用户向该IP节点(CDN服务器)发出请求;

5.由于是第一次访问,CDN服务器会向原web站点请求,并缓存内容;

6.请求结果发给用户。

关于CDN工作流程,这篇是最简明扼要的:
http://www.cnblogs.com/shytong/p/5456698.html

具体可以看这里:
https://www.cnblogs.com/tinywan/p/6067126.html

这篇比较全面:(慢慢看)
http://www.cnblogs.com/futan/archive/2013/04/21/cachehuancun.html
以及
https://www.jianshu.com/p/08b3cb895713

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32是一款由意法半导体(STMicroelectronics)推出的32位微控制器系列。它由ARM Cortex-M内核驱动,被广泛应用于工业控制、汽车电子、家电等各个领域。 关于STM32面试笔试题,以下是一些常见问题和回答: 1. 请介绍STM32系列的特点。 STM32系列具有低功耗、快速时钟速度、多功能外设、丰富的存储器和丰富的中断处理能力等特点。 2. 请简要介绍STM32的中断机制。 STM32具有多个中断向量表,每个中断向量对应一个中断处理函数。在产生中断时,会根据中断号找到对应的中断处理函数进行处理。 3. 请说明STM32的GPIO口。 STM32的GPIO口用于外部设备的输入和输出。它具有多种工作模式、高低电平检测、中断触发等功能,可根据需要进行配置。 4. 请简述STM32的时钟系统。 STM32的时钟系统由主时钟(HCLK)、系统时钟(SYSCLK)、外设时钟(PCLK1和PCLK2)等组成,不同的外设使用不同的时钟频率。 5. 请描述STM32的存储器结构。 STM32具有不同类型的存储器,包括Flash存储器用于程序存储、SRAM用于数据存储、ROM用于存储常量和数据等。 总结:STM32是一款功能强大的32位微控制器,具有多种特点和丰富的外设。掌握了STM32的中断机制、GPIO口、时钟系统以及存储器结构,可以更好地应用STM32进行开发和设计。 ### 回答2: ST官方提供了一套标准的开发环境,也就是 STM32CubeIDE。它是基于Eclipse的集成开发环境,可以用来开发STM32系列的单片机软件。这个IDE集成了STM32CubeMX和编译工具链,用起来非常方便。 在STM32CubeMX中,你可以通过图形化的界面来配置STM32单片机的各种资源,如GPIO、UART、SPI、I2C等。它还可以自动生成初始化代码,这样你就可以快速上手开发。 对于STM32系列的单片机,编写程序一般使用C编程语言。ST官方提供了丰富的固件库,可以用来操作各种外设和功能。同时,ST还提供了丰富的官方文档和例程,可以帮助你学习和使用STM32系列的单片机。 在开发过程中,你需要熟悉一些基本的知识,如串口通信、中断、定时器、PWM等。此外,还需要了解一些硬件相关的知识,如时钟、IO口、外设等。 在面试中,除了要求你对STM32系列单片机有一定的了解之外,还可能会考察你的问题解决能力和项经验。例如,可能会让你解决一些常见的问题,如控制器调试、性能优化等。另外,还可能会要求你介绍一个你完成的STM32项,包括你的设计思路、实现过程和遇到的困难等。 总之,在STM32面试笔试中,除了对STM32系列单片机的基本知识掌握外,还需要有一定的问题解决能力和项经验。通过对官方文档、例程和项实践的学习,能够更好地准备面试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值