<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。