js高级程序设计:canvas、错误调试、json、ajax、其它
使用 Canvas 绘图
基本用法
- 要使用元素,必须先设置其 width 和 height 属性,指定可以绘图的区域大小。
- 要在这块画布(canvas)上绘图,需要取得绘图上下文。而取得绘图上下文对象的引用,需要调用getContext()方法并传入上下文的名字,注意要先检测getContext()方法是否存在
- 使用 toDataURL()方法,可以导出在元素上绘制的图像。这个方法接受一个参数,即图像的 MIME 类型格式,而且适合用于创建图像的任何上下文
后面的内容在首页不显示,请点击下方的展开全文或者
//取得画布中的一幅 PNG 格式的图像
var drawing = document.getElementById("drawing");
//确定浏览器支持
2D上下文,可以绘制简单2D图形
2D 上下文的坐标开始于元素的左上角,原点坐标是(0,0)。所有坐标值都基于这个原点计算,x 值越大表示越靠右,y 值越大表示越靠下。默认情况下,width 和 height 表示水平和垂直两个方向上可用的像素数目。
填充与描边
- 填充,就是用指定的样式(颜色、渐变或图像)填充图形;描边,就是只在图形的边缘画线,这两个操作的结果取决于于两个属性:fillStyle 和 strokeStyle。这两个属性的值可以是字符串、渐变对象或模式对象,而且它们的默认值都是"#000000"。
var drawing = document.getElementById("drawing");
//确定浏览器支持
绘制矩形
- 矩形是唯一一种可以直接在 2D 上下文中绘制的形状
- 与矩形有关的方法包括 fillRect()、strokeRect()和 clearRect()。这三个方法都能接收 4 个参数:矩形的 x 坐标、矩形的 y 坐标、矩形宽度和矩形高度。这些参数的单位都是像素。
- fillRect()方法在画布上绘制的矩形会填充指定的颜色。填充的颜色通过 fillStyle 属性指定
- strokeRect()方法在画布上绘制的矩形会使用指定的颜色描边。描边颜色通过 strokeStyle 属性指定
- clearRect()方法用于清除画布上的矩形区域,用处在于把绘制上下文中的某一矩形区域变透明,例如可以实现把某个形状切掉一块
绘制路径
- 要绘制路径,首先必须调用 beginPath()方法,表示要开始绘制新路径。然后,再通过调用下列方法来实际地绘制路径。
- arc(x, y, radius, startAngle, endAngle, counterclockwise):以(x,y)为圆心绘制一条弧线,弧线半径为 radius,起始和结束角度(用弧度表示)分别为 startAngle 和endAngle。最后一个参数表示 startAngle 和 endAngle 是否按逆时针方向计算,值为 false表示按顺时针方向计算。
- arcTo(x1, y1, x2, y2, radius):从上一点开始绘制一条弧线,到(x2,y2)为止,并且以给定的半径 radius 穿过(x1,y1)。 bezierCurveTo(c1x, c1y, c2x, c2y, x, y):从上一点开始绘制一条曲线,到(x,y)为止,并且以(c1x,c1y)和(c2x,c2y)为控制点。
- lineTo(x, y):从上一点开始绘制一条直线,到(x,y)为止。
- moveTo(x, y):将绘图游标移动到(x,y),不画线。
- quadraticCurveTo(cx, cy, x, y):从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)作为控制点。
- rect(x, y, width, height):从点(x,y)开始绘制一个矩形,宽度和高度分别由 width 和height 指定。这个方法绘制的是矩形路径,而不是 strokeRect()和 fillRect()所绘制的独立的形状。
- 创建完路径后,如果想绘制一条连接到路径起点的线条,可以调用closePath();
- 如果路径已经完成,你想用 fillStyle 填充它,可以调用 fill()方法。
- 可以调用 stroke()方法对路径描边,描边使用的是 strokeStyle
- 调用 clip(),这个方法可以在路径上创建一个剪切区域
// 一个不带数字的简易时钟
//html代码
//js代码
var drawing = document.getElementById("drawing");
//确定浏览器支持
- isPointInPath()的方法。这个方法接收 x 和 y 坐标作为参数,用于在路径被关闭之前确定画布上的某一点是否位于路径上
绘制文本
- 绘制文本主要有两个方法:fillText()和 strokeText()。这两个方法都可以接收 4 个参数:要绘制的文本字符串、x 坐标、y 坐标和可选的最大像素宽度。而且,这两个方法都以下列 3 个属性为基础。
- font:表示文本样式、大小及字体,用 CSS 中指定字体的格式来指定,例如"10px Arial"。
- textAlign:表示文本对齐方式。可能的值有"start"、“end”、“left”、“right"和"center”。建议使用"start"和"end",不要使用"left"和"right",因为前两者的意思更稳妥,能同时适合从左到右和从右到左显示(阅读)的语言。
- textBaseline:表示文本的基线。可能的值有"top"、“hanging”、“middle”、“alphabetic”、“ideographic"和"bottom”。
- fillText()方法使用fillStyle 属性绘制文本,而 strokeText()方法使用 strokeStyle 属性为文本描边
context.font = "bold 14px Arial";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText("12", 100, 20);
- 确定文本大小的方法 measureText()。这个方法接收一个参数,即要绘制的文本;返回一个 TextMetrics对象。返回的对象目前只有一个 width 属性
变换
- 可以通过如下方法来修改变换矩阵
- rotate(angle):围绕原点旋转图像 angle 弧度。
- scale(scaleX, scaleY):缩放图像,在 x 方向乘以 scaleX,在 y 方向乘以 scaleY。scaleX和 scaleY 的默认值都是 1.0。
- translate(x, y):将坐标原点移动到(x,y)。执行这个变换之后,坐标(0,0)会变成之前由(x,y)表示的点。
- transform(m1_1, m1_2, m2_1, m2_2, dx, dy):直接修改变换矩阵,方式是乘以如下矩阵。
- setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy):将变换矩阵重置为默认状态,然后再调用 transform()。
- 如果将来还要返回某组属性与变换的组合,可以调用 save()方法。调用这个方法后,当时的所有设置都会进入一个栈结构,得以妥善保管。然后可以对上下文进行其他修改。等想要回到之前保存的设置时,可以调用 restore()方法,在保存设置的栈结构中向前返回一级,恢复之前的状态。连续调用 save()可以把更多设置保存到栈结构中,之后再连续调用 restore()则可一级一级返回。
绘制图像
- drawImage()方法把一幅图像绘制到画布上,可以使用三种不同的参数组合
- 传入一个 HTML 元素,以及绘制该图像的起点的 x 和 y 坐标
var image = document.images[0];
context.drawImage(image, 10, 10);
- 在上一种方式的基础上,改变绘制后图像的大小,可以再多传入两个参数,分别表示目标宽度和目标高度
//绘制出来的图像大小会变成 20×30 像素
context.drawImage(image, 50, 10, 20, 30);
- 把图像中的某个区域绘制到上下文中,drawImage()方法的这种调用方式总共需要传入 9 个参数:要绘制的图像、源图像的 x 坐标、源图像的 y 坐标、源图像的宽度、源图像的高度、目标图像的 x 坐标、目标图像的 y 坐标、目标图像的宽度、目标图像的高度
阴影
- 上下文会根据以下几个属性的值,自动为形状或路径绘制出阴影。这些属性都可以通过 context 对象来修改
- shadowColor:用 CSS 颜色格式表示的阴影颜色,默认为黑色。
- shadowOffsetX:形状或路径 x 轴方向的阴影偏移量,默认为 0。
- shadowOffsetY:形状或路径 y 轴方向的阴影偏移量,默认为 0。
- shadowBlur:模糊的像素数,默认 0,即不模糊。
渐变
- 要创建一个新的线性渐变,可以调用 createLinearGradient()方法。这个方法接收 4 个参数:起点的 x 坐标、起点的 y 坐标、终点的 x 坐标、终点的 y 坐标。调用这个方法后,它就会创建一个指定大小的渐变,并返回CanvasGradient 对象的实例
- 创建了渐变对象后,下一步就是使用 addColorStop()方法来指定色标。这个方法接收两个参数:色标位置和 CSS 颜色值。色标位置是一个 0(开始的颜色)到 1(结束的颜色)之间的数字
//与fillStyle 或 strokeStyle配合使用
var gradient = context.createLinearGradient(30, 30, 70, 70);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
- 要创建径向渐变(或放射渐变),可以使用 createRadialGradient()方法。这个方法接收 6 个参数,对应着两个圆的圆心和半径。前三个参数指定的是起点圆的原心(x 和 y)及半径,后三个参数指定的是终点圆的原心(x 和 y)及半径
模式
- 模式其实就是重复的图像,可以用来填充或描边图形。要创建一个新模式,可以调用createPattern()方法并传入两个参数:一个 HTML 元素和一个表示如何重复图像的字符串。其中,第二个参数的值包括"repeat"、“repeat-x”、“repeat-y"和"no-repeat”。
使用图像数据
- 通过 getImageData()取得原始图像数据。这个方法接收4 个参数:要取得其数据的画面区域的 x 和 y 坐标以及该区域的像素宽度和高度,返回的对象是 ImageData 的实例。每个 ImageData 对象都有三个属性:width、height 和data。其中 data 属性是一个数组,保存着图像中每一个像素的数据
合成
两个会应用到 2D 上下文中所有绘制操作的属性
- globalAlpha 是一个介于 0 和 1 之间的值(包括 0 和 1),用于指定所有绘制的透明度。默认值为 0
- globalCompositionOperation 表示后绘制的图形怎样与先绘制的图形结合。这个属性的值是字符串[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AHBOwo5K-1569921382455)(JavaScript高级程序设计_files/3.jpg)]
WebGL,暂时略过
类型化数组
WebGL上下文
支持
HTML5脚本编程
跨文档消息传递
- 跨文档消息传送(cross-document messaging)简称为 XDM,指的是在来自不同域的页面间传递消息
- XDM 的核心是 postMessage()方法,接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方
//注意:所有支持 XDM 的浏览器也支持 iframe 的 contentWindow 属性
var iframeWindow = document.getElementById("myframe").contentWindow;
iframeWindow.postMessage("A secret", "http://www.wrox.com");
- 接收到 XDM 消息时,会触发 window 对象的 message 事件(异步,接受信息会有延迟),触发 message事件后,传递给 onmessage 处理程序的事件对象包含以下三方面的重要信息
- data:作为 postMessage()第一个参数传入的字符串数据。
- origin:发送消息的文档所在的域,例如"http://www.wrox.com"。
- source:发送消息的文档的 window 对象的代理。这个代理对象主要用于在发送上一条消息的窗口中调用 postMessage()方法。如果发送消息的窗口来自同一个域,那这个对象就是window。不能通过这个代理对象访问 window 对象的其他任何信息
原生拖放,按住鼠标不放就可以拖动它
拖放事件
- 拖动某元素时,将依次触发下列事件
- dragstart ,按下鼠标键并开始移动鼠标时
- drag ,在元素被拖动期间会持续触发该事件
- dragend,拖动停止时
- 当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生:
- dragenter ,只要有元素被拖动到放置目标上才回触发
- dragover ,在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件
- dragleave 或 drop,元素被拖出了放置目标
自定义放置目标
有些元素默认是不允许放置的,可以把任何元素变成有效的放置目标,方法是重写 dragenter 和 dragover 事件的默认行为
dataTransfer对象
- dataTransfer 对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据,它是事件对象的属性,所以只能在拖放事件的事件处理程序中访问 dataTransfer 对象
- dataTransfer 对象有两个主要方法:getData()和 setData()
dropEffect与effectAllowed
- 通过 dropEffect 属性可以知道被拖动的元素能够执行哪种放置行为
- “none”:不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值。
- “move”:应该把拖动的元素移动到放置目标。
- “copy”:应该把拖动的元素复制到放置目标。
- “link”:表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有 URL)。
- dropEffect 属性只有搭配 effectAllowed 属性才有用。effectAllowed 属性表示允许拖动元素的哪种 dropEffect,effectAllowed 属性可能的值如下
- “uninitialized”:没有给被拖动的元素设置任何放置行为。
- “none”:被拖动的元素不能有任何行为。
- “copy”:只允许值为"copy"的 dropEffect。
- “link”:只允许值为"link"的 dropEffect。
- “move”:只允许值为"move"的 dropEffect。
- “copyLink”:允许值为"copy"和"link"的 dropEffect。
- “copyMove”:允许值为"copy"和"move"的 dropEffect。
- “linkMove”:允许值为"link"和"move"的 dropEffect。
- “all”:允许任意 dropEffect。
可拖动
- draggable 属性,表示元素是否可以拖动,图像和链接的 draggable 属性自动被设置成了 true,而其他元素这个属性的默认值都是 false,要改变可以设置
其他成员
媒体元素,和。
属性
- 和共有属性:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRluMNtW-1569921382457)(JavaScript高级程序设计_files/4.jpg)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mthnKwcC-1569921382459)(JavaScript高级程序设计_files/5.jpg)]
事件
媒体元素相关的事件[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i5wv1Wxv-1569921382460)(JavaScript高级程序设计_files/8.jpg)]
自定义媒体播放器
- 使用和元素的 play()和 pause()方法,可以手工控制媒体文件的播放。组合使
用属性、事件和这两个方法,很容易创建一个自定义的媒体播放器
检测编解码器的支持情况
- canPlayType()方法,该方法接收一种格式/编解码器字符串,返回"probably"、“maybe"或”"( 空字符串)
Audio类型
历史状态管理
错误处理和调试
浏览器报错
IE
火狐
Safari
Opera
Chrome
错误处理
try-catch语句,跟Java一样的
- 语法
try{
// 可能会导致错误的代码
} catch(error){
// 在错误发生时怎么处理
}
- 运行机制:如果 try 块中的任何代码发生了错误,就会立即退出代码执行过程,然后接着执行 catch 块。此时,catch 块会接收到一个包含错误信息的对象,该对象有一个保存着错误消息的 message 属性
- finally 子句,一经使用,其代码无论如何都会执行
- 注意:只要代码中包含finally 子句,那么无论try 还是catch 语句块中的return 语句都将被忽略
- 错误类型
- Error ,基类
- EvalError ,使用 eval()函数而发生异常时被抛出
- RangeError ,数值超出相应范围时触发
- ReferenceError ,找不到对象的情况发生,一般是访问不存在的变量
- SyntaxError ,语法错误
- TypeError ,类型不符,在变量中保存着意外的类型时,或者在访问不存在的方法时
- URIError,使用 encodeURI()或 decodeURI(),而 URI 格式不正确时
- 当 try-catch 语句中发生错误时,浏览器会认为错误已经被处理了,因而不会通过浏览器的机制记录或报告错误
抛出错误
- throw 操作符,用于随时抛出自定义错误。抛出错误时,必须要给 throw 操作符指定一个值,这个值没有类型要求
- 在遇到 throw 操作符时,代码会立即停止执行。仅当有 try-catch 语句捕获到被抛出的值时,代码才会继续执行
- 通过原生错误类型,可以自定义错误消息
throw new SyntaxError("I don’t like your syntax.");
- 可以通过原型链自定义错误类型
function CustomError(message){
this.name = "CustomError";
this.message = message;
}
CustomError.prototype = new Error();
throw new CustomError("My message");
- 抛出错误的时机
function process(values){
if (!(values instanceof Array)){
throw new Error("process(): Argument must be an array.");
}
values.sort();
for (var i=0, len=values.length; i < len; i++){
if (values[i] > 100){
return values[i];
}
}
return -1;
}
- 抛出错误与使用try-catch,只应该捕获那些你确切地知道该如何处理的错误。捕获错误的目的在于避免浏览器以默认方式处理它们;而抛出错误的目的在于提供错误发生具体原因的消息
错误(error)事件
任何没有通过 try-catch 处理的错误都会触发 window 对象的 error 事件,在任何 Web 浏览器中,onerror 事件处理程序都不会创建 event 对象,但它可以接收三个参数:错误消息、错误所在的 URL 和行号。多数情况下,只有错误消息有用
处理错误的策略
常见的错误类型
- 类型转换错误
- 数据类型错误
- 通信错误
区分致命错误和非致命错误
- 对于非致命错误,可以根据下列一或多个条件来确定:
- 不影响用户的主要任务;
- 只影响页面的一部分;
- 可以恢复;
- 重复相同操作可以消除错误。
- 致命错误:
- 应用程序根本无法继续运行;
- 错误明显影响到了用户的主要操作;
- 会导致其他连带错误
把错误记录到服务器,错误日志
- 要建立这样一种 JavaScript 错误记录系统,首先需要在服务器上创建一个页面(或者一个服务器入口点),用于处理错误数据。这个页面的作用无非就是从查询字符串中取得数据,然后再将数据写入错误日志中,这个页面可能使用这样的函数
function logError(sev, msg){
var img = new Image();
img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" +
encodeURIComponent(msg);
}
- 只要是使用 try-catch 语句,就应该把相应错误记录到日志中
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex){
logError("nonfatal", "Module init failed: " + ex.message);
}
}
调试技术
把消息记录到控制台,console
把消息记录到当前页面,过时啦
抛出错误
- 使用throw
function divide(num1, num2){
if (typeof num1 != "number" || typeof num2 != "number"){
throw new Error("divide(): Both arguments must be numbers.");
}
return num1 / num2;
}
- 对于大型应用程序来说,自定义的错误通常都使用 assert()函数抛出。这个函数接受两个参数,一个是求值结果应该为 true 的条件,另一个是条件为 false 时要抛出的错误
function assert(condition, message){
if (!condition){
throw new Error(message);
}
}
//示例
function divide(num1, num2){
assert(typeof num1 == "number" && typeof num2 == "number",
"divide(): Both arguments must be numbers.");
return num1 / num2;
}
常见IE错误
操作终止operation aborted
- 在修改尚未加载完成的页面时,就会发生操作终止错误。发生错误时,会出现一个模态对话框,告诉你“操作终止。”单击确定(OK)按钮,则卸载整个页面,继而显示一张空白屏幕
无效字符invalid character
未找到成员Member not found,一般是于垃圾收集例程配合错误所直接导致
未知运行时错误Unknown runtime error,一是把块元素插入到行内元素时,二是访问表格任意部分(、等)的任意属性时
语法错误syntax error
系统无法找到指定资源The system cannot locate the resource specified
JavaScript与XML,跳过
E4X,也是跟XML有关,跳过
JSON,是一种数据格式,不从属于 JavaScript,别的语音也可以用
语法
- JSON 的语法可以表示以下三种类型的值
- 简单值:使用与 JavaScript 相同的语法,可以在 JSON 中表示字符串、数值、布尔值和 null。 但 JSON 不支持 JavaScript 中的特殊值 undefined。
- 对象:对象作为一种复杂数据类型,表示的是一组无序的键值对儿。而每个键值对儿中的值可以是简单值,也可以是复杂数据类型的值。
- 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,可以通过数值索引来访问其中的值。数组的值也可以是任意类型——简单值、对象或数组。
- JSON 不支持变量、函数或对象实例,它就是一种表示结构化数据的格式
简单值
- JSON 字符串必须使用双引号
对象
- JSON 中的对象要求给属性加引号
{
"name": "Nicholas",
"age": 29
}
- 与js的对象字面量的区别
- 没有声明变量,json中没有变量的概念
- 没有末尾的分号,因为不是js语句
- 属性的值可以是简单值,也可以是复杂类型值包括对象
- 同一个对象中绝对不应该出现两个同名属性
数组
- JSON 数组采用的就是 JavaScript 中的数组字面量形式
//没有变量和分号
[25, "hi", true]
- 对象和数组可以结合使用
解析与序列化
可以把JSON 数据结构解析为有用的 JavaScript 对象,代码精简
json对象
- JSON 对象有两个方法:stringify()和 parse()。在最简单的情况下,这两个方法分别用于把JavaScript 对象序列化为 JSON 字符串和把 JSON 字符串解析为原生 JavaScript 值
- stringify()示例
var book = {
title: "Professional JavaScript",
authors: [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book);
- 默认情况下,JSON.stringify()输出的 JSON 字符串不包含任何空格字符或缩进,因此保存在 jsonText 中的字符串如下所示:
{"title":"Professional JavaScript","authors":["Nicholas C. Zakas"],"edition":3,
"year":2011}
- 在序列化 JavaScript 对象时,所有函数及原型成员都会被有意忽略,不体现在结果中。此外,值为undefined 的任何属性也都会被跳过。结果中最终都是值为有效 JSON 数据类型的实例属性
- parse()示例
//创建与 book 类似的对象
var bookCopy = JSON.parse(jsonText);
- book 与 bookCopy 具有相同的属性,但它们是两个独立的、没有任何关系的对象
序列化选项
- JSON.stringify()除了要序列化的 JavaScript 对象外,还可以接收另外两个参数,这两个参数用于指定以不同的方式序列化 JavaScript 对象,第一个参数是个过滤器,可以是一个数组,也可以是一个函数;第二个参数是一个选项,表示是否在 JSON 字符串中保留缩进
- 过滤结果
- 如果过滤器参数是数组,那么 JSON.stringify()的结果中将只包含数组中列出的属性
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011
};
var jsonText = JSON.stringify(book, ["title", "edition"]);
//json结果
{"title":"Professional JavaScript","edition":3}
- 第二个参数是函数,传入的函数接收两个参数,属性(键)名和属性值,函数返回的值就是相应键的值,如果函数返回了undefined,那么相应的属性会被忽略
- 字符串缩进,JSON.stringify()方法的第三个参数用于控制结果中的缩进和空白符。如果这个参数是一个数值,那它表示的是每个级别缩进的空格数
- toJSON()方法,在stringify()不能满足序列化需求时,可以给对象定义toJSON()方法,返回其自身的 JSON 数据格式,可以为任何对象添加 toJSON()方法
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011,
toJSON: function(){
return this.title;
}
};
var jsonText = JSON.stringify(book);
- 把一个对象传入 JSON.stringify(),序列化该对象的顺序如下
- 如果存在 toJSON()方法而且能通过它取得有效的值,则调用该方法。否则,返回对象本身。
- 如果提供了第二个参数,应用这个函数过滤器。传入函数过滤器的值是第(1)步返回的值。
- 对第(2)步返回的每个值进行相应的序列化。
- 如果提供了第三个参数,执行相应的格式化。
解析选项
- JSON.parse()方法也可以接收另一个参数,该参数是一个函数(还原函数),将在每个键值对儿上调用
- 如果还原函数返回 undefined,则表示要从结果中删除相应的键;如果返回其他值,则将该值插入到结果中,在将日期字符串转换为 Date 对象时,经常要用到还原函数
var book = {
"title": "Professional JavaScript",
"authors": [
"Nicholas C. Zakas"
],
edition: 3,
year: 2011,
releaseDate: new Date(2011, 11, 1)
};
var jsonText = JSON.stringify(book);
var bookCopy = JSON.parse(jsonText, function(key, value){
if (key == "releaseDate"){
return new Date(value);
} else {
return value;
}
});
Ajax与Comet
Ajax能够向服务器请求额外的数据而无须卸载页面, 技术的核心是 XMLHttpRequest 对象(简称 XHR)
XMLHttpRequest 对象
- IE7以前使用 MSXML 库中的 XHR 对象
//适用于 IE7 之前的版本
function createXHR(){
if (typeof arguments.callee.activeXString != "string"){
var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i, len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
}
- IE7之后用XMLHttpRequest 构造函数。
var xhr = new XMLHttpRequest();
- 兼容IE7与新的
function createXHR(){
if (typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){
if (typeof arguments.callee.activeXString != "string"){
var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i, len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}
XHR的用法
- 使用 XHR 对象时,要调用的第一个方法是 open(),它接受 3 个参数:要发送的请求的类型(“get”、"post"等)、请求的 URL 和表示是否异步发送请求的布尔值
//启动一个针对 example.php 的 GET 请求
xhr.open("get", "example.php", false);
- URL相对于执行代码的当前页面(当然也可以使用绝对路径)
- 调用 open()方法并不会真正发送请求,而只是启动一个请求以备发送
- 只能向同一个域中使用相同端口和协议的 URL 发送请求
- 要发送特定的请求,必须像下面这样调用 send()方法将请求发送到服务器:send()方法接收一个参数,即要作为请求主体发送的数据。如果不需要通过请求主体发送数据,则必须传入 null.
xhr.open("get", "example.txt", false);
xhr.send(null);
- 这次请求是同步的,JavaScript 代码会等到服务器响应之后再继续执行
- 收到响应后,响应的数据会自动填充 XHR 对象的属性
- responseText:作为响应主体被返回的文本。
- responseXML:如果响应的内容类型是"text/xml"或"application/xml",这个属性中将保存包含着响应数据的 XML DOM 文档。
- status:响应的 HTTP 状态。
- statusText:HTTP 状态的说明。
- 为确保接收到适当的响应,应该像下面这样检查上述这两种状态代码
xhr.open("get", "example.txt", false);
xhr.send(null);
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
- 发送异步请求,此时,可以检测 XHR 对象的 readyState 属性,该属性表示请求/响应过程的当前活动阶段,这个属性的取值有
- 0:未初始化。尚未调用 open()方法。
- 1:启动,服务器连接已经建立。已经调用 open()方法,但尚未调用 send()方法。
- 2:发送。已经调用 send()方法,但尚未接收到响应。
- 3:接收。已经接收到部分响应数据。
- 4:完成。已经接收到全部响应数据,而且已经可以在客户端使用了
- 只要 readyState 属性的值由一个值变成另一个值,都会触发一次 readystatechange 事件,通常,我们只对 readyState 值为 4 的阶段感兴趣,因为这时所有数据都已经就绪,注意必须在调用 open()之前指定 onreadystatechange事件处理程序才能确保跨浏览器兼容性
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "example.txt", true);
xhr.send(null);
- 在接收到响应之前还可以调用 abort()方法来取消异步请求
//XHR 对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性
xhr.abort();
HTTP头部信息
- 默认情况下,在发送 XHR 请求的同时,还会发送下列头部信息。
- Accept:浏览器能够处理的内容类型。
- Accept-Charset:浏览器能够显示的字符集。
- Accept-Encoding:浏览器能够处理的压缩编码。
- Accept-Language:浏览器当前设置的语言。
- Connection:浏览器与服务器之间连接的类型。
- Cookie:当前页面设置的任何 Cookie。
- Host:发出请求的页面所在的域 。
- Referer:发出请求的页面的 URI。注意,HTTP 规范将这个头部字段拼写错了,而为保证与规范一致,也只能将错就错了。(这个英文单词的正确拼法应该是 referrer。)
- User-Agent:浏览器的用户代理字符串。
- 使用 setRequestHeader()方法可以设置自定义的请求头部信息。这个方法接受两个参数:头部字段的名称和头部字段的值,要成功发送请求头部信息,必须在调用 open()方法之后且调用 send()方法之前调用 setRequestHeader()
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("get", "example.php", true);
xhr.setRequestHeader("MyHeader", "MyValue");
xhr.send(null);
- 调用 XHR 对象的 getResponseHeader()方法并传入头部字段名称,可以取得相应的响应头部信息。而调用 getAllResponseHeaders()方法则可以取得一个包含所有头部信息的长字符串。
var myHeader = xhr.getResponseHeader("MyHeader");
var allHeaders = xhr.getAllResponseHeaders();
GET请求
- 使用 GET 请求经常会发生的一个错误,就是查询字符串的格式有问题,查询字符串中每个参数的名称和值都必须使用 encodeURIComponent()进行编码,而且所有名-值对儿都必须由和号(&)分隔
//这个函数可以辅助向现有 URL 的末尾添加查询字符串参数
function addURLParam(url, name, value) {
url += (url.indexOf("?") == -1 ? "?" : "&");
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
//使用示例
var url = "example.php";
//添加参数
url = addURLParam(url, "name", "Nicholas");
url = addURLParam(url, "book", "Professional JavaScript");
//初始化请求
xhr.open("get", url, false);
POST请求
- 在 open()方法第一个参数的位置传入"post",就可以初始化一个 POST 请求
xhr.open("post", "example.php", true);
- 发送 POST 请求的第二步就是向 send()方法中传入某些数据
XMLHttpRequest 2 级
FormData
- FormData 为序列化表单以及创建与表单格式相同的数据(用于通过 XHR 传输)提供了便利。
var data = new FormData();
//append()方法接收两个参数:键和值,分别对应表单字段的名字和字段中包含的值
data.append("name", "Nicholas");
//用表单元素的数据预先向其中填入键值对儿
var data = new FormData(document.forms[0]);
- 创建了 FormData 的实例后,可以将它直接传给 XHR 的 send()方法
var xhr = createXHR();
xhr.onreadystatechange = function(){
if (xhr.readyState == 4){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
}
};
xhr.open("post","postexample.php", true);
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
超时设定,兼容性差
- timeout 属性,表示请求在等待响应多少毫秒之后就终止,在给timeout 设置一个数值后,如果在规定的时间内浏览器还没有接收到响应,那么就会触发 timeout 事件,进而会调用 ontimeout 事件处理程序
overrideMimeType()方法
- overrideMimeType()方法,用于重写 XHR 响应的 MIME 类型
进度事件,了解就好
- 有6个进度事件
- loadstart:在接收到响应数据的第一个字节时触发。
- progress:在接收响应期间持续不断地触发。
- error:在请求发生错误时触发。
- abort:在因为调用 abort()方法而终止连接时触发。
- load:在接收到完整的响应数据时触发。
- loadend:在通信完成或者触发 error、abort 或 load 事件后触发。
- 每个请求都从触发 loadstart 事件开始,接下来是一或多个 progress 事件,然后触发 error、abort 或 load 事件中的一个,最后以触发 loadend 事件结束。
load事件
- load 事件,用以替代 readystatechange 事件。响应接收完毕后将触发 load 事件,因此也就没有必要去检查 readyState 属性
- onload 事件处理程序会接收到一个 event 对象,其 target 属性就指向 XHR 对象实例,因而可以访问到 XHR 对象的所有方法和属性
- 为保证兼容性,还是要检查status
var xhr = createXHR();
xhr.onload = function(){
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
progress事件
- progress 事件,这个事件会在浏览器接收新数据期间周期性地触发,onprogress 事件处理程序会接收到一个 event 对象,其 target 属性是 XHR 对象,但包含着三个额外的属性:lengthComputable、position 和 totalSize
- lengthComputable是一个表示进度信息是否可用的布尔值
- position 表示已经接收的字节数
- totalSize 表示根据Content-Length 响应头部确定的预期字节数
//运用示例
var xhr = createXHR();
xhr.onload = function(event){
if ((xhr.status >= 200 && xhr.status < 300) ||
xhr.status == 304){
alert(xhr.responseText);
} else {
alert("Request was unsuccessful: " + xhr.status);
}
};
xhr.onprogress = function(event){
var divStatus = document.getElementById("status");
if (event.lengthComputable){
divStatus.innerHTML = "Received " + event.position + " of " +
event.totalSize +" bytes";
}
};
xhr.open("get", "altevents.php", true);
xhr.send(null);
跨资源共享CORS(Cross-Origin Resource Sharing
- 默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源,CORS的出现是为了实现合理的跨域请求
- CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败