<script>
元素。使用这个元素能够把JavaScript嵌入到HTML页面中,让脚本与标记混合在一块儿;也能够包含外部的JavaScript文件。而咱们须要注意的地方有:
<script>
元素都会按照它们在页面中出现的前后顺序依次被解析。在不使用defer和async属性的状况下,只有在解析完前面<script>
元素中的代码以后,才会开始解析后面<script>
元素中的代码。<script>
元素中的代码,而后再解析后面的内容,因此通常应该把<script>
元素放在页面最后,即主要内容后面,</body>
标签前面。<noscript>
元素能够指定在不支持脚本的浏览器中显示的替代内容。但在启用了脚本的状况下,浏览器不会显示<noscript>
元素中的任何内容。ECMAScript 5引入了严格模式(strictmode)的概念。严格模式是为JavaScript定义了一种不一样的解析与执行模型。在严格模式下,ECMAScript 3中的一些不肯定的行为将获得处理,并且对某些不安全的操做也会抛出错误。要在整个脚本中启用严格模式,能够在顶部添加以下代码:javascript
"use strict";php
在函数内部的上方包含这条编译指示,也能够指定函数在严格模式下执行:html
functiondoSomething(){ "usestrict"; //函数体 }
严格模式是一个编译指示,目的是为了兼容 ECMAScript 3语法。html5
var
操做符能够定义全局变量,但这也不是咱们推荐的作法。由于在局部做用域中定义的全局变量很难维护,并且若是有意地忽略了var
操做符,也会因为相应变量不会立刻就有定义而致使没必要要的混乱。给未经声明的变量赋值在严格模式下会致使抛出ReferenceError
错误。省略
var
可声明全局变量,但不推荐使用。java
使用操做符typeof能够检测变量的数据类型。jquery
实际上,undefined值是派生自null值的,所以ECMA-262规定对它们的相等性测试要返回true:web
alert(null == undefined); //trueajax
不管在什么状况下都没有必要把一个变量的值显式地设置为undefined,但是一样的规则对null却不适用。换句话说,只要意在保存对象的变量尚未真正保存对象,就应该明确地让该变量保存null值。这样作不只能够体现null做为空对象指针的惯例,并且也有助于进一步区分null和undefined。编程
数据类型 | 转换为true的值 | 转换为false的值 |
---|---|---|
Boolean | true | false |
String | 任何非空字符串 | ""(空字符串) |
Number | 任何非零数字值(包括无穷大) | 0和NaN(参见本章后面有关NaN的内容) |
Object | 任何对象 | bull |
Undefined | n/a | undefined |
n/a(或N/A),是notapplicable的缩写,意思是“不适用”。json
八进制字面量在严格模式下是无效的,会致使支持的JavaScript引擎抛出错误。在进行算术计算时,全部以八进制和十六进制表示的数值最终都将被转换成十进制数值。
浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1加0.2的结果不是0.3,而是0.30000000000000004。这个小小的舍入偏差会致使没法测试特定的浮点数值。例如:
if(a + b == 0.3){ //不要作这样的测试! alert("You got 0.3."); }
永远不要测试某个特定的浮点数值。这是使用基于IEEE754数值的浮点计算的通病。
因为内存的限制,ECMAScript并不能保存世界上全部的数值。ECMAScript可以表示的最小数值保存在Number.MIN_VALUE中——在大多数浏览器中,这个值是5e-324;可以表示的最大数值保存在Number.MAX_VALUE中——在大多数浏览器中,这个值是1.7976931348623157e+308。若是某次计算的结果获得了一个超出JavaScript数值范围的值,那么这个数值将被自动转换成Infinity值。具体来讲,若是这个数值是负数,则会被转换成-Infinity(负无穷),若是这个数值是正数,则会被转换成Infinity(正无穷)。
NaN,即非数值(NotaNumber)是一个特殊的数值,这个数值用于表示一个原本要返回数值的操做数未返回数值的状况(这样就不会抛出错误了)。NaN有两个特色。首先,任何涉及NaN的操做(例如NaN/10)都会返回NaN,这个特色在多步计算中有可能致使问题。其次,NaN与任何值都不相等,包括NaN自己。例如,下面的代码会返回false:
alert(NaN == NaN); //false
针对NaN的这两个特色,ECMAScript定义了isNaN()函数。这个函数接受一个参数,该参数能够是任何类型,而函数会帮咱们肯定这个参数是否“不是数值”。例如:
alert(isNaN(NaN)); //true alert(isNaN(10)); //false(10是一个数值) alert(isNaN("10")); //false(能够被转换成数值10) alert(isNaN("blue"));//true(不能转换成数值) alert(isNaN(true)); //false(能够被转换成数值1)
使用parseInt()进行转换时,应当在第二个参数指定基数。
仅仅建立Object的实例并无什么用处,但关键是要理解一个重要的思想:即在ECMAScript中,(就像Java中的java.lang.Object对象同样)Object类型是全部它的实例的基础。换句话说,Object类型所具备的任何属性和方法也一样存在于更具体的对象中。
B>a
为真,"23" < "3"
为真),最好避免隐式转换。===
和不全等!==
操做符,避免隐式转换带来的问题。关于返回值:推荐的作法是要么让函数始终都返回一个值,要么永远都不要返回值。不然,若是函数有时候返回值,有时候有不返回值,会给调试代码带来不便。
对参数的理解
arguments
对象来访问这个参数数组,从而获取传递给函数的每个参数。其实,
arguments
对象只是与数组相似(它并非Array的实例),由于可使用方括号语法访问它的每个元素(即第一个元素是arguments[0]
,第二个元素是arguments[1]
,以此类推),使用length属性来肯定传递进来多少个参数。ECMAScript不支持重载,但利用这一属性,能够模仿方法的重载。
function sayHi() { alert("Hello" + arguments[0] + "," + arguments[1]); }
这个重写后的函数中不包含命名的参数。虽然没有使用name和message标识符,但函数的功能依旧。这个事实说明了ECMAScript函数的一个重要特色:命名的参数只提供便利,但不是必需的。
第3章讨论了5种基本数据类型:Undefined、Null、Boolean、Number和String。这5种基本数据类型是按值访问的,由于能够操做保存在变量中的实际的值。
引用类型的值是保存在内存中的对象(Object)。与其余语言不一样,JavaScript不容许直接访问内存中的位置,也就是说不能直接操做对象的内存空间。在操做对象时,其实是在操做对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。
思考:
function setName( obj) { obj. name = "Nicholas"; obj = new Object(); obj.name = "Greg"; } var person = new Object(); setName( person); alert( person. name); //"Nicholas" //函数结束后,函数内建立的引用被销毁,只有对原引用的修改生效。
类型检测:判断基本数据类型使用typeof,判断对象类型用instanceof。
alert( person instanceof Object);
window. CollectGarbage()
或相似的函数)。null
,即解引用(主要针对全局变量)。splice()
:基于当前数组的一个或多个项建立数组。
var colors = ["red", "green", "blue", "yellow", "purple"]; var colors2 = colors. slice( 1); var colors3 = colors. slice( 1, 4); alert( colors2); //green, blue, yellow, purple alert( colors3); //green, blue, yellow
splice()
:对数组指定项执行删除/插入/替换操做。
array.splice(start[, deleteCount[, item1[, item2[, ...]]]]) // start:开始位置 deleteCount:删除项的数量 item1...:插入的项
迭代方法:every(),forEach(),filter(),map(),some()
传入的参数为迭代函数,该函数接受item, index, array
三个量。
var numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1]; var everyResult = numbers.every(function(item, index, array){ return (item > 2); }); alert( everyResult);
缩小方法:reduce(), reduceRight()
与上相似,经常使用于累加等操做。
Date.parse("datestring")
做为参数:注意非法日期返回值Date.UTC(year,month,day,hrs,min,sec,msec)
做为参数(月份从0开始)toString()
和toLocaleString()
有格式差异。valueOf()
返回日期的毫秒表示。var pattern1 = /[bc]at/i;
var pattern2 = new RegExp("[bc]at", "i");
arguments
和this
arguments
有callee
属性,该指针指向拥有该arguments
的函数。callee
属性用于函数递归能够下降函数的耦合程度。this
引用的是函数据以执行的环境对象。caller
属性,该指针指向调用该当前函数的引用。length
和prototype
length
定义了函数但愿接受的命名参数的个数。prototype
与函数继承的实现有关,以后详述。apply()
和call()
apply()
至关于设置函数体内this对象的值。
function sum(num1, num2){ return num1 + num2; } function callSum1(num1,num2){ return sum.apply( this, arguments); //传入arguments对象 } function callSum2(num1, num2){ return sum.apply( this, [num1, num2]); // 传入数组 } alert( callSum1( 10, 10)); //20 alert( callSum2( 10, 10)); //20
call()
与apply()
做用相同,但传递参数的方式不一样。
function sum(num1, num2){ return num1 + num2; } function callSum(num1, num2){ return sum. call( this, num1, num2); } alert( callSum( 10, 10)); //20
apply()
和call()
真正强大的地方是可以扩充函数赖以运行的做用域。使用call()
或apply()
来扩充做用域的最大最大好处,就是对象不须要与方法有任何耦合关系。ECMAScript 5还定义了一个方法:bind()
。这个方法会建立一个函数的实例,其this值会被绑定到传给bind()
函数的值。
window. color = "red"; var o = { color: "blue" }; function sayColor(){ alert(this. color); } var objectSayColor = sayColor. bind(o); objectSayColor(); //blue
substring()
方法等。销毁这个实例。
思考: var s1 = "some text"; s1. color = "red"; alert(s1.color); //undefined. why?
ECMAScript 经过原型来实现面向对象的程序设计,而非类或接口。
原型模式的问题:对于包含引用类型属性的类而言,修改其中一个对象的引用类型属性会致使其余共享同一原型的对象也发生变化。
例:person1
,person2
共享同一原型,原型包含Array类型的friend
属性,执行person1.friend.push_back("Jacob"); alert(person2.friend); // Jacob
建立自定义类型的最多见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。例:
function Person( name, age, job){ this. name = name; this. age = age; this. job = job; this. friends = ["Shelby", "Court"]; } Person. prototype = { constructor : Person, sayName : function(){ alert( this. name); } }
其余建立方式:动态原型模式、寄生构造函数模式、稳妥构造函数模式(须要时查询)。
JavaScript使用原型链实现继承,其本质是重写原型对象,代之以一个新类型的示例。
当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。若是没有找到该属性,则会继续搜索实例的原型。在经过原型链实现继承的状况下,搜索过程就得以沿着原型链继续向上。在找不到属性或方法的状况下,搜索过程老是要一环一环地前行到原型链末端才会停下来。
能够经过两种方式来肯定原型和实例之间的关系。第一种方式是使用instanceof
操做符,第二种方式是使用isPrototypeOf()
方法。
原型链的第二个问题是:在建立子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响全部对象实例的状况下,给超类型的构造函数传递参数。
继承方式:最经常使用的是组合继承,指的是将原型链和借用构造函数的技术组合到一块,从而发挥两者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,而经过借用构造函数来实现对实例属性的继承。这样,既经过在原型上定义方法实现了函数复用,又可以保证每一个实例都有它本身的属性。例:
//组合使用构造函数模式和原型模式 function SuperType(name){ this. name = name; this. colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function(){ alert( this. name); }; function SubType(name, age){ //继承属性 SuperType.call(this, name); this. age = age; } //继承方法 SubType.prototype = new SuperType(); SubType.prototype.sayAge = function(){ alert( this. age); }; var instance1 = new SubType(" Nicholas", 29); instance1. colors. push("black"); alert( instance1. colors); //"red, blue, green, black" instance1. sayName(); //"Nicholas"; instance1. sayAge(); //29 var instance2 = new SubType(" Greg", 27); alert( instance2. colors); //"red, blue, green" instance2. sayName(); //"Greg"; instance2. sayAge(); //27
在严格模式下不能经过脚本访问arguments.callee,则可使用匿名函数。如:
var factorial = (function f(num){ if (num <= 1){ return 1; } else { return num * f(num- 1); } });
闭包是指有权访问另外一个函数做用域中的变量的函数。建立闭包的常见方式,就是在一个函数内部建立另外一个函数。
在匿名函数从上一级函数中被返回后,它的做用域链被初始化为包含上一级函数的活动对象和全局变量对象。这样,匿名函数就能够访问在上一级函数中定义的全部变量。
更为重要的是,上一级函数在执行完毕后,其活动对象也不会被销毁,由于匿名函数的做用域链仍然在引用这个活动对象。换句话说,当上一级函数返回后,其执行环境的做用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁后,上级函数的活动对象才会被销毁。
过分使用闭包会致使内存占用过多,请只在绝对必要时考虑使用闭包。
做用域链的反作用:闭包只能取得包含函数中任何变量的最后一个值。例如:
function createFunctions(){ var result = new Array(); for (var i= 0; i < 10; i++){ result[ i] = function(){ return i; }; } return result; } // 执行 var a = createFunctions(); a[0](); // 10
缘由:result中每一项都是匿名函数,这些匿名函数返回了createFunctions()
中声明的i。createF1unctions()
运行结束时,i
的值为10,因为所以调用全部的匿名函数返回的值都是10。另外,这些匿名函数不销毁,createFunctions()
中的变量也不会释放。
解决办法:建立另外一个匿名函数,并传入对应的参数。
function createFunctions(){ var result = new Array(); for (var i = 0; i < 10; i++){ result[i] = function(num){ return function(){ return num; }; }(i); } return result; }
this对象的问题:匿名函数的执行环境具备全局性,所以其this对象一般指向window。
解决办法:使用闭包,可让匿名函数的this对象指向特定的对象。例如:
var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that. name; }; } }; alert( object. getNameFunc()()); //"My Object"
使用that
获取目标对象的this
,而后在匿名函数使用that
。
使用匿名函数模拟块级做用域:
(function(){ //这里是块级做用域 })(); // or var someFunction = function(){ //这里是块级做用域}; someFunction();
ECMAScript6以上的版本,使用let
取代var
来声明变量,便可实现块级做用域变量的声明。
简单来讲就是经过做用域链的特性来隐藏不该该被直接修改的数据,例如:
function Person(name){ this. getName = function(){ return name; }; this. setName = function (value) { name = value; }; } var person = new Person(" Nicholas"); alert( person. getName()); //"Nicholas" person. setName(" Greg"); alert( person. getName()); //"Greg"
此外还支持使用闭包来实现单例模式,须要时浏览 7.4.3 模块模式。
window对象也是ECMAScript的global对象,全局做用域中的变量、函数都会变成window对象的属性和方法。经过查询window对象,能够知道某个可能未声明的变量是否存在,例如:
//这里会抛出错误,由于oldValue未定义 var newValue = oldValue; //这里不会抛出错误,由于这是一次属性查询 //newValue的值是undefined var newValue = window. oldValue;
本章我的认为不是重点。
最经常使用也最为人们普遍接受的客户端检测形式是能力检测(又称特性检测)。能力检测的目标不是识别特定的浏览器,而是识别浏览器的能力。采用这种方式没必要顾及特定的浏览器如何如何,只要肯定浏览器支持特定的能力,就能够给出解决方案。
要理解能力检测,首先必须理解两个重要的概念。如前所述,第一个概念就是先检测达成目的的最经常使用的特性。对前面的例子来讲,就是要先检测document.getElementById(),后检测document.all。先检测最经常使用的特性能够保证代码最优化,由于在多数状况下均可以免测试多个条件。第二个重要的概念就是必须测试实际要用到的特性。一个特性存在,不必定意味着另外一个特性也存在。即:不要经过一个特性是否存在来判断浏览器类型。
通常来讲,使用typeof
操做符来进行能力检测(判断方法是否是函数),但由于typeof
在IE中存在行为不标准的状况,所以建议使用下面的函数来测试任何对象的某个特性是否存在:
//做者: Peter Michaux function isHostMethod(object, property) { var t = typeof object[property]; return t==' function' || (!!(t==' object' && object[property])) || t==' unknown'; }
建议仅检测那些对你有直接影响的“怪癖”,并且最好在脚本一开始就执行此类检测,以便尽早解决问题。
getElementById()
和getElementByTagName()
。querySelector()
和querySelectorAll()
。在兼容的浏览器中,能够经过Document及Element类型的实例调用它们。querySelector()
方法接收一个CSS选择符,返回与该模式匹配的第一个元素,若是没有找到匹配的元素,返回null。例:
//取得body元素 var body = document.querySelector(" body"); //取得ID为"myDiv"的元素 var myDiv = document.querySelector("# myDiv"); //取得类为"selected"的第一个元素 var selected = document.querySelector(". selected");
经过Doument类型调用querySelector()
方法时,会在文档元素的范围内查找匹配的元素。而经过Element类型调用querySelector()
方法时,只会在该元素后代元素的范围内查找匹配的元素。
querySelectorAll()
一样接受CSS选择符,但返回匹配该选择符的全部元素(一个NodeList实例)。若是没有匹配的元素,NodeList为空。取得这些元素能够经过item()
方法,也可使用方括号访问。
与querySelector()
相似,可以调用querySelectorAll()
方法的类型包括Document、DocumentFragment和Element。
对于元素间的空格,IE9及以前版本不会返回文本节点,而其余全部浏览器都会返回文本节点。这样,就致使了在使用childNodes和firstChild等属性时的行为不一致。为了弥补这一差别,而同时又保持DOM规范不变,ElementTraversal规范新定义了一组属性。
getElementByClassName()
:返回带有指定类的全部元素的NodeList,只有位于调用元素子树中的元素才会返回。add(value), contains(value), remove(value), toggle(value)
便可很是方便地管理class属性。focus()
:得到焦点, document.activeElement
:获取document中的焦点元素, hasFocus()
判断元素是否得到焦点。readyState
属性:使用document.readyState
来确认文档是否已经加载完。<col>、<colgroup>、<frameset>、<head>、<html>、<style>、<table>、<tbody>、<thead>、<tfoot>和<tr>
。此外,在IE8及更早版本中,<title>
元素也没有innerHTML属性。scrollIntoView()
方法:能够在全部HTML元素上调用。传入true或不传参数时,该元素会经过滚动尽可能出如今窗口顶部,传入false时,该元素会经过滚动尽可能出如今窗口底部。ancestorElement.contains(childElement);
支持contains()
方法的浏览器有IE、Firefox9+、Safari、Opera和Chrome。使用DOMLevel3的compareDocumentPosition()
也可以肯定节点间的关系。支持这个方法的浏览器有IE9+、Firefox、Safari、Opera9.5+和Chrome。如前所述,这个方法用于肯定两个节点间的关系,返回一个表示该关系的位掩码(bitmask)。下表列出了这个位掩码的值。
掩码 | 节点关系 |
---|---|
1 | 无关(给定的节点不在当前文档中) |
2 | 居前(给定的节点在DOM树中位于参考节点以前) |
4 | 居后(给定的节点在DOM树中位于参考节点以后) |
8 | 包含(给定的节点是参考节点的祖先) |
16 | 被包含(给定的节点是参考节点的后代) |
<div> -> <body> -> <html> -> document
;document -> <html> -> <body> -> <div>
;<div>
元素会按照下图所示顺序触发事件。<div>
元素)在捕获阶段不会接收到事件。这意味着在捕获阶段,事件从document到<html>
再到<body>
后就中止了。下一个阶段是“处于目标”阶段,因而事件在<div>
上发生,并在事件处理(后面将会讨论这个概念)中被当作冒泡阶段的一部分。而后,冒泡阶段发生,事件又传播回文档。var btn = document.getElementById("myBtn"); btn.onclick = function(){ alert(" Clicked"); };
onclick
事件设置为null
。全部DOM节点中都包含这两个方法,而且它们都接受3个参数:要处理的事件名、做为事件处理程序的函数和一个布尔值。最后这个布尔值参数若是是true,表示在捕获阶段调用事件处理程序;若是是false,表示在冒泡阶段调用事件处理程序。例如:
var btn = document. getElementById(" myBtn"); btn. addEventListener(" click", function(){ alert( this. id); }, false);
经过addEventListener()添加的事件处理程序只能使用removeEventListener()来移除;移除时传入的参数与添加处理程序时使用的参数相同。这也意味着经过addEventListener()添加的匿名函数将没法移除。
大多数状况下,都是将事件处理程序添加到事件流的冒泡阶段,这样能够最大限度地兼容各类浏览器。最好只在须要在事件到达目标以前截获它的时候将事件处理程序添加到捕获阶段。若是不是特别须要,咱们不建议在事件捕获阶段注册事件处理程序。
bubbles, cancelBubble, cancelable, composed, currentTarget, defaultPrevented, eventPhase, target, timeStamp, type, isTrusted, initEvent(), preventDefault(), stopImmediatePropagation(), stopPropagation()
,详细使用查询:MDN web docs - Event。DOM事件类型很是多,使用时查询:MDN web docs - 事件类型一览表
除了DOM事件,还有HTML5事件、设备事件、触摸与手势事件。须要用到时再查询。
UIEvents, MouseEvents, MutationEvents, HTMLEvents
,具体使用时查询MDN web docs - Event为了在拖放操做时实现数据交换,IE5引入了dataTransfer对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据。由于它是事件对象的属性,因此只能在拖放事件的事件处理程序中访问dataTransfer对象。在事件处理程序中,可使用这个对象的属性和方法来完善拖放功能。目前,HTML5规范草案也收入了dataTransfer对象。
dataTransfer对象有两个主要方法:getData()和setData()。不难想象,getData()能够取得由setData()保存的值。setData()方法的第一个参数,也是getData()方法惟一的一个参数,是一个字符串,表示保存的数据类型,取值为"text"或"URL"。
IE只定义了"text"和"URL"两种有效的数据类型,而HTML5则对此加以扩展,容许指定各类MIME类型。考虑到向后兼容,HTML5也支持"text"和"URL",但这两种类型会被映射为"text/plain"和"text/uri-list"。
在拖动文本框中的文本时,浏览器会调用setData()方法,将拖动的文本以"text"格式保存在dataTransfer对象中。相似地,在拖放连接或图像时,会调用setData()方法并保存URL。而后,在这些元素被拖放到放置目标时,就能够经过getData()读到这些数据。固然,做为开发人员,你也能够在dragstart事件处理程序中调用setData(),手工保存本身要传输的数据,以便未来使用。
将数据保存为文本和保存为URL是有区别的。若是将数据保存为文本格式,那么数据不会获得任何特殊处理。而若是将数据保存为URL,浏览器会将其当成网页中的连接。换句话说,若是你把它放置到另外一个浏览器窗口中,浏览器就会打开该URL。
Firefox在其第5个版本以前不能正确地将"url"和"text"映射为"text/uri-list"和"text/plain"。可是却能把"Text"(T大写)映射为"text/plain"。为了更好地在跨浏览器的状况下从dataTransfer对象取得数据,最好在取得URL数据时检测两个值,而在取得文本数据时使用"Text"。
要使用dropEffect属性,必须在ondragenter事件处理程序中针对放置目标来设置它。
必须在ondragstart事件处理程序中设置effectAllowed属性。
HTML5 新增了两个与媒体相关的标签,让开发人员没必要依赖任何插件就能在网页中嵌入跨浏览器的音频和视频内容。这两个标签就是<audio>
和<video>
。
这两个标签除了能让开发人员方便地嵌入媒体文件以外,都提供了用于实现经常使用功能的JavaScriptAPI,容许为媒体建立自定义的控件。这两个元素的用法以下。
<!--嵌入视频--> <video src="conference.mpg" id="myVideo">Video player not available.</video> <!--嵌入音频--> <audio src="song.mp3" id="myAudio">Audio player not available.</audio>
使用这两个元素时,至少要在标签中包含src属性,指向要加载的媒体文件。还能够设置width和height属性以指定视频播放器的大小,而为poster属性指定图像的URI能够在加载视频内容期间显示一幅图像。另外,若是标签中有controls属性,则意味着浏览器应该显示UI控件,以便用户直接操做媒体。位于开始和结束标签之间的任何内容都将做为后备内容,在浏览器不支持这两个媒体元素的状况下显示。
因为这一部份内容较多,在此仅粗略介绍,须要时再查询
<audio>
和<video>
标签的使用方法。(或者有空另开一篇博客介绍)
历史状态管理是现代Web应用开发中的一个难点。在现代Web应用中,用户的每次操做不必定会打开一个全新的页面,所以“后退”和“前进”按钮也就失去了做用,致使用户很难在不一样状态间切换。要解决这个问题,首选使用hashchange事件(第13章曾讨论过)。HTML5经过更新history对象为管理历史状态提供了方便。
经过hashchange事件,能够知道URL的参数何时发生了变化,即何时该有所反应。而经过状态管理API,可以在不加载新页面的状况下改变浏览器的URL。为此,须要使用history.pushState()方法,该方法能够接收三个参数:状态对象、新状态的标题和可选的相对URL。例如:
history.pushState({name:" Nicholas"}, "Nicholas' page", "nicholas.html");
其余相关方法和属性有:window.popstate(), history.replaceState(), event.state
在使用HTML5的状态管理机制时,请确保使用pushState()创造的每个“假”URL,在Web服务器上都有一个真的、实际存在的URL与之对应。不然,单击“刷新”按钮会致使404错误。
try - catch:若是try块中的任何代码发生了错误,就会当即退出代码执行过程,而后接着执行catch块。此时,catch块会接收到一个包含错误信息的对象。与在其余语言中不一样的是,即便你不想使用这个错误对象,也要给它起个名字。这个对象中包含的实际信息会因浏览器而异,但共同的是有一个保存着错误消息的message属性。ECMA-262还规定了一个保存错误类型的name属性;当前全部浏览器都支持这个属性(Opera9以前的版本不支持这个属性)。所以,在发生错误时,就能够就能够像下面这样实事求是地显示浏览器给出的消息。
try { window.someNonexistentFunction(); } catch (error){ alert(error.message); }
finally子句:不管如何也会执行,甚至不受return影响。
利用不一样的错误类型,能够获悉更多有关异常的信息,从而有助于对错误做出恰当的处理。要想知道错误的类型,能够在try-catch语句的catch语句中使用instanceof操做符。
合理使用try-catch:当try-catch语句中发生错误时,浏览器会认为错误已经被处理了,于是不会经过本章前面讨论的机制记录或报告错误。try-catch可以让咱们实现本身的错误处理机制。使用try-catch最适合处理那些咱们没法控制的错误。假设你在使用一个大型JavaScript库中的函数,该函数可能会有意无心地抛出一些错误。因为咱们不能修改这个库的源代码,因此大可将对该函数的调用放在try-catch语句当中,万一有什么错误发生,也好恰当地处理它们。在明明白白地知道本身的代码会发生错误时,再使用try-catch语句就不太合适了。例如,若是传递给函数的参数是字符串而非数值,就会形成函数出错,那么就应该先检查参数的类型,而后再决定如何去作。在这种状况下,不该用使用try-catch语句。
在遇到throw操做符时,代码会当即中止执行。仅当有try-catch语句捕获到被抛出的值时,代码才会继续执行。经过使用内置错误类型,能够更真实地模拟浏览器错误。每种错误类型的构造函数接收一个参数,即实际的错误消息。下面是一个例子。
throw new Error("Something bad happened."); throw new TypeError("What type of variable do you take me for?");
在建立自定义错误消息时最经常使用的错误类型是Error、RangeError、ReferenceError和TypeError。
咱们认为只应该捕获那些你确切地知道该如何处理的错误。捕获错误的目的在于避免浏览器以默认方式处理它们;而抛出错误的目的在于提供错误发生具体缘由的消息。
关于JSON,最重要的是要理解它是一种数据格式,不是一种编程语言。虽然具备相同的语法形式,但JSON并不从属于JavaScript。并且,并非只有JavaScript才使用JSON,毕竟JSON只是一种数据格式。不少编程语言都有针对JSON的解析器和序列化器。
JSON不支持变量、函数或对象实例,它就是一种表示结构化数据的格式,虽然与JavaScript中表示数据的某些语法相同,但它并不局限于JavaScript的范畴。
一个JSON对象的示例: { "name": "Nicholas", "age": 29 }
与JavaScript不一样,JSON中对象的属性名任什么时候候都必须加双引号。手工编写JSON时,忘了给对象属性名加双引号或者把双引号写成单引号都是常见的错误。
JSON数组使用[]
表示。
stringify()
和parse()
。在最简单的状况下,这两个方法分别用于把JavaScript对象序列化为JSON字符串和把JSON字符串解析为原生JavaScript值。JSON.stringify()
输出的JSON字符串不包含任何空格字符或缩进。JSON.stringify()
除了要序列化的JavaScript对象外,还能够接收另外两个参数,这两个参数用于指定以不一样的方式序列化JavaScript对象。第一个参数是个过滤器,能够是一个数组,也能够是一个函数;第二个参数是一个选项,表示是否在JSON字符串中保留缩进。单独或组合使用这两个参数,能够更全面深刻地控制JSON的序列化。
JSON.stringify()
的结果中将只包含数组中列出的属性。JSON.stringify()
方法的第三个参数用于控制结果中的缩进和空白符。若是这个参数是一个数值,那它表示的是每一个级别缩进的空格数。最大缩进空格数为10,全部大于10的值都会自动转换为10。只要传入有效的控制缩进的参数值,结果字符串就会包含换行符。 例如,要在每一个级别缩进4个空格,能够这样写代码:
var jsonText = JSON.stringify(book, null, 4); // 获得结果以下 { "title": "Professional JavaScript", "authors": [ "Nicholas C. Zakas" ], "edition": 3, "year": 2011 }
若是缩进参数是一个字符串而非数值,则这个字符串将在JSON字符串中被用做缩进字符(再也不使用空格)。在使用字符串的状况下,能够将缩进字符设置为制表符,或者两个短划线之类的任意字符。
toJSON()
能够做为函数过滤器的补充,所以理解序列化的内部顺序十分重要。假设把一个对象传入JSON.stringify()
,序列化该对象的顺序以下。
toJSON()
方法并且能经过它取得有效的值,则调用该方法。不然,按默认顺序执行序列化。不管是考虑定义toJSON()
方法,仍是考虑使用函数过滤器,亦或须要同时使用二者,理解这个顺序都是相当重要的。
JSON.parse()方法也能够接收另外一个参数,该参数是一个函数,将在每一个键值对儿上调用。在将日期字符串转换为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; } }); alert(bookCopy.releaseDate.getFullYear()); // bookCopy.releaseDate被解析为Date类型的对象,所以可以调用getFullYear()方法
AJAX:经过XMLHttpRequest(XHR)对象以异步方式与服务器进行通讯,意味着无需刷新页面便可取得新数据。
实践中使用jQuery的ajax函数较多,此笔记为了解背后的机制。
IE7+、Firefox、Opera、Chrome和Safari都支持原生的XHR对象,在这些浏览器中建立XHR对象要像下面这样使用XMLHttpRequest构造函数。
var xhr = new XMLHttpRequest();
在使用XHR对象时,要调用的第一个方法是open()
,它接受3个参数:要发送的请求的类型("get"、"post"等)、请求的URL(相对路径或绝对路径)和表示是否异步发送请求的布尔值。
xhr.open("get", "example.php", false);
只能向同一个域中使用相同端口和协议的URL发送请求。若是URL与启动请求的页面有任何差异,都会引起安全错误。需使用CORS。
xhr. open("get", "example.txt", false); xhr.send(null);
这里的send()
方法接收一个参数,即要做为请求主体(body)发送的数据。若是不须要经过请求主体发送数据,则必须传入null,由于这个参数对有些浏览器来讲是必需的。调用send()
以后,请求就会被分派到服务器。
异步发送请求时,能够经过readestatechange事件来监听readyState属性的变化,从而确认是否收到响应。当readyState为4的时候,说明已经接收到所有响应数据,并且已经能够在客户端使用了。
open()
以后,send()
以前使用SetRequestHeader()
方法。获取响应头部信息,使用getResponseHeader()
。
open()
函数的URL必须使用encodeURIComponent()
进行编码。下面是一个添加参数的辅助函数示例:
function addURLParam(url, name, value) { url += (url.indexOf("?") == -1 ? "?" : "&"); url += encodeURIComponent(name) + "=" + encodeURIComponent(value); return url; }
application/x-www-form-urlencoded
。因为表单序列化很是经常使用,XMLHTTPRequest2级定义了FormData类型,为序列化提供了便利。其用法一目了然。
var data = new FormData(); data.append("name", "Nicholas");
建立后的FormData实例能够直接传给XHR的send()方法。
overrideMimeType()
方法能够重写响应的MIME类型,从而更加恰当地处理响应。<img>
标签,实现浏览器到服务器的单向通讯。<script>
标签请求加载一个脚本,服务器返回JS函数调用形式的数据。更多请看说说JSON和JSONP。