目录
20页面解析顺序
一、浏览器
浏览器的主要功能是将用户选择的web资源呈现出来,它需要从服务器请求资源,并将其显示在浏览器窗口中,资源的格式通常是HTML,也包括PDF、image及其他格式。用户用URI(Uniform Resource Identifier统一资源标识符)来指定所请求资源的位置,通过DNS查询,将网址转换为IP地址。整个浏览器工作的流程:
1、输入网址。
2、浏览器查找域名的IP地址。
3. 浏览器给web服务器发送一个HTTP请求
4. 网站服务的永久重定向响应
5. 浏览器跟踪重定向地址 现在,浏览器知道了要访问的正确地址,所以它会发送另一个获取请求。
6. 服务器“处理”请求,服务器接收到获取请求,然后处理并返回一个响应。
7. 服务器发回一个HTML响应
8. 浏览器开始显示HTML
9. 浏览器发送请求,以获取嵌入在HTML中的对象。在浏览器显示HTML时,它会注意到需要获取其他地址内容的标签。这时,浏览器会发送一个获取请求来重新获得这些文件。这些文件就包括CSS/JS/图片等资源,这些资源的地址都要经历一个和HTML读取类似的过程。所以浏览器会在DNS中查找这些域名,发送请求,重定向等;
浏览器的主要组件包括
- 用户界面: 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分;
- 浏览器引擎:用来查询及操作渲染引擎的接口;
- 渲染引擎: 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来;
- 网络:用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作;
- UI 后端:用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口;
- JS解释器:用来解释执行JS代码;
- 数据存储:H5定义了web database技术,这是一种轻量级完整的客户端存储技术;
二、页面生成过程
1、DNS服务器通过域名查找对应的web 服务器ip地址;
2、浏览器访问web服务器;这里涉及到客户端与服务器的tcp 三次握手与四次挥手;
3、服务器处理完成返回html;
4、浏览器解析、加载页面
解析html 构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树 :
我们知道浏览器为了体验友好,并不是文档全部都解析才绘制到屏幕上,而是从上至下开始解析html,遇到css 会开启线程下载css;
解析:
1、将HTML构建成一个DOM树(DOM = Document Object Model 文档对象模型),DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
2、将CSS解析成CSS去构造CSSOM树( CSSOM = CSS Object Model CSS对象模型)
3、根据DOM树和CSSOM来构造 Rendering Tree(渲染树)。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
4.有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。
5.下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置 layout render tree。
6.再下一步就是绘制,即遍历render树,并使用浏览器UI后端层绘制每个节点。
性能优化中重绘、重排:
(1)Reflow(回流/重排):当它发现了某个部分发生了变化影响了布局,渲染树需要重新计算。
(2)Repaint(重绘):改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的repaint,根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随重排;
Reflow要比Repaint更花费时间,也就更影响性能。所以在写代码的时候,要尽量避免过多的Reflow。
reflow的原因:
(1)页面初始化的时候;
(2)操作DOM时;
(3)某些元素的尺寸变了;
(4)如果 CSS 的属性发生变化了。
减少 reflow/repaint
(1)不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。
(2)不要把 DOM 结点的属性值放在一个循环里当成循环里的变量。
(3)为动画的 HTML 元件使用 fixed 或 absolute 的 position,那么修改他们的 CSS 是不会 reflow 的。
(4)千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局。
三、影响页面渲染
css注意事项
css选择符是从右到左进行匹配的。所以,#nav li 我们以为这是一条很简单的规则,秒秒钟就能匹配到想要的元素,所以,会去找所有的li,然后再去确定它的父元素是不是#nav。因此,写css的时候需要注意:
1dom深度尽量浅。
2减少inline javascript、css的数量。
3使用现代合法的css属性。
4不要为id选择器指定类名或是标签,因为id可以唯一确定一个元素。
5避免后代选择符,尽量使用子选择符。原因:子元素匹配符的概率要大于后代元素匹配符。后代选择符;#tp p{} 子选择符:#tp>p{}
6避免使用通配符,举一个例子,.mod .hd *{font-size:14px;} 根据匹配顺序,将首先匹配通配符,也就是说先匹配出通配符,然后匹配.hd(就是要对dom树上的所有节点进行遍历他的父级元素),然后匹配.mod,这样的性能耗费可想而知.
javascript 位置
如果在解析html的时候遇到js会阻塞页面渲染,所以一般我们会将所有的script标签放到页面底部,也就是body闭合标签之前,这能确保在脚本执行前页面已经完成了DOM树渲染。尽可能地合并脚本。页面中的script标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。
采用无阻塞下载 JavaScript 脚本的方法:
(1)使用script标签的 defer、async 属性、;
(2)使用动态创建的script元素来下载并执行代码等异步加载等方法;
defer、async 区别:
defer、async都是异步下载,但是执行时刻不一致;
相同点:
1加载文件时不阻塞页面渲染;
2使用这两个属性的脚本中不能调用document.write方法;
3允许不定义属性值,仅仅使用属性名;
不同点:
1html的版本html4.0中定义了defer,html5.0中定义了async;这将造成由于浏览器版本的不同而对其支持的程度不同;
2每一个async属性的脚本都在它下载结束之后立刻执行,同时会在window的load事件之前执行,所以就有可能出现脚本执行顺序被打乱 的情况;
3每一个defer属性的脚本都是在页面解析完毕之后,按照原本的顺序执行,同时会在document的DOMContentLoaded之前执行;
详解重定向:
重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置(如:网页重定向、域名的重定向、路由选择的变化也是对数据报文经由路径的一种重定向)。
其实网站重定向极为普遍,譬如不满意原来的域名而申请了一个新域名;买下容易被人错拼的域名,防止客户因为拼错URL而找不到网站
一个多域名站主的经验之谈:
“我只有一个网站,主域名是www.domain.com,此外还有诸如domain1.com、domain2.com、domain3.com等共计十几个域名。所有这些次级域名都映射到www.domain.com,而且所有域名对应的是同一个IP地址。
常用的重定向方式有:301 redirect、302 redirect与meta fresh。
301 redirect
301代表永久性转移(Permanently Moved),301重定向是网页更改地址后对搜索引擎友好的最好方法,只要不是暂时搬移的情况,都建议使用301来做转址。
302 redirect
302代表暂时性转移(Temporarily Moved ),在前些年,不少Black Hat SEO(黑帽SEO)曾广泛应用这项技术作弊,目前,各大主要搜索引擎均加强了打击力度,像Google前些年对域名之王(Business)以及近来对BMW德国网站的惩罚。即使网站客观上不是spam,也很容易被搜寻引擎容易误判为spam而遭到惩罚。
meta fresh
这在2000年前比较流行,不过现在已很少见。其具体是通过网页中的meta指令,在特定时间后重定向到新的网页,如果延迟的时间太短(约5秒之内),会被判断为spam。
19应用层协议
1、应用层协议(application layer protocol)定义了运行在不同端系统上的应用程序进程如何相互传递报文。
OSI体系结构分为7层:物理层、链路层、网路层、传输层、会话层、表示层、应用层。
TCP/IP5层参考模型:物理层、数据链路层、网络层、传输层、应用层
TCP/IP的体系结构分为4层:网络接口层(物理层、链路层)、网际层(网络层IP)、传输层(UDP/TCP)、应用层(会话层、表示层、应用层)
原理体系结构:物理层、链路层、网络层、传输层、应用层(会话层+表示层+应用层)
OSI七层协议体系结构:优点:概念清楚,理论完整,缺点但是复杂而不实用
TCP/IP协议族四层,缺点:太简单,但被广泛使用
结合上面两个的优缺点,就有了5层协议的原理体系结构,即简洁又能把概念描述清楚。
应用层协议分类
(1)域名系统(Domain Name System,DNS):用于实现网络设备名字到IP地址映射的网络服务。
(2)文件传输协议(File Transfer Protocol,FTP):用于实现交互式文件传输功能。
(3)简单邮件传送协议(Simple Mail Transfer Protocol, SMTP):用于实现电子邮箱传送功能
(4)超文本传输协议(HyperText Transfer Protocol,HTTP):用于实现WWW服务。
(5)简单网络管理协议(simple Network Management Protocol,SNMP):用于管理与监视网络设备。
(6)远程登录协议(Telnet):用于实现远程登录功能。
注:主要是124,3个点。
2、应用层中的应用软件分两种:客户/服务器和P2P体系结构
1)客户/服务器(client/server)
这种类型,就是我们很熟悉的客户端,服务器模型,客户端请求服务器,服务器响应客户端这样的一种方式进行“交流”
2)P2P
也称为对等体系结构。P2P相当于每个人的电脑都可以当服务器,也可以当客户端,不单单限制于只能客户端访问服务器,
你自己的计算机可以去访问别人的计算机上的内容,别的同样可以访问你计算机上的内容,这样达到一种共享的状态。
3应用层协议之HTTP协议
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。
HTTP是客户端浏览器或其他程序与Web服务器之间的应用层通信协议。在Internet上的Web服务器上存放的都是超文本信息,客户机需要通过HTTP协议传输所要访问的超文本信息。HTTP包含命令和传输信息,仅可用于Web访问,也可以用于其他因特网/内联网应用系统之间的通信,从而实现各类应用资源超媒体访问的集成。我们在浏览器的地址栏里输入的网站地址叫做URL(Uniform Resource Locator,统一资源定位符)。就像每家每户都有一个门牌地址一样,每个网页也都有一个Internet地址。当你在浏览器的地址框中输入一个URL或是单击一个超级链接时,URL就确定了要浏览的地址。浏览器通过超文本传输协议(HTTP),将Web服务器上站点的网页代码提取出来,并翻译成漂亮的网页。
URL:统一资源定位符,通过下面格式,可以看出,就是用来定位我们所需要资源在服务器上的位置。
格式:<协议>://<主机>:<端口>/<路径>
协议:http
主机:域名/IP地址,原理度一样,到头来还是会转换为IP地址,通过这个才能找到目标服务器
端口: 在传输层需要使用的,访问目的主机的哪个端口号。
路径:精准的定位我们所需要的资源位置、
平常会省略协议和端口号,因为这些都是默认的,在访问主页时,路径也会省略。比如www.baidu.com这个默认进入百度的主页 完整写法 http://www.baidu.com:80/index.html 。
18position属性
position定位有4个属性,分别是static,absolute,relative,fixed
1.static(默认)
static属性为默认值,没有定位,元素出现在正常的流中(忽略 top, bottom, left, right 或者 z-index 声明).
2.relative(相对定位)
此处的相对并不是相对于哪个父div或子div,相对只是相对于自身原本的位置发生变化。
3.absolute(绝对定位)
absolute定位是布局中最常用到的定位,其生成的位置是相对于带有position属性的父(父...)级来定位;
如果父级都没有position属性,则相对于document来定位;
使用absolute定位后,定位元素是脱离文档流的,这时候父级会检测不到定位元素的宽高。inline元素使用absolute定位之后,可以对其设置宽高;
元素是不可以同时使用绝对定位和浮动的。
4.fixed(固定定位--相对于浏览器窗口)
生成绝对定位的元素,相对于浏览器窗口进行定位。 元素的位置通过 "left", "top", "right" 以及 "bottom" 属性进行规定.
也是脱离了文档流,与absolute一样,可以设置宽高,但是父级会检测不到定位元素的宽高,所以无法由子元素撑开。
元素是不可以同时使用fixed定位和浮动的。
17css的选择器及其权重
一、样式类型
1、行间
<h1 style="font-size:12px;color:#000;">我的行间CSS样式。</h1>
2、内联
<style type="text/css">
h1{font-size:12px;
color:#000;
}
</style>
3、外部
<link rel="stylesheet" href="css/style.css">
二、选择器类型
1、ID #id
2、class .class
3、标签 p
4、通用 *
5、属性 [type=“text”]
6、伪类 :hover
7、伪元素 ::first-line
8、子选择器、相邻选择器
所有伪类
所有伪元素:
三、权重计算规则
第一等:代表内联样式,如: style=””,权值为1000。
第二等:代表ID选择器,如:#content,权值为0100。
第三等:代表类,伪类和属性选择器,如.content,权值为0010。
第四等:代表类型选择器和伪元素选择器,如div p,权值为0001。
通配符、子选择器、相邻选择器等的。如*、>、+,权值为0000。
继承的样式没有权值。
四、比较规则
(1)1,0,0,0 > 0,99,99,99。也就是说从左往右逐个等级比较,前一等级相等才往后比。
(2)无论是行间、内部和外部样式,都是按照这个规则来进行比较。而不是直观的行间>内部>外部样式;ID>class>元素。之所以有这样的错觉,是因为确实行间为第一等的权重,所以它的权重是最高的。而内部样式可能一般写在了外部样式引用了之后,所以覆盖掉了之前的。
(3)在权重相同的情况下,后面的样式会覆盖掉前面的样式。
(4)通配符、子选择器、相邻选择器等的。虽然权值为0000,但是也比继承的样式优先。
五、!important优先级是最高的
ie7+和别的浏览器对important的这种作用的支持度都很好。只有ie6有些bug
16js继承机制
Javascript语言的继承机制全靠一种很奇特的"原型链"(prototype chain)模式,来实现继承。
与java不同的是,Javascript语言中,new命令后面跟的不是类,而是构造函数。
经典案例:
现在有一个叫做DOG的构造函数,表示狗对象的原型。
function DOG(name){
this.name = name;
}
对这个构造函数使用new,就会生成一个狗对象的实例。
var dogA = new DOG('大毛');
alert(dogA.name); // 大毛
注意构造函数中的this关键字,它就代表了新创建的实例对象。
new运算符的缺点:用构造函数生成实例对象,有一个缺点,那就是无法共享属性和方法。
经典案例:
在DOG对象的构造函数中,设置一个实例对象的共有属性species。
function DOG(name){
this.name = name;
this.species = '犬科';
}
然后,生成两个实例对象:
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
这两个对象的species属性是独立的,修改其中一个,不会影响到另一个。
dogA.species = '猫科';
alert(dogB.species); // 显示"犬科",不受dogA的影响
每一个实例对象,都有自己的属性和方法的副本。这不仅无法做到数据共享,也是极大的资源浪费。
js用prototype属性来完成继承
经典案例:
还是以DOG构造函数为例,现在用prototype属性进行改写:
function DOG(name){
this.name = name;
}
DOG.prototype = { species : '犬科' };
var dogA = new DOG('大毛');
var dogB = new DOG('二毛');
alert(dogA.species); // 犬科
alert(dogB.species); // 犬科
现在,species属性放在prototype对象里,是两个实例对象共享的。只要修改了prototype对象,就会同时影响到两个实例对象。
DOG.prototype.species = '猫科';
alert(dogA.species); // 猫科
alert(dogB.species); // 猫科
由于所有的实例对象共享同一个prototype对象,那么从外界看起来,prototype对象就好像是实例对象的原型,而实例对象则好像"继承"了prototype对象一样。
15原型与原型链
一. 普通对象与函数对象
JavaScript 中,万物皆对象!但对象也是有区别的。分为普通对象和函数对象,Object 、Function 是 JS 自带的函数对象。
经典案例:
var o1 = {};
var o2 = new Object();
var o3 = new f1();
function f1(){};
var f2 = function(){};
var f3 = new Function('str','console.log(str)');
console.log(typeof Object); //function
console.log(typeof Function); //function
console.log(typeof f1); //function
console.log(typeof f2); //function
console.log(typeof f3); //function
console.log(typeof o1); //object
console.log(typeof o2); //object
console.log(typeof o3); //object
注:凡是通过 new Function() 创建的对象都是函数对象,其他的都是普通对象。f1,f2,归根结底都是通过 new Function()的方式进行创建的。Function Object 也都是通过 New Function()创建的。
二. 构造函数
构造函数的特点:
a:构造函数的首字母必须大写,用来区分于普通函数
b:内部使用的this对象,来指向即将要生成的实例对象
c:使用New来生成实例对象
我们先复习一下构造函数的知识:
function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { alert(this.name) }
}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');
上面的例子中 person1 和 person2 都是 Person 的实例。这两个实例都有一个 constructor (构造函数)属性,该属性(是一个指针)指向 Person。 即:
console.log(person1.constructor == Person); //true
console.log(person2.constructor == Person); //true
我们要记住两个概念(构造函数,实例):
person1 和 person2 都是 构造函数 Person 的实例
注:实例的构造函数属性(constructor)指向构造函数。
三. 原型对象
在 JavaScript 中,每当定义一个对象(函数也是对象)时候,对象中都会包含一些预定义的属性。其中每个函数对象都有一个prototype 属性,这个属性指向函数的原型对象,函数对象的实例是普通对象,没有 prototype 属性。
经典案例:
function Person() {}
Person.prototype.name = 'Zaxlct';
Person.prototype.age = 28;
Person.prototype.job = 'Software Engineer';
Person.prototype.sayName = function() {
alert(this.name);
}
var person1 = new Person();
person1.sayName(); // 'Zaxlct'
var person2 = new Person();
person2.sayName(); // 'Zaxlct'
console.log(person1.sayName == person2.sayName); //true
注:每个对象都有 __proto__
属性,但只有函数对象才有 prototype 属性
用typeof()校验,返回object的是普通对象,返回function的是函数对象。
经典案例:
Person.prototype = {
name: 'Zaxlct',
age: 28,
job: 'Software Engineer',
sayName: function() {
alert(this.name);
}
}
原型对象就是 Person.prototype
在上面我们给 A 添加了 四个属性:name、age、job、sayName。其实它还有一个默认的属性:constructor
注:在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)
上面这句话有点拗口,我们「翻译」一下:A 有一个默认的 constructor 属性,这个属性是一个指针,指向 Person。即:
Person.prototype.constructor == Person
实例的构造函数属性(constructor)指向构造函数 :person1.constructor == Person
原型对象(Person.prototype)是 构造函数(Person)的一个实例。
原型对象其实就是普通对象(但 Function.prototype 除外,它是函数对象,但它很特殊,他没有prototype属性(前面说道函数对象都有prototype属性))。
经典案例:
function Person(){};
console.log(Person.prototype) //Person{}
console.log(typeof Person.prototype) //Object
console.log(typeof Function.prototype) // Function,这个特殊
console.log(typeof Object.prototype) // Object
console.log(typeof Function.prototype.prototype) //undefined
原型对象是用来做什么的呢?主要作用是用于继承。
经典案例:
var Person = function(name){
this.name = name;
}
Person.prototype.getName = function(){ //通过给 Person.prototype 设置了一个函数对象的属性,那有 Person 的实例(person1)出来的普通对象就继承了这个属性。
return this.name; //两次 this 在函数执行时都指向 person1。
}
var person1 = new Person('lxf');
console.log(person1.getName()); //lxf
四. __proto__
JS 在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做__proto__ 的内置属性,用于指向创建它的构造函数的原型对象。
对象 person1 有一个 __proto__属性,创建它的构造函数是 Person,构造函数的原型对象是 Person.prototype ,所以:
person1.proto == Person.prototype
五. 构造器
可以创建对象的构造器不仅仅有 Object,也可以是 Array,Date,Function等。所以我们也可以构造函数来创建 Array、 Date、Function
经典案例:
var b = new Array();
console.log(b.constructor == Array); //true
var c = new Date();
console.log(c.constructor == Date); //true
var d = new Function();
console.log(d.constructor == Function); //true
六. 原型链
prototype //只有函数对象有
__proto__ //每个对象都有 `__proto__` 属性,原型属性
constructor //构造函数属性
小测试来检验一下你理解的怎么样:
1
person1.__proto__ 是什么?
2
Person.__proto__ 是什么?
3
Person.prototype.__proto__ 是什么?
4
Object.__proto__ 是什么?
5
Object.prototype__proto__ 是什么?
答案:
第一题:
因为 person1.__proto__ === person1 的构造函数.prototype
因为 person1的构造函数 === Person
所以 person1.__proto__ === Person.prototype
第二题:
因为 Person.__proto__ === Person的构造函数.prototype
因为 Person的构造函数 === Function
所以 Person.__proto__ === Function.prototype
第三题:
Person.prototype 是一个普通对象,我们无需关注它有哪些属性,只要记住它是一个普通对象。
因为一个普通对象的构造函数 === Object
所以 Person.prototype.__proto__ === Object.prototype
第四题,参照第二题,因为 Person 和 Object 一样都是构造函数
第五题:
Object.prototype 对象也有proto属性,但它比较特殊,为 null 。因为 null 处于原型链的顶端,这个只能记住。
Object.prototype.__proto__ === null
七. 再解函数对象
所有函数对象的__proto__都指向Function.prototype,它是一个空函数(Empty function)
Number.__proto__ === Function.prototype // true
Number.constructor == Function //true
Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true
String.__proto__ === Function.prototype // true
String.constructor == Function //true
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Object.__proto__ === Function.prototype // true
Object.constructor == Function // true
// 所有的构造器都来自于Function.prototype,甚至包括根构造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true
Array.__proto__ === Function.prototype // true
Array.constructor == Function //true
RegExp.__proto__ === Function.prototype // true
RegExp.constructor == Function //true
Error.__proto__ === Function.prototype // true
Error.constructor == Function //true
Date.__proto__ === Function.prototype // true
Date.constructor == Function //true
案例2:
console.log(Number.__proto__ == Function.prototype); //true
console.log(Number.__proto__ == Object.prototype); //false
JavaScript中有内置(build-in)构造器/对象共计12个(ES5中新加了JSON),这里列举了可访问的8个构造器。剩下如Global不能直接访问,Arguments仅在函数调用时由JS引擎创建,Math,JSON是以对象形式存在的,无需new。它们的proto是Object.prototype。如下
Math.__proto__ === Object.prototype // true
Math.construrctor == Object // true
JSON.__proto__ === Object.prototype // true
JSON.construrctor == Object //true
所有的构造器都来自于 Function.prototype,甚至包括根构造器Object及Function自身。所有构造器都继承了·Function.prototype·的属性及方法。如length、call、apply、bind
14GET和POST两种基本请求方法的区别
最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。
HTTP的底层是TCP/IP。所以GET和POST的底层也是TCP/IP,也就是说,GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。你要给GET加上request body,给POST带上url参数,技术上是完全行的通的。
urlencode是一个函数,可将字符串以URL编码,用于编码处理。
GET产生一个TCP数据包;POST产生两个TCP数据包。
-
GET与POST都有自己的语义,不能随便混用。
-
据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
-
并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
13说出几个http协议状态码
200:请求成功
201:请求成功并且服务器创建了新的资源
302:服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来响应以后的请求。
304:自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
400:服务器不理解请求的语法。
404:请求的资源(网页等)不存在
405:方法不被允许
500: 内部服务器错误
12vue双向数据绑定的原理
实现数据绑定的做法有大致如下几种:
1发布者-订阅者模式(backbone.js)
2脏值检查(angular.js)
3数据劫持(vue.js) vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperty(obj, prop, descriptor)
参数
obj 要在其上定义属性的对象。
prop 要定义或修改的属性的名称。
descriptor 将被定义或修改的属性描述符。
属性描述符
对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可能是可写的,也可能不是可写的。存取描述符是由getter-setter函数对描述的属性。描述符必须是这两种形式之一;不能同时是两者。
数据描述符和存取描述符均具有以下可选键值(共同属性):
configurable
当且仅当该属性的 configurable 为 true 时,该属性描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false。
enumerable
当且仅当该属性的enumerable为true时,该属性才能够出现在对象的枚举属性中。默认为 false。
数据描述符同时具有以下可选键值:
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。默认为 undefined。
writable
当且仅当该属性的writable为true时,value才能被赋值运算符改变。默认为 false。
存取描述符同时具有以下可选键值:
get
一个给属性提供 getter 的方法,如果没有 getter 则为 undefined。当访问该属性时,该方法会被执行,方法执行时没有参数传入,但是会传入this对象(由于继承关系,这里的this并不一定是定义该属性的对象)。
默认为 undefined。
set
一个给属性提供 setter 的方法,如果没有 setter 则为 undefined。当属性值修改时,触发执行该方法。该方法将接受唯一参数,即该属性新的参数值。
默认为 undefined。
描述符可同时具有的键值
configurable enumerable value writable get set
数据描述符 Yes Yes Yes Yes No No
存取描述符 Yes Yes No No Yes Yes
如果一个描述符不具有value,writable,get 和 set 任意一个关键字,那么它将被认为是一个数据描述符。如果一个描述符同时有(value或writable)和(get或set)关键字,将会产生一个异常。
记住,这些选项不一定是自身属性,如果是继承来的也要考虑。为了确认保留这些默认值,你可能要在这之前冻结 Object.prototype,明确指定所有的选项,或者通过 Object.create(null)将__proto__属性指向null。
经典案例:
<script type="text/javascript">
var data = {name: 'kindeng'};
observe(data);
data.name = 'dmq'; // 哈哈哈,监听到值变化了 kindeng --> dmq
function observe(data) {
if (!data || typeof data !== 'object') {
return;
}
// 取出所有属性遍历
console.log(Object.keys(data)); //["name"]
Object.keys(data).forEach(function(key) {
console.log(key); //name
defineReactive(data, key, data[key]); //{name: 'kindeng'},name,kindeng
});
};
function defineReactive(data, key, val) {
observe(val); // 监听子属性
Object.defineProperty(data, key, {
enumerable: true, // 可枚举
configurable: false, // 不能再define
get: function() {
return val;
},
set: function(newVal) {
console.log('哈哈哈,监听到值变化了 ', val, ' --> ', newVal);
val = newVal;
}
});
}
</script>
例子中函数拓展
1Object.keys() 方法会返回一个由一个给定对象的自身可枚举属性组成的数组,数组中属性名的排列顺序和使用 for…in 循环遍历该对象时返回的顺序一致 。
Object.keys(obj)
参数
obj 要返回其枚举自身属性的对象。
返回值 一个表示给定对象的所有可枚举属性的字符串数组。
Object.create(proto, [propertiesObject])
Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 (请打开浏览器控制台以查看运行结果。)
参数
proto 新创建对象的原型对象。
propertiesObject 可选。如果没有指定为 undefined,则是要添加到新创建对象的可枚举属性(即其自身定义的属性,而不是其原型链上的枚举属性)对象的属性描述符以及相应的属性名称。这些属性对应Object.defineProperties()的第二个参数。
返回值
一个新对象,带着指定的原型对象和属性。
注:如果propertiesObject参数是 null 或非原始包装对象,则抛出一个 TypeError 异常。
经典案例:
const person = {
isHuman: false,
printIntroduction: function () {
console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`); //es6 新的语法$ {NAME},并把它放在反引号里
}
};
const me = Object.create(person);
me.name = "Matthew"; // "name" is a property set on "me", but not on "person"
me.isHuman = true; // inherited properties can be overwritten
me.printIntroduction();
// expected output: "My name is Matthew. Am I human? true"
document.createDocumentFragment() 创建一个文档碎片
说白了就是为了节约使用DOM。每次JavaScript对DOM的操作都会改变页面的变现,并重新刷新整个页面,从而消耗了大量的时间。为解决这个问题,可以创建一个文档碎片,把所有的新节点附加其上,然后把文档碎片的内容一次性添加到document中。
经典案例:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<ul id="ul">
</ul>
<script type="text/javascript">
var element = document.getElementById('ul'); // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ['Firefox', 'Chrome', 'Opera',
'Safari', 'Internet Explorer'];
browsers.forEach(function(browser) {
var li = document.createElement('li');
li.textContent = browser;
fragment.appendChild(li);
});
element.appendChild(fragment);
</script>
</body>
</html>
仿vue双向绑定的简易demo(因为太大用链接下载)
https://github.com/DMQ/mvvm