簡介
直接寫入 HTML 輸出流:
document.write("<h1>這是一個標題</h1>");
document.write("<p>這是一個段落。</p>");
注:只能在 HTML 輸出中使用 document.write,如果在文檔加載后使用該方法,會覆蓋整個文檔。
對事件的反應:
<button type="button" onclick="alert('歡迎!')">點我!</button>
注:alert() 函數對於代碼測試非常方便,onclick 事件只是眾多事件之一。
改變 HTML 內容:
x=document.getElementById("demo"); //查找元素
x.innerHTML="Hello JavaScript"; //改變內容
注:document.getElementById("some id")這個方法是 HTML DOM 中定義的,DOM (Document Object Model)(文檔對象模型)是用於訪問 HTML 元素的正式 W3C 標准。
改變 HTML 圖像:
動態地改變 HTML <image> 的來源(src):
<script>
function changeImage()
{
var s = document.getElementById('myimage');
s.src = s.src.match('bulboff')?"/images/pic_bulbon.gif":"/images/pic_bulboff.gif";
}
</script>
<img loading="lazy" id="myimage" onclick="changeImage()" src="/images/pic_bulboff.gif" width="100" height="180">
改變 HTML 樣式:
x=document.getElementById("demo") //找到元素
x.style.color="#ff0000"; //改變樣式
驗證輸入:
if isNaN(x) { alert("不是數字"); }
用法
標簽:在 HTML 頁面中插入 JavaScript,請使用 <script> 標簽。
外部的 JavaScript:外部文件通常包含被多個網頁使用的代碼,文件擴展名是 .js,請在 <script> 標簽的 "src" 屬性中設置該 .js 文件。
可以在 HTML 文檔中放入不限數量的腳本,腳本可位於 HTML 的 <body> 或 <head> 部分中,或者同時存在於兩個部分中。
通常的做法是把函數放入 部分中,或者放在頁面底部。
輸出
JavaScript 可以通過不同的方式來輸出數據:
- 使用 window.alert() 彈出警告框。
- 使用 document.write() 方法將內容寫到 HTML 文檔中。
- 使用 innerHTML 寫入到 HTML 元素。
- 使用 console.log() 寫入到瀏覽器的控制台。
語法
JavaScript 是一個腳本語言,輕量級,但功能強大的編程語言。
字面量
在編程語言中,一般固定值稱為字面量,如 3.14。
- 數字(Number)字面量 可以是整數或者是小數,或者是科學計數(e),如 123e5 。
- 字符串(String)字面量 可以使用單引號或雙引號:
"John Doe"
'John Doe'
-
表達式字面量 用於計算,如5 + 6 、5 * 10 。
-
數組(Array)字面量 定義一個數組:
[40, 100, 1, 5, 25, 10] 。
- 對象(Object)字面量 定義一個對象:
{firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"}
- 函數(Function)字面量 定義一個函數:
function myFunction(a, b) { return a * b;}
變量
JavaScript 使用關鍵字 var 來定義變量, 使用等號來為變量賦值:
var x, length;
x = 5;
length = 6;
注:變量是一個名稱,字面量是一個值。變量通常是可變的,字面量是一個恆定的值。
操作符
JavaScript語言有多種類型的運算符:
類型 | 實例 | 描述 |
---|---|---|
賦值,算術和位運算符 | = + - * / | 在 JS 運算符中描述 |
條件,比較及邏輯運算符 | == != < > | 在 JS 比較運算符中描述 |
語句
在 HTML 中,JavaScript 語句向瀏覽器發出的命令,語句是用分號分隔:
x = 5 + 6;
y = x * 10;
關鍵字
JavaScript 關鍵字用於標識要執行的操作,和其他任何編程語言一樣,JavaScript 保留了一些關鍵字為自己所用。
注:基本上,編程通用的關鍵字盡量不要使用即可。
注釋
不是所有的 JavaScript 語句都是"命令",注釋后的內容將會被瀏覽器忽略。
單行注釋以 // 開頭,多行注釋以 /* 開始,以 */ 結尾。
數據類型
JavaScript 有多種數據類型:數字,字符串,數組,對象等等:
var length = 16; // Number 通過數字字面量賦值
var points = x * 10; // Number 通過表達式字面量賦值
var lastName = "Johnson"; // String 通過字符串字面量賦值
var cars = ["Saab", "Volvo", "BMW"]; // Array 通過數組字面量賦值
var person = {firstName:"John", lastName:"Doe"}; // Object 通過對象字面量賦值
函數
JavaScript 語句可以寫在函數內,函數可以重復引用,引用一個函數 = 調用函數(執行函數內的語句):
function myFunction(a, b) {
return a * b; // 返回 a 乘以 b 的結果
}
注:JavaScript 對大小寫是敏感的,使用 Unicode 字符集。
函數表達式
JavaScript 函數可以通過一個表達式定義:
var x = function (a, b) {return a * b};
var z = x(4, 3);
以上函數實際上是一個 匿名函數 (函數沒有名稱),函數存儲在變量中,不需要函數名稱,通常通過變量名來調用。
Function() 構造函數
函數可以通過內置的 JavaScript 函數構造器(Function())定義:
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);
注:在 JavaScript 中,需要避免使用 new 關鍵字,建議使用函數表達式替換函數構造器(Function())。
自調用函數
函數表達式可以 "自調用",自調用表達式會自動調用,如果表達式后面緊跟 () ,則會自動調用:
(function () {
var x = "Hello!!"; // 我將調用自己
})();
箭頭函數
箭頭函數表達式的語法比普通函數表達式更簡潔:
(參數1, 參數2, …, 參數N) => { 函數聲明 }
(參數1, 參數2, …, 參數N) => 表達式(單一)
// 相當於:(參數1, 參數2, …, 參數N) =>{ return 表達式; }
(單一參數) => {函數聲明}
單一參數 => {函數聲明}
() => {函數聲明}
// ES5
var x = function(x, y) {
return x * y;
}
// ES6
const x = (x, y) => x * y;
注:當使用箭頭函數的時候,箭頭函數會默認綁定外層 this 的值,所以在箭頭函數中 this 的值和外層的 this 是一樣的。
arguments 對象
JavaScript 函數有個內置的對象 arguments 對象,argument 對象包含了函數調用的參數數組。
創建一個函數用來統計所有數值的和:
x = sumAll(1, 123, 500, 115, 44, 88);
function sumAll() {
var i, sum = 0;
for (i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
使用構造函數調用函數
如果函數調用前使用了 new 關鍵字, 則是調用了構造函數:
// 構造函數:
function myFunction(arg1, arg2) {
this.firstName = arg1;
this.lastName = arg2;
}
// This creates a new object
var x = new myFunction("John","Doe");
x.firstName; // 返回 "John"
注:構造函數中 this 關鍵字沒有任何的值,this 的值在函數調用實例化對象(new object)時創建。
作為函數方法調用函數
在 JavaScript 中, 函數是對象,有它的屬性和方法。
call() 和 apply() 是預定義的函數方法, 兩個方法可用於調用函數,兩個方法的第一個參數必須是對象本身:
function myFunction(a, b) {
return a * b;
}
myObject = myFunction.call(myObject, 10, 2); // 返回 20
myArray = [10, 2];
myObject = myFunction.apply(myObject, myArray); // 返回 20
call() 和 apply()兩者的區別在於第二個參數: apply傳入的是一個參數數組,也就是將多個參數組合成為一個數組傳入,而call則作為call的參數傳入(從第二個參數開始)。
注:通過 call() 或 apply() 方法可以設置 this 的值, 且作為已存在對象的新方法調用。
閉包
參考函數自我調用:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// 計數器為 3
變量 add 指定了函數自我調用的返回字值,自我調用函數只執行一次,設置計數器為 0,並返回函數表達式。
add變量可以作為一個函數使用,它可以訪問函數上一層作用域的計數器,這個叫作 JavaScript 閉包。
它使得函數擁有私有變量變成可能,計數器受匿名函數的作用域保護,只能通過 add 方法修改。
注:閉包是一種保護私有變量的機制,在函數執行時形成私有的作用域,保護里面的私有變量不受外界干擾,形成一個不銷毀的棧環境。
對象
JavaScript 對象是擁有屬性和方法的數據。
對象定義
定義和創建 JavaScript 對象:
var person = {
//屬性
firstName: "John",
lastName : "Doe",
id : 5566,
//方法
fullName : function()
{
return this.firstName + " " + this.lastName;
}
對象屬性
可以說 "JavaScript 對象是變量的容器",或者說"JavaScript 對象是鍵值對的容器"。
鍵值對通常寫法為 name : value (鍵與值以冒號分割),鍵值對在 JavaScript 對象通常稱為 對象屬性。
可以通過兩種方式訪問對象屬性:
//方法1
person.lastName;
//方法2
person["lastName"];
對象方法
對象的方法定義了一個函數,並作為對象的屬性存儲。
對象方法通過添加 () 調用 (作為一個函數):
name = person.fullName();
如果把函數當成屬性訪問(不添加()),它將作為一個定義函數的字符串返回:
name = person.fullName;
作用域
在 JavaScript 中, 作用域為可訪問變量,對象,函數的集合。
局部作用域
變量在函數內聲明,變量為局部作用域,只能在函數內部訪問:
// 此處不能調用 carName 變量
function myFunction() {
var carName = "Volvo";
// 函數內可調用 carName 變量
}
不同的函數可以使用相同名稱的變量,局部變量在函數開始執行時創建,函數執行完后局部變量會自動銷毀。
注:函數參數只在函數內起作用,是局部變量。
全局變量
變量在函數外定義,即為全局變量,全局變量有全局作用域—— 網頁中所有腳本和函數均可使用:
var carName = " Volvo";
// 此處可調用 carName 變量
function myFunction() {
// 函數內可調用 carName 變量
}
如果變量在函數內沒有聲明(沒有使用 var 關鍵字),該變量為全局變量:
// 此處可調用 carName 變量
function myFunction() {
carName = "Volvo";
// 此處可調用 carName 變量
}
變量生命周期
JavaScript 變量生命周期在它聲明時初始化,局部變量在函數執行完畢后銷毀,全局變量在頁面關閉后銷毀。
HTML 中的全局變量
在 HTML 中, 全局變量是 window 對象:所有數據變量都屬於 window 對象。
//此處可使用 window.carName
function myFunction() {
carName = "Volvo";
}
事件
HTML 事件是發生在 HTML 元素上的事情, JavaScript 可以觸發這些事件。
HTML 事件
HTML 事件可以是瀏覽器行為,也可以是用戶行為,以下是 HTML 事件的實例:
- HTML 頁面完成加載
- HTML input 字段改變時
- HTML 按鈕被點擊
HTML 元素中可以添加事件屬性,使用 JavaScript 代碼來添加 HTML 元素:
<!-- 單引號 -->
<some-HTML-element some-event='JavaScript 代碼'>
<!-- 雙引號 -->
<some-HTML-element some-event="JavaScript 代碼">
常見的HTML事件
下面是一些常見的HTML事件的列表:
事件 | 描述 |
---|---|
onchange | HTML 元素改變 |
onclick | 用戶點擊 HTML 元素 |
onmouseover | 用戶在一個HTML元素上移動鼠標 |
onmouseout | 用戶從一個HTML元素上移開鼠標 |
onkeydown | 用戶按下鍵盤按鍵 |
onload | 瀏覽器已完成頁面的加載 |
更多事件列表: JavaScript 參考手冊 - HTML DOM 事件
typeof, null, 和 undefined
typeof 操作符
可以使用 typeof 操作符來檢測變量的數據類型:
typeof "John" // 返回 string
typeof 3.14 // 返回 number
typeof false // 返回 boolean
typeof [1,2,3,4] // 返回 object
typeof {name:'John', age:34} // 返回 object
*注:在JavaScript中,數組是一種特殊的對象類型,因此 typeof [1,2,3,4] 返回 object。 *
null
null是一個只有一個值的特殊類型,表示一個空對象引用(用 typeof 檢測 null 返回是object),可以設置為 null 來清空對象:
var person = null; // 值為 null(空), 但類型為對象
也可以設置為 undefined 來清空對象:
var person = undefined; // 值為 undefined, 類型為 undefined
undefined
在 JavaScript 中, undefined 是一個沒有設置值的變量,typeof 一個沒有值的變量會返回 undefined:
var person; // 值為 undefined(空), 類型是undefined
任何變量都可以通過設置值為 undefined 來清空, 類型為 undefined:
person = undefined; // 值為 undefined, 類型是undefined
undefined 和 null 的區別
null 和 undefined 的值相等,但類型不等:
typeof undefined // undefined
typeof null // object
null === undefined // false
null == undefined // true
類型轉換
數據類型
在 JavaScript 中有 6 種不同的數據類型:
- string
- number
- boolean
- object
- function
- symbol
3 種對象類型:
- Object
- Date
- Array
2 個不包含任何值的數據類型:
- null
- undefined
typeof 操作符
可以使用 typeof 操作符來查看 JavaScript 變量的數據類型:
typeof "John" // 返回 string
typeof 3.14 // 返回 number
typeof NaN // 返回 number
typeof false // 返回 boolean
typeof [1,2,3,4] // 返回 object
typeof {name:'John', age:34} // 返回 object
typeof new Date() // 返回 object
typeof function () {} // 返回 function
typeof myCar // 返回 undefined (如果 myCar 沒有聲明)
typeof null // 返回 object
constructor 屬性
constructor 屬性返回所有 JavaScript 變量的構造函數:
"John".constructor // 返回函數 String() { [native code] }
(3.14).constructor // 返回函數 Number() { [native code] }
false.constructor // 返回函數 Boolean() { [native code] }
[1,2,3,4].constructor // 返回函數 Array() { [native code] }
{name:'John', age:34}.constructor // 返回函數 Object() { [native code] }
new Date().constructor // 返回函數 Date() { [native code] }
function () {}.constructor // 返回函數 Function(){ [native code] }
可以使用 constructor 屬性來查看對象是否為數組 (包含字符串 "Array")、日期 (包含字符串 "Date"):
function isArray(myArray) {
return myArray.constructor.toString().indexOf("Array") > -1;
}
function isDate(myDate) {
return myDate.constructor.toString().indexOf("Date") > -1;
}
JavaScript 類型轉換
- 全局方法 String():可以將數字、字母、變量、表達式、布爾值、日期轉換為字符串
- Number 方法 toString():可以將數字轉換為字符串。
- Boolean 方法 toString():可以將布爾值轉換為字符串,"false"、"true"。
- Date 方法 toString():可以將日期對象轉換為字符串,返回 “Thu Jul 17 2014 15:38:19 GMT+0200 (W. Europe Daylight Time)”。
- 全局方法 Number():可以將字符串、布爾值、日期轉換為數字,空字符串轉換為 0,其他的字符串會轉換為 NaN (不是個數字),布爾值對應0和1,日期返回 1404568027739(毫秒數)。
- 一元運算符 +:Operator + 可用於將變量轉換為數字,如果變量不能轉換,則值為 NaN (不是一個數字)。
- **日期方法 getTime() **:可將日期轉換為數字,返回 1404568027739(毫秒數)。
正則表達式
語法
/正則表達式主體/修飾符(可選)
其中修飾符是可選的:
var patt = /runoob/i
- /runoob/i 是一個正則表達式。
- runoob 是一個正則表達式主體 (用於檢索)。
- i 是一個修飾符 (搜索不區分大小寫)。
使用字符串方法
在 JavaScript 中,正則表達式通常用於兩個字符串方法 : search() 和 replace()。
- search() 方法:用於檢索字符串中指定的子字符串,或檢索與正則表達式相匹配的子字符串,並返回子串的起始位置。
var str = "Visit Runoob!";
var n = str.search(/Runoob/i); //不區分大小
var n = str.search("Runoob");
- replace() 方法:用於在字符串中用一些字符替換另一些字符,或替換一個與正則表達式匹配的子串。
var str = document.getElementById("demo").innerHTML;
var txt = str.replace(/microsoft/i,"Runoob"); //不區分大小
var txt = str.replace("Microsoft","Runoob");
使用 RegExp 對象
在 JavaScript 中,RegExp 對象是一個預定義了屬性和方法的正則表達式對象。
- test() 方法:用於檢測一個字符串是否匹配某個模式,如果字符串中含有匹配的文本,則返回 true,否則返回 false。
var patt = /e/;
patt.test("The best things in life are free!");
//以上兩行代碼可以合並為一行:
/e/.test("The best things in life are free!");
- **exec() 方法:用於檢索字符串中的正則表達式的匹配,該函數返回一個存放匹配結果的數組,未找到匹配則返回值為 null。
/e/.exec("The best things in life are free!");
表單
表單驗證
判斷表單字段(fname)值是否存在, 如果不存在,就彈出信息,阻止表單提交:
function validateForm() {
var x = document.forms["myForm"]["fname"].value;
if (x == null || x == "") {
alert("需要輸入名字。");
return false;
}
}
<form name="myForm" action="demo_form.php" onsubmit="return validateForm()" method="post">
名字: <input type="text" name="fname">
<input type="submit" value="提交">
</form>
多表單使用同一驗證函數可以使用以下方法:
function validateForm(form) {
var x = form.name.value;
if (x == null || x == "") {
alert("輸入不能為空!");
return false;
}
}
所有表單調用時都使用:
onsubmit="return validateForm(this)"
onsubmit="validateForm()" 能夠調用 validateForm() 對表單進行驗證,但是在驗證不通過的情況下,並不能阻止表單提交。
onsubmit="return validateForm()" 當驗證不通過時,返回 false,可以阻止表單提交。
onsubmit="return validateForm()" 相當於復寫了 onsubmit 的默認方法(默認返回 true),根據 validateForm() 的結果返回 true 或 false,當驗證不通過時,返回 false,onsubmit="return false;" 阻止表單提交。
this 的多種指向:
- 在對象方法中, this 指向調用它所在方法的對象。
- 單獨使用 this,它指向全局(Global)對象。
- 函數使用中,this 指向函數的所屬者。
- 嚴格模式下函數是沒有綁定到 this 上,這時候 this 是 undefined。
- 在 HTML 事件句柄中,this 指向了接收事件的 HTML 元素。
- apply 和 call 允許切換函數執行的上下文環境(context),即 this 綁定的對象,可以將 this 引用到任何對象。
let 和 const
let關鍵字
塊級作用域(Block Scope)
使用 var 關鍵字聲明的變量不具備塊級作用域的特性,它在 {} 外依然能被訪問到:
{
var x = 2;
}
// 這里可以使用 x 變量
let 聲明的變量只在 let 命令所在的代碼塊 {} 內有效,在 {} 之外不能訪問:
{
let x = 2;
}
// 這里不能使用 x 變量
重新定義變量
使用 var 關鍵字重新聲明變量可能會帶來問題,在塊中重新聲明變量也會重新聲明塊外的變量:
var x = 10;
// 這里輸出 x 為 10
{
var x = 2;
// 這里輸出 x 為 2
}
// 這里輸出 x 為 2
let 關鍵字可以解決這個問題,它只在 let 命令所在的代碼塊 {} 內有效:
var x = 10;
// 這里輸出 x 為 10
{
let x = 2;
// 這里輸出 x 為 2
}
// 這里輸出 x 為 10
使用全局變量
使用 var 關鍵字聲明的全局作用域變量屬於 window 對象:
var carName = "Volvo";
// 可以使用 window.carName 訪問變量
使用 let 關鍵字聲明的全局作用域變量不屬於 window 對象:
let carName = "Volvo";
// 不能使用 window.carName 訪問變量
重置變量
- 使用 var 關鍵字聲明的變量在任何地方都可以修改
- 在相同的作用域或塊級作用域中,不能使用 let 關鍵字來重置 var 關鍵字聲明的變量
- 在相同的作用域或塊級作用域中,不能使用 let 關鍵字來重置 let 關鍵字聲明的變量
- 在相同的作用域或塊級作用域中,不能使用 var 關鍵字來重置 let 關鍵字聲明的變量
- let 關鍵字在不同作用域,或不同塊級作用域中是可以重新聲明賦值的
const 關鍵字
const 用於聲明一個或多個常量,聲明時必須進行初始化,且初始化后值不可再修改。
const定義常量與使用let 定義的變量相似:
- 二者都是塊級作用域
- 都不能和它所在作用域內的其他變量或函數擁有相同的名稱
兩者還有以下兩點區別:
- const聲明的常量必須初始化,而let聲明的變量不用
- const 定義常量的值不能通過再賦值修改,也不能再次聲明。而 let 定義的變量值可以修改。
注:const 定義的變量並非不可改變,比如使用const聲明對象,可以改變對象值。可以使用Object.freeze()方法來 凍結變量 ,被凍結后的對象只能進行讀操作。
重置變量
- 使用 var 關鍵字聲明的變量在任何地方都可以修改
- 在相同的作用域或塊級作用域中,不能使用 const 關鍵字來重置 var 和 let關鍵字聲明的變量
- 在相同的作用域或塊級作用域中,不能使用 const 關鍵字來重置 const 關鍵字聲明的變量
- const 關鍵字在不同作用域,或不同塊級作用域中是可以重新聲明賦值的
JSON
JSON 是用於存儲和傳輸數據的格式,通常用於服務端向網頁傳遞數據 。
語法規則
- 數據為 鍵/值 對
- 數據由逗號分隔
- 大括號保存對象
- 方括號保存數組
{"sites":[
{"name":"Runoob", "url":"www.runoob.com"},
{"name":"Google", "url":"www.google.com"},
{"name":"Taobao", "url":"www.taobao.com"}
]}
JSON 字符串和 JavaScript 對象互轉
要實現從JSON字符串轉換為JS對象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //結果是 {a: 'Hello', b: 'World'} 一個對象
要實現從JS對象轉換為JSON字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'}); //結果是 '{"a": "Hello", "b": "World"}' 一個JSON格式的字符串
void(0) 含義
javascript:void(0) 中最關鍵的是 void 關鍵字, void 是 JavaScript 中非常重要的關鍵字,該操作符指定要計算一個表達式但是不返回值。
void(alert("Warnning!"))
href="#"與href="javascript:void(0)"的區別:
-
包含了一個位置信息,默認的錨是#top 也就是網頁的上端。
- javascript:void(0), 僅僅表示一個死鏈接。
在頁面很長的時候會使用 # 來定位頁面的具體位置,格式為:# + id,如果要定義一個死鏈接請使用 javascript:void(0):
<a href="javascript:void(0);">點我沒有反應的!</a>
<a href="#pos">點我定位到指定位置!</a>
<br>
...
<br>
<p id="pos">尾部定位點</p>
異步編程
異步的概念
異步(Asynchronous, async)是與同步(Synchronous, sync)相對的概念,同步按碼順序執行,異步不按照代碼順序執行,異步的執行效率更高。
通俗地解釋一下異步:異步就是從主線程發射一個子線程來完成任務。
什么時候用異步編程
主線程作為一個線程,不能夠同時接受多方面的請求。當一個事件沒有結束時,界面將無法處理其他請求。
現在有一個按鈕,如果設置它的 onclick 事件為一個死循環,那么當這個按鈕按下,整個網頁將失去響應。
子線程有一個局限:一旦發射了以后就會與主線程失去同步,無法確定它的結束。
為了解決這個問題,JavaScript 中的異步操作函數往往通過回調函數來實現異步任務的結果處理。
回調函數
回調函數就是一個函數,在啟動一個異步任務的時候就告訴它:等完成了這個任務之后要干什么。
function print() {
document.getElementById("demo").innerHTML="RUNOOB!";
}
setTimeout(print, 3000);
這段程序中的 setTimeout 就是一個消耗時間較長(3 秒)的過程,它的第一個參數是個回調函數,第二個參數是毫秒數,這個函數執行之后會產生一個子線程,子線程會等待 3 秒,然后執行回調函數 "print",在命令行輸出 "Time out"。
不必單獨定義一個函數 print ,常常將上面的程序寫成:
setTimeout(function () {
document.getElementById("demo").innerHTML="RUNOOB!";
}, 3000);
異步 AJAX
除了 setTimeout 函數以外,異步回調廣泛應用於 AJAX 編程,參考:https://www.runoob.com/ajax/ajax-tutorial.html
XMLHttpRequest 常常用於請求來自遠程服務器上的 XML 或 JSON 數據,一個標准的 XMLHttpRequest 對象往往包含多個回調:
var xhr = new XMLHttpRequest();
xhr.onload = function () {
// 輸出接收到的文字數據
document.getElementById("demo").innerHTML=xhr.responseText;
}
xhr.onerror = function () {
document.getElementById("demo").innerHTML="請求出錯";
}
// 發送異步 GET 請求
xhr.open("GET", "https://www.runoob.com/try/ajax/ajax_info.txt", true);
xhr.send();
XMLHttpRequest 的 onload 和 onerror 屬性都是函數,分別在它請求成功和請求失敗時被調用。
如果使用完整的 jQuery 庫,也可以更加優雅的使用異步 AJAX:
$.get("https://www.runoob.com/try/ajax/demo_test.php",function(data,status){
alert("數據: " + data + "\n狀態: " + status);
});
Promise
Promise 是一個 ECMAScript 6 提供的類,目的是更加優雅地書寫復雜的異步任務。由於 Promise 是 ES6 新增加的,一些舊的瀏覽器並不支持。
構造 Promise
現在新建一個 Promise 對象:
new Promise(function (resolve, reject) {
// 要做的事情...
});
用 Promise 來實現三次輸出字符串,第一次間隔 1 秒,第二次間隔 4 秒,第三次間隔 3 秒:
new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("First");
resolve();
}, 1000);
}).then(function () {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("Second");
resolve();
}, 4000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});
Promise 的使用
下面通過剖析這段 Promise "計時器" 代碼來講述 Promise 的使用:
Promise 構造函數只有一個參數,是一個函數,這個函數在構造之后會直接被異步運行——稱之為起始函數。
**起始函數包含兩個參數 resolve 和 reject*8,當 Promise 被構造時起始函數會被異步執行:
new Promise(function (resolve, reject) {
console.log("Run");
});
resolve 和 reject 都是函數,其中調用 resolve 代表一切正常,reject 是出現異常時所調用的:
new Promise(function (resolve, reject) {
var a = 0;
var b = 1;
if (b == 0) reject("Divide zero");
else resolve(a / b);
}).then(function (value) {
console.log("a / b = " + value);
}).catch(function (err) {
console.log(err);
}).finally(function () {
console.log("End");
});
Promise 類有 .then() .catch() 和 .finally() 三個方法,這三個方法的參數都是一個函數:
- .then() 可以將參數中的函數添加到當前 Promise 的正常執行序列;
- .catch() 則是設定 Promise 的異常處理序列;
- .finally() 是在 Promise 執行的最后一定會執行的序列。
.then() 傳入的函數會按順序依次執行,有任何異常都會直接跳到 catch 序列:
new Promise(function (resolve, reject) {
console.log(1111);
resolve(2222);
}).then(function (value) {
console.log(value);
return 3333;
}).then(function (value) {
console.log(value);
throw "An error";
}).catch(function (err) {
console.log(err);
});
執行結果:
1111
2222
3333
An error
- resolve() 中可以放置一個參數用於向下一個 then 傳遞一個值,then 中的函數也可以返回一個值傳遞給 then。如果 then 中返回的是一個 Promise 對象,那么下一個 then 將相當於對這個返回的 Promise 進行操作。
- reject() 參數中一般會傳遞一個異常給之后的 catch 函數用於處理異常。
- **resolve 和 reject 的作用域只有起始函數,不包括 then 以及其他序列。
- **resolve 和 reject 並不能夠使起始函數停止運行,別忘了 return。
Promise 函數
可以將上面程序的核心部分寫成一個 Promise 函數:
function print(delay, message) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(message);
resolve();
}, delay);
});
}
然后實現程序功能:
print(1000, "First").then(function () {
return print(4000, "Second");
}).then(function () {
print(3000, "Third");
});
異步函數
異步函數(async function)是 ECMAScript 2017 (ECMA-262) 標准的規范,可以上面實現程序功能的代碼變得更好看:
async function asyncFunc() {
await print(1000, "First");
await print(4000, "Second");
await print(3000, "Third");
}
asyncFunc();
異步函數 async function 中可以使用 await 指令,await 指令后必須跟着一個 Promise,異步函數會在這個 Promise 運行中暫停,直到其運行結束再繼續運行。
異步函數實際上原理與 Promise 原生 API 的機制是一模一樣的,處理異常的機制將用 try-catch 塊實現:
async function asyncFunc() {
try {
await new Promise(function (resolve, reject) {
throw "Some error"; // 或者 reject("Some error")
});
} catch (err) {
console.log(err);
// 會輸出 Some error
}
}
asyncFunc();
如果 Promise 有一個正常的返回值,await 語句也會返回它:
async function asyncFunc() {
let value = await new Promise(
function (resolve, reject) {
resolve("Return value");
}
);
console.log(value);
}
asyncFunc();
輸出:
Return value