JS筆記

tags: web frontend

變數

  • JS 的基本型別(Primitives)有 String(文字)、 Number(數字)、 Boolean(true/false)、 undefined、 null

  • JS 中變數為寬鬆型別,不用事先宣告型別(type)

  • 區分大小寫、不可用保留字、不可用數字開頭

在宣告變數的同時也可以給定初始值,給值的同時也決定了變數的型別(type)

區塊和活動範圍

C語系

  1. block就是區塊,是程式中的一塊獨立段落。 C 式語系中,由 { } 包起的內容就屬於一個 block (區塊),而在區塊之中還可以有小區塊。層層區塊組成巢狀結構。
  2. 伴隨著區塊而來的還有稱為 scope 的變數活動範圍、或稱作用域的觀念。
    程式語言用變數活動範圍劃分各個變數的可用範圍,讓符號名稱可以在不同的活動範圍中繫結不同的變數,也才有現在的區域變數常識。
  3. 在C語言中,一個區塊(block)就等於一個活動範圍(scope)
int main() { int i = 0; { int i = 1; printf("inner scope: %d\n", i); } printf("main scope: %d\n", i); return 0; }

在 main 區塊中,又寫了一對 { } 劃出一個小區塊,亦即一個新的活動範圍。所以我可以在這個小區塊中再宣告一次 i 。

符號 i 在這一大一小兩區塊中,分別繫結兩個不同活動範圍的變數。所以小區塊中令 i 為 1 的敘述,並不會影響到大區塊的 i 值。一前一後的兩行輸出指令,也就分別輸出 1 和 0 兩個結果。

如果你把這個 C 語言範例中的第5行和第8行的角括號拿掉的話,編譯器會直接告訴你重複宣告變數。

JS

var

但是同樣的想法,用在 JavaScript 就錯了。先看看下面的 JavaScript 範例:

{ var i = 0; { var i = 1; console.log("inner block: ", i); } console.log("host block: %d", i); }

前後兩行都顯示 1 ,也就是在小區塊中第二次宣告 i 並賦值為 1 的敘述,其實作用在第一次宣告的 i 身上。
這表示在 JavaScript 中,區塊並不等於活動範圍。一大一小兩區塊的 i 都繫結到同一個變數了。

在 ES6 之前的規範中,對於 scope 的定義只有兩種

  1. 全域活動範圍(global scope)
  2. 函數活動範圍(function scope)

你每定義一個函數,就會建立一個屬於這個函數的活動範圍;不在函數內的資源就屬於全域活動範圍。
因為沒有採用區塊即活動範圍的定義。所以像 C 語言那樣的區塊用法,在 JavaScript 中就是錯的。

ES6 以前,也只有 var 一種變數宣告方式。它的用途和函數活動範圍有關。

  • 在函數內以 var 宣告的變數,僅限函數活動範圍內可用,外部看不到。
  • 而沒有用 var 或是在函數外宣告的變數,就屬於全域範圍了。

var 是看函數,而不是區塊。

let

let 和 var 不同之處在於它帶來了以區塊為活動範圍的定義。
在 ES6 中,你可以在 for 區塊、if 區塊、或者是不帶任何控制目的純區塊中,使用 let 宣告以區塊為活動範圍的變數。
因此若將前述JavaScript 的範例程式碼中以 var 宣告的變數全改為以 let 宣告,就會跑出正確的結果。

  • let 禁止在同一活動範圍中再次宣告相同名稱的變數。
  • var 會無視第二次宣告,只管指派變數值。但 let 視為重複宣告的語法錯誤。
  • let 禁止在宣告變數之前就使用它。
  • 在全域範圍以 let 宣告的變數,不會成為全域個體(global object)的屬性。但以 var 宣告的變數同時也會是全域個體的屬性。因此 let 變數是真正的區域變數,你用 module 或其他方式載入的程式碼看不到那些 let 變數。註: 在瀏覽器中運行的 JavaScript 之全域個體一律是 window 。

let 是看區塊,而不是函數。

const

凡是用 const 定義的符號,其繫結的內容僅能在定義時設定初值,之後不允許再改變,這就是常數了。
試圖改變 const 常數的敘述,都是語法錯誤。除此之外,const 的語法限制和 let 相同,不允許重複宣告、不允許宣告前使用。

const是看區塊,而不是函數

const 常數還有一點要注意,它可以在定義時計算初值。
所以定義時的初值部份不限定為字面內容,而可以使用變數或函數等運算敘述。
若初值部份用了變數或運算敘述, JavaScript 會將計算結果作為初值。
即使你之後改變了那個變數,也不會影響 const 常數的內容。

const MAX1 = 1; let i = 100; const MAX100 = i + 1; // 計算 i+1 之現值後存入,故為 101 i += 20; console.log(MAX100); // 仍為 101

若以 const 宣告物件或陣列,還是有辦法更改裡面的值,
像是

const myObj = {
  url: "http://123.com"
}
myObj.url = "changed";
console.log(myObj.url); // changed

要解決這個問題可以用

Object.freeze(myObj);

例子

function test(){ var outside_var = "outside_var"; let outside_let = "outside_let"; for(let i=0; i<1; i++){ var inner_var = "inner_var"; let inner_let = "inner_let"; // outside_var console.log(outside_var); // outside_let console.log(outside_let); } // inner_var console.log(inner_var); // inner_let is not defined console.log(inner_let); } test();

函數

內建函數

parseInt()、parseFloat():轉換指定型態
isNaN():判斷是否非數字
isFinite():判斷是否是無窮數
.toFixed(3):取到小數第3位

Math

輸出x的m次方

Math.pow(x, m);

產生min~max間的隨機亂數

/*Math.floor:去小數 * 注意min要加在取整數之後 */ var x = Math.floor(Math.random()*(max-min+1))+min;

Date

new Date (year, month, date, hours, minutes, seconds, ms)

var today = new Date(); // Jan 31 00:00:00 1978 // 月份為0~11 var birthDay = new Date(1978, 0, 31); // 取得年份 var day = today.getFullYear(); // 取得月份(0~11) var day = today.getMonth(); // 取得日期 var day = today.getDate(); // 取得星期 var day = today.getDay(); // 可取得詳細時間 // Sun Apr 23 2017 01:29:23 GMT+0800 (台北標準時間) Date(); // 可取得1970年到現在的毫秒數 Date.now(); // 轉成 ISO8601 var dt = new Date(); var tmp = dt.toISOString(); // ISO8601轉毫秒 Date.parse("2017-12-12T18:00:00.000Z"); // ISO8601轉當地時間 var tmpp = new Date(tmp).toLocaleString();

URI 編碼/解碼

統一資源標識符(Uniform Resource Identifier,或URI)
是一個用於標識某一網際網路資源名稱的字元串,
而URL (定義位置) 和 URN (定義身份) 則是URI的子集。

在處理網頁(址)資料,常會有遇到中文字或是空白、標點符號等問題(英文、數字通常不會有問題),
此時就可以使用 URI 編碼函數(通常標點轉為十六進位ASCII、中文轉成十六進位統一字元碼),
如需轉回則使用解碼函數。

encodeURIComponent()不編碼符號包括: ~ ! * ( ) '
適合參數編碼,應用範圍廣

encodeURIComponent():轉碼
decodeURIComponent():解碼

alert / confirm / prompt

//可吃換行\n
alert('text') / confirm('text') 
//若取消則回傳 null
prompt('text','使用者未輸入的預設值')

三者皆會在瀏覽器顯示對話框 但用處各自不同
其中 confirm('text') 和 alert('text') 類似
都會把參數內的字串顯示在對話視窗中
但confirm()有確定和取消 會回傳 truefalse

if(confirm('你想吃飯嗎')){ alert('客人,請進'); console.log('客人,請進'); }else{ alert('掰'); console.log('掰'); }

prompt()則是除了顯示文字外 還可以讓使用者輸入數值

if(prompt('請輸入年齡')>=20){ console.log('你成年可以投票囉~~'); }else{ console.log('孩子再等幾年吧!'); }

自訂函數

定義命名函數

function sum(a,b){ return a+b; }

定義匿名函數

/*函數可以當作變數傳遞*/ var sum=function(a,b){ return a+b; };

中斷函數

迴圈可以用 break、continue 來中斷迴圈 ; 而函數的 return 除了用來回傳值,也可用來中斷函數。
作法為 return 一個 value 或是乾脆回傳空值

function test(str = 'working'){ if(str == 'stop'){ return } console.log(str); } test(); // working test('stop'); // show nothing

條件運算子

if else可以簡寫

// <p>The answer is <span id="ans"></span></p> // var ans = document.getElementById("ans"); var isTrue = (1+1 == 2); var isFalse = (1+1 != 2); ans.innerHTML = (isTrue) ? "Yes" : "No"; // 也可以再串下去 ans.innerHTML = (isFalse) ? "Yes" : (isTrue) ? "No => Yes" : "No => No";

Hoisting

函數

一般而言 JavaScript 有 內建函數 和 自定義函數
在 JS 中 函數在被執行之前會被解析(hoisted)
因此它可以在任意的地方都是有宣告的 即便比這個函式還早呼叫

console.log(sum(1,2)); function sum(a,b){ return a+b; } console.log(sum(1,2));

變數

JavaScript 是 function scope
無論你在函數內哪裡宣告變數 var 都會提升到最前面宣告
稱為變數的拉升(Variable Hoisting),
建議把變數的宣告放在函數的最頂端 否則可能導致混亂的情況。

但是使用 let 或 const 須注意,並不會自動拉升,所以在宣告前使用的話會顯示 is not define

function test(){ /*儘管還沒定義但會回傳 undefinded*/ console.log(a); var a=1; } test();

上面代碼實際執行狀況是

function test(){ /*程式會將function中全部需要宣告的Local Variable提升到function的第一行來執行*/ var a; console.log(a); a=1; } test();

更精確的說 在我們定義變數的過程中
可以分成宣告(declaration)和給值(initialization)的兩個過程
只有declaration的內容會在逐行執行程式前先被執行並儲存在記憶體(hoisted)
給值的內容則是在hoisted後 逐行執行程式時 才會被執行到

所以程式一開始執行的時候 就已經把var a的宣告存在記憶體中了
但是還沒把值指定進去a這個變數當中 這使得a得到了undefined的結果

Scope Chain

用 var 所宣告的變數 作用範圍是在當時所在環境(函數內)
而不使用 var 直接指定值而建立的變數 則是全域物件(window)上的一個屬性
也就全域範圍

在 JS 中有稱作 scope chain(範圍鏈)的特性
JS在查找變數時 會循著範圍鏈(Scope Chain)一層一層往外找
若函數內找不到 則往外找

注意:內層函式都可以存取外部函式的變數;但外部不能存取內部

自調用函數

自調用函數(立即函數)是一種
不用額外呼叫可自己立刻執行
方便建構自己的生存域

(function(name){ var cat=name; document.write(cat); })('momo');

Closure

閉包(Closure)是擁有閒置變數(Free variable)的物件
建立函數不等於建立閉包
如果函數的閒置變數與當時語彙環境綁定
該函數才稱為閉包

閒置變數是指對於函式而言,既非區域變數也非參數的變數

function makeFunc(){ var name = "Mozilla"; function displayName(){ alert(name); } return displayName; } /*closure,理論上 name 在函數執行完就消失 *但由於內部函數 displayName 參考到 name 變數 *所以當 displayName 給定給 myFunc 全域變數時 *生存域突破成全域 記憶了創建函數時的環境變數參考 *所以 name 活下來了 */ var myFunc = makeFunc(); myFunc();
function doSome() { var x = 10; function f(y) { return x + y; } return f; } var foo = doSome(); console.log(foo(20)); /*30*/ console.log(foo(30)); /*40*/

上面doSome的例子中,f建立了一個閉包,如果你單看:

function f(y) {
  return x + y;
}

看來起x似乎沒有定義。實際上,x是從外部函式捕捉而來。
閉包是個捕捉了外部函式變數(或使之繼續存活)的函式。
在上例中,函式f建立了閉包,因為它將變數x關入(close)自己的範圍。
如果形式閉包的函式物件持續存活,被關閉的變數x也會繼續存活。
就像是延續了變數x的生命週期。

由於doSome傳回了函式物件並指定給foo,就doSome而言已經執行完畢。
單看x的話,理應x已結束其生命週期,但由於doSome中建立了閉包並傳回,x被關閉在閉包中,所以x的生命週期就與閉包的生命週期相同了。
如上例所示,呼叫foo(20)結果就是10+20(因為被關閉的x值是10),呼叫foo(30)結果就是10+30。

注意!閉包關閉的是變數,而不是變數所參考的值。

function doOther() { var x = 10; function f(y) { return x + y; } x = 100; return f; } var foo = doOther(); console.log(foo(20)); /*120*/ console.log(foo(30)); /*130*/

建立閉包時,綁定了x變數,而不是數值10(x變數的值),
也因此doOther之後改變了x變數的值,而後傳回閉包給foo參數後,
範例顯示的結果分別是100+20與100+30。

你可能會有疑問的是,如果閉包關閉了某個變數,使得該變數的生命週期得以延長,那麼這個會怎麼樣?

function doOther() { var x = 10; function f() { x--; return x; } return f; } var f1 = doOther(); var f2 = doOther(); console.log(f1()); /*9*/ console.log(f2()); /*9*/

像這類的例子,其實結果是很一致的,關閉的是建立閉包時外部範圍下的變數。
以上例來說,第一次呼叫doOther時,建立了x變數,指定值給x變數,而後建立閉包將之關閉。
第二次呼叫doOther時,建立了x變數,指定值給x變數,而後建立閉包將之關閉。
所以f1與f2關閉的根本是不同作用範圍的x變數(也就是該次呼叫doOther時所建立的x變數)。
所以上例中,呼叫f2之後顯示的值仍是9

下面這個也是個例子

function doSome(x) { return function(a) { return x + a; }; } var f1 = doSome(100); var f2 = doSome(200); console.log(f1(10)); /*110*/ console.log(f2(10)); /*210*/

Closure常見應用

  1. 實現 private
  2. 嵌套 callback 函數非同步處理(Ex. 事件處理)
  3. 非同步處理(Ex. 事件處理)

Closure 模擬 private

使用閉包來定義公共函數,且其可以訪問私有函數和變數。
這個方式也稱為模組模式(module pattern)

/*自調用函數,共用生存域*/ var Counter = (function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } })(); console.log(Counter.value()); /* logs 0 */ Counter.increment(); Counter.increment(); console.log(Counter.value()); /* logs 2 */ Counter.decrement(); console.log(Counter.value()); /* logs 1 */
/*每次產生都創建一個獨立記憶環境*/ var makeCounter = function() { var privateCounter = 0; function changeBy(val) { privateCounter += val; } return { increment: function() { changeBy(1); }, decrement: function() { changeBy(-1); }, value: function() { return privateCounter; } } }; var Counter1 = makeCounter(); var Counter2 = makeCounter(); console.log(Counter1.value()); /* logs 0 */ Counter1.increment(); Counter1.increment(); console.log(Counter1.value()); /* logs 2 */ Counter1.decrement(); console.log(Counter1.value()); /* logs 1 */ console.log(Counter2.value()); /* logs 0 */

嵌套 callback 函數

<a href="#" id="size-10">10</a> <a href="#" id="size-20">20</a> <a href="#" id="size-30">30</a>
function makeSizer(size){ return function(){ document.body.style.fontSize=size+'px'; }; } /*每個函數的創建都會有自己獨立的生存環境*/ var size10 = makeSizer(10); var size20 = makeSizer(20); var size30 = makeSizer(30); document.getElementById('size-10').onclick = size10; document.getElementById('size-20').onclick = size20; document.getElementById('size-30').onclick = size30;

處理非同步問題

for(var i = 0; i < 5; i++){ setTimeout(function () { console.log(i); },1000); } /* 5 * 5 * 5 * 5 * 5 */

上方全部都顯示 5 是因為 JS 在執行 for 時,會把 setTimeout 放進 Queue 中,所以迴圈跑完才會執行 setTimeout ,但此時的 i 是全域變數,也就是跑完迴圈後的 i = 5,所以 console.log(i) 才會顯示 5。
關於 JS 中 Stack、Queue、WebApis 更詳細的說明

可以使用閉包解決這個問題,讓 i 的值與當時的環境綁定:

function printLog(i){ return function(){ console.log(i); } } for(var i = 0; i < 5; i++){ setTimeout(printLog(i),1000); } /* * 0 * 1 * 2 * 3 * 4 */

其實 ES6 之後有更簡單的方法解決這個問題:
改成 let 即可,這也像閉包的概念,因為迴圈執行完後,i 並不是全域變數,所以會綁定當時生成的環境。

for(let i = 0; i < 5; i++){ setTimeout(function () { console.log(i); },1000); } /* * 0 * 1 * 2 * 3 * 4 */

箭頭函數

// 寫法 (參數1, 參數2,, 參數N) => { statements } (param1, param2,, paramN) => expression // expression 等於 { return expression; }, // 若函數內只有 return 值而已,則可省略大括號和 return // 只有一個參數時,括號才能不加 (singleParam) => { statements } singleParam => { statements } // 若無參數,就一定要加括號: () => { statements }
  • param(參數):
    多個參數以()包住,如不需要參數則以 () 表示,
    如果只有一個參數可以省略括弧 (如 foo => 1)。
  • statements or expression:
    當有多個陳述 (statement) 需用 { } 框住,只有一個陳述則不需要;
    其中表達式 (Expression) 最後也會是箭頭函數的返回值。
var sum = function(x, y){ return x + y; }; console.log(sum(1,2));

可以寫成

/* let empty = (參數) => {回傳值}; */ var sum = (x, y) => x + y; console.log(sum(1,2));

JavaScript 函數式程式設計

forEach

const numbers = [1,2,3,4]; numbers.forEach(function(item, index, array){ console.log(item); }); // 1 // 2 // 3 // 4

map 迭代函數

forEach 相似,但會有回傳值

const numbers = [1,2,3,4]; const newNumbers = numbers.map(function(item, index, array){ return item * 2; }); console.log("The doubled numbers are", newNumbers); /* [2,4,6,8] */

可是 map 不適合拿來過濾

const numbers = [1,2,3,4]; const newNumbers = numbers.map(function(item, index, array){ if(item > 1){ return item * 2; } }); console.log("The doubled numbers are", newNumbers); /* [undefined, 4, 6, 8] */

回傳物件

const numbers = [1,2,3,4]; const allPeople = numbers.map(function(item, index, array){ return { money: item * 100 } }); console.log(allPeople); /* [undefined, 4, 6, 8] */

filter 過濾函數

回傳所有結果為 true 的值

// 過濾出所有奇數 const numbers = [1,2,3,4]; const newNumbers = numbers.filter(function(item, index, array){ return(item % 2 !== 0); }) console.log(newNumbers); // [1, 3]

結合 map

const numbers = [1,2,3,4]; const newNumbers = numbers.filter(function(item, index, array){ return(item % 2 !== 0); }) .map(function(item){ return item * 2; }); console.log("The doubled numbers are",newNumbers); /* [2,6] */

find

filter 原理相同,但差別在 find 只會回傳第一個為 true 的結果。

// 過濾出所有奇數 const numbers = [1,2,3,4]; const newNumbers = numbers.find(function(item, index, array){ return(item % 2 !== 0); }) console.log(newNumbers); // 1

every

檢查所有判斷結果,全為 true 則 return true,有一個 false 則 return false。

// 檢查是否所有數字都為偶數 const numbers = [1,2,3,4]; const allNumbersAreEven = numbers.every(function(item, index, array){ return(item % 2 === 0); }) console.log(allNumbersAreEven); // false

some

檢查所有判斷結果,只要有一個 true 則 return true。

// 檢查是否所有數字都為偶數 const numbers = [1,2,3,4]; const someNumbersAreEven = numbers.some(function(item, index, array){ return(item % 2 === 0); }) console.log(someNumbersAreEven); // false

reduce 累計函數

一樣對陣列進行遍歷操作,但會將每回合 return 的值,傳入下一回合,所以可以用來加總。

var numbers = [1,2,3,4]; // pre 為前一個 reduce return 的值, // 但第一回合執行時並沒有前一個回傳值, // 所以在 , 後面加上初始 pre 值 // 而 item 則為當前取出的陣列值 var totalNumber = numbers.reduce(function(pre, item){ return pre + item; }, 0); console.log("The total number is " + totalNumber); /* 10 */

陣列

宣告

var myArray = []; var myOther = new Array(); myArray[0] = "apple"; myOther = ["dog", "cat", "bird"]; // 宣告同時給定初始值 var arr4 = new Array(1.5, '2009', true); var arr5 = [1.5, '2009', true];

查看長度

console.log(myArray.length);

添加新元素到尾端

myArray.push("banana");

添加新元素到前端

myArray.unshift("banana");

刪除尾端元素

myArray.pop();

反轉陣列

var data = [1, 2, 3, 4, 5]; data.reverse(); console.log(data); // 5 4 3 2 1

添加/刪除指定元素

/*用法:第一參數為指定位置 * 第二參數為刪除幾個元素 * 第三參數後可以新增元素 */ /*ex:刪除myArray[1]起(包含)的3個元素*/ myArray.splice(1, 3); /*ex:新增3個元素到myArray[2]之前*/ myArray.splice(2, 0, "a", "b", "c");

取出指定範圍的元素

// 第一個參數: 起始 index(包含) // 第二個參數: 結束 index(不包含) var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; var tmp = arr.slice(5,8); // 5,6,7

查詢陣列中某元素位置(是否存在)

/*第一參數為要查找的元素值 * 第二參數為指定從哪裡開始找(可不加) * 找到則回傳元素在陣列中的位置(第一筆) * 若找不到則回傳-1 */ myArray.indexOf("apple", fromIndex);

兩者結合

myArray.splice(myArray.indexOf("apple"), 1);

陣列合併

var a = ['a', 'b', 'c']; var b = ['d', 'e', 'f']; var c = a.concat(b); console.log(c) // a~f

陣列轉字串

var myArr = [1, 2, 3]; var myStr = myArr.join(""); // 123 // 自訂義中間符 var myStr = myArr.join("Y") //1Y2Y3

ES6 延展

依序 return 陣列每個值

var a = [1, 2, 3]; console.log(...a); // 1 2 3

故可以使用此種寫法改寫上方的陣列合併

var a = ['a', 'b', 'c']; var b = ['d', 'e', 'f']; var c = [...a, ...b]; console.log(c) // a~f

深拷貝

和物件一樣,Array 也是傳參考,如果想要實現陣列的深拷貝,可以使用 ES6 延展快速實現,因為它和手動取值一樣,依序取出陣列的值。

var a = [1, 2, 3]; var b = [...a]; b.push(4); // a: 1~3 // b: 1~4

類陣列

結構與陣列幾乎一樣,但是能夠使用 __proto__ 中的函數較少。

var doms = document.querySelectorAll("li"); console.log(doms);

正常 Array

var test = [1, 2, 3]; console.log(test);

套用延展特性,可以使類陣列轉為正常陣列

var doms = document.querySelectorAll("li"); var newDoms = [...doms]; console.log(newDoms);

實際應用

JS 函數中,若傳入未定義的參數,實際上還是會透過 arguments 變數存起來(有定義的參數也會存進)。

function addCash(){ var arg = arguments; console.log(arg); } addCash(100, 200, 300); // [100, 200, 300]

而聰明的你一定會想到,沒錯,這個 arguments 也是類陣列,沒有辦法使用太多的方法,像是加總(reduce),那解決方法也是透過上方的延展來轉換。

function updateEasyCard() { let arg = [...arguments]; let sum = arg.reduce(function (accumulator, currentValue) { return accumulator + currentValue; }, 0); console.log('我有 ' + sum + ' 元'); } updateEasyCard(10, 50, 100, 50, 5, 1, 1, 1, 500); // 我有 718 元

其餘參數

此處介紹的其餘參數和上方 arguments 概念類似,但 arguments 是將所有傳入函數的參數存起來,不論那些參數有沒有定義,而其餘參數則是存放沒有定義的參數而已。

此範例結果乍看之下和 arguments 差不多,

function moreMoney(...money) { console.log(money); } moreMoney(100, 100, 100); // [100, 100, 100]

但是改成這樣,就發現 Bob 是傳入有定義的參數 name,而其餘未定義的參數則被放入 money 中。

function moreMoney(name, ...money) { console.log(name, money); } moreMoney("Bob", 100, 100, 100); // Bob, [100, 100, 100]

解構賦值

Case1. 取陣列值

這樣的場景有沒有似曾相似

var family = ['小明', '杰倫', '阿姨', '老媽', '老爸']; var ming = family[0]; var jay = family[1]; var auntie = family[2]; ...

現在不用再這麼麻煩了,而且左邊變數數量小於等於右數陣列長度就好。

var family = ['小明', '杰倫', '阿姨', '老媽', '老爸']; var [ming, jay, auntie] = family; // 小明, 杰倫, 阿姨

如果想要跳過某一個值,可以改成這樣

var family = ['小明', '杰倫', '阿姨', '老媽', '老爸']; // 想要跳過的變數直接留空 // 此處即不想設變數接收阿姨的值 var [ming, jay, ,mom] = family; // 小明, 杰倫, 老媽

Case2. 兩數交換

蛤,上面的場景你沒見過?
那這個呢

var a = 123; var b = 456; var tmp = a; a = b; b = tmp;

一樣,可以直接改成簡潔有力的寫法

var a = 123; var b = 456; [a, b] = [b, a]; // a: 456 // b: 123

Q&A

1.
var [ming = '小明', jay = '杰倫'] = ['阿明']; console.log(ming, jay); // 請問答案是什麼? // --------------------------- // Ans: // 第一個會被覆蓋,第二個會用預設 // ming: "阿明" // jay: "杰倫"

字串

把字串變成陣列

/*split中放入字串切割分組的關鍵詞 ex: "," *不放則是切割每個字元 */ var list = "123abc".split(""); // 像是文章有好幾行,也可以用換行來切割: .split("\n") /*調用每個元素*/ for(let i = 0; i < list.length; i++){ console.log(list[i]); }

替換字元

var string = "abc"; var test = string.replace("c", "zzz"); console.log(test); // abzzz

左邊搜尋文字可以放正則式,常用寫法為 /搜尋文字/g

  1. 參數 g 為搜尋所有相符字串,若沒有加的話,則是替換第一個搜尋到的結果。
  2. 參數 i 為檢查時忽略大小寫
var string = "a c b c d"; var test = string.replace(/c/g, "QQ"); console.log(test); // a QQ b QQ d
var string = "acbcd"; var test = string.replace(/C/g, "QQ"); console.log(test); // acbcd test = string.replace(/C/ig, "QQ"); console.log(test); // aQQbQQd

取字串中的限定範圍

var str = "hello"; // 第一個參數是起始index,若是負數則從尾數來, // ex: -1則是最後, -2是倒數第二 // 第二個參數是取多長,不加則是取完剩下字元 // he console.log(str.substr(0, 2));

解構賦值

可以到陣列>解構賦值看更多說明

Case1. 取出每個字串

var str = "abc"; var [q, w, e] = str; // a, b, c

物件

格式

var myObject = { 屬性: 設定值, name: 'Ryan' }; console.log(myObject.name); myObject.age = 18; console.log(myObject.age);

也可以這樣寫,當一筆資料有 key 時,不想用陣列儲存,可以用這種方式:

var students = {}; students['Bob'] = {age: 18, sex: "male"}; students['Alice'] = {age: 17, sex: "female"}; console.log(students); // { // Bob: {age: 18, sex: "male"}, // Alice: {age: 17, sex: "female"} // }

物件 & 陣列 & 函式

var myObject = { teacher: 'Ken', student: ['A', 'B', 'C'], sayHi: function(grade){ alert('Hi'); } }; // 要call函數要加() // 沒有的話會回傳sayHi的設定值 myObject.sayHi();

建構函式(函式 & 物件)

function circle(r){ this.r = r; this.area = Math.PI * r * r; this.peri = 2 * Math.PI * r; this.print = function(){ alert("radius = " + r); }; } var ball = new circle(10); console.log(ball.r);

call by reference(sharing)

除了基本型別(Number、String、Boolean、null、undefined)是 call by value 之外,
Array、Object 為 call by reference(sharing)。

// call by value // a 和 b 的值存放的記憶體位置不一樣 // 所以改動 b 不會影響到 a var a = 123; var b = a; b = 456; console.log(a); // 123 console.log(b); // 456
// call by reference(sharaing) // b = a,實際上 b 是指到 a 的記憶體位置 // 所以 a 和 b 指向的是同一個地址,更改 b 的值也會影響到 a 的值 var a = {value: 100}; var b = a; b.value = 500; console.log(a.value); // 500 console.log(b.value); // 500
// 函數傳遞的參數為物件時也是一樣 function addMoney(tmp){ tmp.money += 100; } var cash = {money: 100}; addMoney(cash); console.log(cash.money); // 200

深拷貝

那該怎麼單純的取物件的值呢?

  1. 透過此種方式重新賦值
// b={xxx},將會使 b 存放的值,指向一個新的記憶體位置 var a = {value: 123} var b = a; b = {value: 456}; console.log(a.value); // 123 console.log(b.value); // 456

同樣的範例還有

var a = {name: 'Ted', age: 18}; var b = {name: a.name, age: a.age}; b.name = "changed"; console.log(a.name); // Ted console.log(b.name); // changed
  1. Object.assign
    懶得像上方手動複製,可以使用此方法
var a = {name: 'Ted'}; var b = Object.assign({}, a); b.name = "Ray"; // a.name: Ted // b.name: Ray

但是沒這麼簡單,以上方法只能深拷貝一層,也就是可能會發生以下狀況

var a = { name: 'Ted', language: { chinese: 'nice', english: 'nice' } }; var b = Object.assign({}, a); b.name = "Bob"; b.language.chinese = 'bad'; // a.name: Ted // a.language.chinese: bad

注意到了嗎
b 改變 name 並不會影響 a 的 name,但是改變再深一層的物件的值,就會影響到原本 a 的 language.chinese 了!

當初遇到這個問題爬了一下文,才知道原來這是很熱門的問題,也是面試常常會被問到的XD

那該怎麼解決呢?

  1. 原生 js 寫個 function,裡面用遞迴或 for 不斷取值,真正實現深拷貝。
// 網路上自己找QAQ
  1. 轉換成 JSON
var a = { name: 'Ted', language: { chinese: 'nice', english: 'nice' } }; var b = JSON.parse(JSON.stringify(a)); b.name = "Bob"; b.language.chinese = 'bad'; // a.name: Ted // a.language.chinese: nice
  1. 使用第三方套件(Jquery、lodash)
    • lodash
    ​​​​var test = {
    ​​​​    childrenKey: 'value',
    ​​​​    childrenObject: {
    ​​​​        keyA: 'value a',
    ​​​​        keyB: 'value b'
    ​​​​    }
    ​​​​}
    ​​​​var cloneA = cloneDeep(test);
    
    • JQuery
      深淺拷貝對應的參數是可選的,為true或false。默認情況是false(淺拷貝),並且false是不能夠顯示的寫出來的。如果想寫,只能寫true(深拷貝)。
    ​​​​var a = {};
    ​​​​var b = {
    ​​​​    name: 'Ted',
    ​​​​    language: {
    ​​​​        chinese: 'nice',
    ​​​​        english: 'nice'
    ​​​​        }
    ​​​​};
    ​​​​// 深拷貝
    ​​​​$.extend(true, a, b);
    ​​​​a.language.chinese = 'bad';
    ​​​​// b.language.chinese: nice
    ​​​​
    ​​​​
    ​​​​// 至於沒有加上參數就是淺拷貝
    ​​​​$.extend(a, b);
    ​​​​// 也等於
    ​​​​a = Object.assign({}, b);
    

解構賦值

Case1. 取出物件某值

var family = { ming: '小明', jay: '杰倫', }; var { ming } = family; console.log(ming); // 小明

如果想要將取出的值放到不同名稱的變數上

var family = { ming: '小明', jay: '杰倫', }; var { ming: newMing } = family; console.log(newMing);

Q&A

1.
var { ming: newMing, family: [, mom] } = { ming: '小明', family: ['阿姨', '老媽', '老爸'] } console.log(newMing, mom); // 請問答案是什麼? // ---------------------------- // Ans: // newMing: 小明 // mom: 老媽
2.
var { family: ming = '小明' } = {}; console.log(ming); // 請問答案是什麼? // ----------- // Ans: // ming: '小明'

縮寫

若屬性與值的名稱一樣,則可省略後者

var a = 123; var b = { a: a }; // 可縮寫為 var b = { a }

函數也可以縮寫

var a = { sayHi: function(){ console.log("Hi"); } } // 可縮寫為 var a = { sayHi () { console.log("Hi"); } }

搭配縮寫與延展的深拷貝

var a = { age: 18 } var b = { ...a } // b // { age: 18 } b.age = 20; // a.age: 18 // b.age: 20

For 迭代取值

inkey

var obj = { name: "HI", age: 20 }; for(var tmp in obj){ console.log("key: " + tmp + " value: " + obj[tmp]); } // "key: name value: HI" // "key: age value: 20" // 物件中的物件 var obj2 = { class1: { num: 30 }, class2: { num: 35 } } for(var tmp in obj2){ console.log("key: " + tmp + " value: " + obj[tmp].num); }
var arr = [1,2,3]; for(var tmp in arr){ console.log(tmp); } // 0 // 1 // 2

ofvalue

var arr = [1,2,3]; for(var tmp of arr){ console.log(tmp); } // 1 // 2 // 3

forEach

如果是陣列的話,也可以用 forEach 個別操作元素

var test = ['a', 'b', 'c']; test.forEach(function(value, index){ console.log("索引:" + index + "變數值" + value); })

HTML DOM(Document Object Model)

  1. 整份文件: document
  2. 整個 document 的根元素 document.documentElement
  3. 文件為一個由節點組成的樹狀結構
    • 節點(node)
    • 元素節點(element)
    • 屬性節點(attribute)
    • 文字節點(text)

選擇器

document.getElementById("id"); document.getElementsByTagName("tagname"); document.getElementsByClassName("classname"); document.querySelector("#id .class span"); document.querySelectorAll(".class");

querySelector

<h1 id="test">ID : <em></em></h1> <h1 class="test">First class: <em></em></h1> <h1 class="test">Second class: <em></em></h1>
document.querySelector("#test em").textContent = "#id > em"; var class_choose = document.querySelectorAll(".test em"); // [em, em] console.log(class_choose); for(var i = 0; i < class_choose.length; i++){ class_choose[i].textContent = "第 "+(i+1)+"個 class" }

判斷元素是否存在

這邊有一個方法可以快速判斷某 id、class 之元素是否存在

if(document.getElementById("test") == null){ console.log("不存在"); }else if(document.getElementById("test") != null){ console.log("存在"); }

HTML

innerHTML

<div id="demo"> <a href="#">myLink</a> </div> <div id="demo2"></div> <div id="demo3"></div>
var demo = document.getElementById("demo"); console.log(demo.innerHTML); // <a href="#">myLink</a> demo.innerHTML = "Hello"; console.log(demo.innerHTML); // Hello var demo2 = document.getElementById("demo2"); var demo3 = document.getElementById("demo3"); var num = 123; demo2.innerHTML = "<h1>"+num+"</h1>"; // 123 demo3.innerHTML = `<h1>${num}</h1>`; // 123

innerHTML使用上需注意資料來源是否安全
這是一個簡易的留言板,會把用戶輸入的內容顯示出來

<textarea id="content" cols="10" rows="10"></textarea> <input id="submit" type="button" value="送出"> <div id="showContent"></div>
document.getElementById("submit").onclick = function(){ var content = document.getElementById("content").value; document.getElementById("showContent").innerHTML = content; };

但是如果前後端沒有過濾掉一些非法字元,導致內容直接傳入資料庫,會發生安全問題。
Ex用戶輸入:

Hello~~~
<script>alert('Hi')</script>

Hello會正常顯示,script不會顯示出來,但是已經被植入到頁面。這筆留言被傳入資料庫後,日後其他用戶到這個頁面時就會執行這段script

createElement

<h1 id="testH1"></h1>
var newLink = document.createElement('a'); newLink.textContent = "click me"; newLink.setAttribute('href','http://www.google.com'); document.getElementById('testH1').appendChild(newLink);

removeElement

document.getElementById('testH1').removeChild(newLink);

CSS

遇到屬性有 - 號的,移除後把後面的字母變大寫。

document.getElementById("demo").style.color = "blue"; // padding-top document.getElementById("demo").style.paddingTop = "12px";

取得屬性值時會包含單位,ex: px、em等等,若是要運算時需要轉為數字

myDiv.style.width = (parseInt(myDiv.style.width)+50) + "px";

Attribute

<a id="myLink" href="#">Click Me</a>

setAttribute

document.getElementById("myLink").setAttribute('href','http://www.google.com');

getAttribute

document.getElementById("myLink").getAttribute('href');

Form

Event(事件)

1. 寫在HTML標籤上

<h1 onclick="alert('clicked')">inline</h1>

2. 寫在js中

但是元素無法一次綁兩個一樣的事件。
當事件觸發時,會自動把事件的詳細資訊傳入function中的第一個參數,通常會命名為eevent

var btn = document.getElementById("btn"); btn.onclick = function(e){ alert('Hi1'); }; btn.onclick = function(e){ alert('Hi2'); }; // Hi2

移除

btn.onclick = null;

3. addEventListener

var btn = document.getElementById("btn"); btn.addEventListener('click',function(e){ alert('clicked'); },false);

addEventListener的第三個參數決定事件處理是Event Bubbling(事件氣泡)還是Event Capturing(事件捕捉),一個是由內找到外(false預設);一個是由外到內(true)
舉例:
點click me時,console會先印出li,再印出ul
但是改成true時,則會先印出ul,再印出li

<ul id="ul"> <li id="li">click me</li> </ul>
var ul = document.querySelector("#ul"); var li = document.querySelector("#li"); ul.addEventListener('click',function(e){ console.log('ul clicked'); },false); li.addEventListener('click',function(e){ console.log('li clecked'); },false);

移除

myBox.removeEventListener('click', myFun);

e 事件詳細資訊

stopPropagation 中止冒泡事件

上一個例子,點click me時,會先顯示li,再往外顯示ul
很多情況會遇到這種元素重疊的問題,當我們只想顯示點擊的li時,可以把冒泡中止,不讓它再往外找。

ul.addEventListener('click',function(e){ console.log('ul clicked'); },false); li.addEventListener('click',function(e){ e.stopPropagation(); console.log('li clecked'); },false);

preventDefault 取消預設行為

有些元素會有默認行為,像是a連結點它會自動跳轉、submit點擊會自動把表單內容送出。
當我們不想讓它的預設動作執行時,可以取消它,像是前端想先檢查表單內容是否正確再傳到後端,所以要阻止submit點擊後就把內容送出。

<a href="http://www.google.com" id="link">myLink</a>
var link = document.querySelector("#link"); link.addEventListener('click',function(e){ e.preventDefault(); console.log('link no jump'); },false);

target 取得點擊的元素

<div class="header"> <ul style="border: 1px solid black; padding: 10px"> <li><a href="#" id="link">123</a></li> </ul> </div>
var header = document.querySelector(".header"); header.addEventListener('click',function(e){ // EX: <a href="#" id="link">123</a> console.log(e.target); // EX: LI console.log(e.target.nodeName); // EX: link console.log(e.target.id); },false);

優化綁定,由父元素監聽子元素

<div class="header"> <ul style="border: 1px solid black; padding: 10px; list-style: none"> <li>1</li> <li>2</li> <li>3</li> </ul> </div>

若是想顯示點擊的li內容,可以綁定每個li再顯示

var li = document.querySelectorAll('.header ul li'); for(var i = 0; i < li.length; i++){ li[i].addEventListener('click',showText); } function showText(e){ console.log(e.target.textContent); // console.log(this.textContent); }

但是這樣需要個別綁定,效率較低,而且動態新增的元素不會被綁定。
較好的做法是綁定li的父元素來監聽

var header = document.querySelector(".header"); header.addEventListener('click',function(e){ if(e.target.nodeName == "LI"){ console.log(e.target.textContent); } });

取得滑鼠座標位置

screen 計算在整個螢幕之位置(算入解析度)
page 計算在瀏覽器中的網頁頁面之位置
client 計算在瀏覽器中的位置
知道滑鼠座標也可以把滑鼠圖示改變(覆蓋)成自訂圖案

<body style="height: 1000px; cursor: none"> <div class="wrap" style="position: fixed; top:0"> <p> screenX: <span class="screenX"></span> screenY: <span class="screenY"></span> </p> <p> // ex: 滑到最下面y座標:1000 pageX: <span class="pageX"></span> pageY: <span class="pageY"></span> </p> <p> // ex: 滑到最下面y座標:578 clientX: <span class="clientX"></span> clientY: <span class="clientY"></span> </p> </div> <div class="mouseImg"> <img src="http://dl.stickershop.line.naver.jp/products/0/0/9/768/android/main.png" width="50px"> </div> </body>
var screenX = document.querySelector('.screenX'); var screenY = document.querySelector('.screenY'); var pageX = document.querySelector('.pageX'); var pageY = document.querySelector('.pageY'); var clientX = document.querySelector('.clientX'); var clientY = document.querySelector('.clientY'); var mouseImg = document.querySelector('.mouseImg'); var body = document.body; body.addEventListener('mousemove', getPosition, false); function getPosition(e){ screenX.textContent = e.screenX; screenY.textContent = e.screenY; pageX.textContent = e.pageX; pageY.textContent = e.pageY; clientX.textContent = e.clientX; clientY.textContent = e.clientY; mouseImg.style.left = e.clientX + 'px'; mouseImg.style.top = e.clientY + 'px'; }

綁定 document

// 綁定整個 document document.addEventListener('click',fun); // 綁定 body var body = document.body; body.addEventListener('click',fun);

DOM Event 觸發事件

change 表單內容更動觸發

<select id="select"> <option value="1">1</option> <option value="2">2</option> </select>
function showValue(e){ console.log(e.target.value); console.log(this.value); } var select = document.querySelector("#select"); select.addEventListener('change',showValue,false);

keydown 按下鍵盤觸發

<input type="text" id="input">
function showKey(e){ console.log(e.keyCode); // enter: 13, space: 32, 1: 49, 2: 50, 3: 51 switch(e.keyCode){ case 49: console.log('1'); break; case 50: console.log('2'); break; case 51: console.log('3'); break; } } var input = document.querySelector("#input"); input.addEventListener('keydown',showKey,false);

focus / blur 表單進入焦點/離開焦點觸發

<input type="text" id="input">
var input = document.getElementById("input"); input.addEventListener('blur',function(e){ var value = this.value; (value != '')? console.log(`Your text is ${value}`):console.log('You must write something'); });

mousemove 滑鼠hover(滑動)時觸發

box.addEventListener('mousemove',function(){ console.log('mouse hover'); });

mouseover 滑鼠進入元素時觸發

<div id="box" style="background-color: orange; width: 150px; height: 150px"></div>
document.getElementById('box').addEventListener('mouseover', function(){ console.log('進入'); });

mouseout 滑鼠離開元素觸發

<div id="box" style="background-color: orange; width: 150px; height: 150px"></div>
document.getElementById('box').addEventListener('mouseout', function(){ console.log('離開'); });

copy 複製時觸發

若要更改用戶複製內容可寫

document.addEventListener('copy', function (e) { e.preventDefault(); // We want our data, not data from any selection, to be written to the clipboard e.clipboardData.setData('text/plain', "Don't copy!"); e.clipboardData.setData('text/plain', window.getSelection() + "Don't copy!"); // e.clipboardData.setData('text/html', '<b>Hello, world!</b>'); });

說明:

  • 如果默認事件沒有取消,就複製所選內容(如果有選中內容)到剪貼板;
  • 如果取消了默認事件,同時調用 setData()方法:就複製 clipboardData 的內容到剪貼板;
  • 如果取消了默認行為,而且沒有調用使用 setData()方法,就沒有任何行為(用戶所選內容也不會複製到剪貼板)。

paste 貼上時觸發

輸入重要資料時,不希望使用者使用複製貼上的方式可寫

<label>請輸入密碼: <input type="password" id="pass"></label>
document.getElementById("pass").addEventListener('paste', function(e){ e.preventDefault(); alert("密碼請勿用貼上的"); });

表單取值

radio

<form name="myForm"> <label><input name="language" type="radio" value="繁中" checked>繁中</label> <label><input name="language" type="radio" value="韓文">韓文</label> </form>
var form = document.getElementById("myForm"); var userLanguage; // 取得radio的值 for(var i = 0; i < form.language.length; i++){ if(form.language[i].checked){ userLanguage = form.language[i].value; break; } }

select

<form id="myForm"> <select name="country"> <option value="台灣">台灣</option> <option value="韓國">韓國</option> <option value="日本">日本</option> </select> </form>
var form = document.getElementById("myForm"); var userCountry = form.country.value;

localStorage

瀏覽器儲存在本地端的資料,格式為Key : value
value 的型態只有 String

setItem 建立

var name = 'XXX'; localStorage.setItem('userName',name);

getItem 取得

localStorage.getItem('userName');

範例

<h2>請輸入你的名字:</h2> <input type="text" id="userName"> <input type="button" id="submit" value="送出"> <input type="button" id="callName" value="顯示名字">
var submit = document.getElementById('submit'); submit.addEventListener('click',function(){ var name = document.getElementById('userName').value; localStorage.setItem('userName',name); }); var callName = document.getElementById('callName'); callName.addEventListener('click',function(){ var name = localStorage.getItem('userName'); alert(name); });

removeItem 移除

localStorage.removeItem('userName');

全部移除

localStorage.clear();

JSON字串轉換

由於 value 只能存入 String ,所以要使用某些函數來轉換 String ,好方便存取。

轉換成字串 JSON.stringify

var arr = ['a','b','c']; // a,b,c is object console.log(arr + " is " + typeof(arr)); var toStr = JSON.stringify(arr); // [\"a\",\"b\",\"c\"] is string console.log(toStr + " is " + typeof(toStr));

物件轉 JSON 字串 + 縮排

<pre id="content"></pre> document.getElementById('content').textContent = JSON.stringify(myJson, null, 3);

轉換回原本格式 JSON.parse

var strToArr = JSON.parse(toStr); // a,b,c is object console.log(strToArr + " is " + typeof(strToArr));

html屬性data-* 自訂資料

可以存放 value 到 html 屬性中,格式為data-key="value"

<div class="test" data-name="Rose" data-age="18">click me</div>

存取方法為.dataset.key

var test = document.querySelector('.test'); console.log(test.dataset.name); console.log(test.dataset.age);

結合 array 用法

動態新增 array 資料到 ul ,並紀錄 index 到 data-num 中,
點擊 li 時依所點之 num 來刪除 array 中相對應之 index。

<ul id="myList"></ul>
var myList = document.getElementById('myList'); var content = [ { name: 'Molt', sex: 'male' }, { name: 'YaoYao', sex: 'male' }, { name: 'Lisa', sex: 'famale' } ]; function updateList(){ var str = ""; for(var i = 0; i < content.length; i++){ str += '<li data-num:' + i + '>' + content[i].name + '</li>'; } myList.innerHTML = str; } updateList(); myList.addEventListener('click', function(e){ if(e.target.nodeName == "LI"){ content.splice(this.dataset.num, 1); updateList(); } });

sessionStorage

相似於 localStorage ,不同之處在於 sessionStorage 在瀏覽器關掉後就自動清除。

建立

sessionStorage.setItem("key", value);

刪除

sessionStorage.removeItem("key");

BOM(Browser Object Model)


開啟瀏覽器後會開啟 window 物件,裡面包含了很多資訊,像是 document 中的變數、函式。

window

列印

window.print();

開啟視窗

// 實際還有很多參數可以帶 window.open('http://www.google.com');

網頁頁面寬高

window.innerWidth; window.innerHeight;

瀏覽器寬高

window.outerWidth; window.outerHeight;

配合 JS 動態指定圖片大小

body{ background-image: url('img/bg.png'); background-repeat: no-repeat; }
// 網頁載入時設定背景圖片為當前頁面寬度 var body = document.body; body.style.backgroundSize = window.innerWidth + "px"; // 監聽使用者改變瀏覽器寬高事件,跟著改變圖片寬度 body.onresize = function(){ body.style.backgroundSize = window.innerWidth + "px"; }

setInterval 設定重複執行週期任務

test = window.setInterval("show()", 1000); function show(){ console.log('Do in 1 sec again'); }

clearInterval 清除週期任務

window.clearInterval(test);

setTimeOut 設定單次執行任務

// 注意: 若是把 function 寫在裡面,不需要 "" 起來 test = setTimeout(function show(){ console.log('HIHI'); }, 1000);

clearTimeout

clearTimeOut(test);

history

回到上一頁

window.history.back();

下一頁

window.history.forward();

location

顯示當前網址

window.location.href;

跳轉到指定網址

window.location.href = 'http://www.google.com';

顯示參數
ex: ?a=3&b=4

window.location.search;

顯示瀏覽器資訊

window.navigator.appVersion; // "5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36"

顯示用戶當前是否連接網路

window.navigator.onLine; // true、false

Ajax

AJAX即「Asynchronous JavaScript and XML」(非同步的JavaScript與XML技術),指的是一套綜合了多項技術的瀏覽器端網頁開發技術。 來源:維基百科

var xhr = new XMLHttpRequest(); // true: 非同步 , false: 同步 xhr.open('get', 'http://xxx', true); xhr.send(null);

XMLHttpRequest 狀態碼(readyState)

  • 0
    已經產生 XMLHttpRequest ,但還沒連結到要取得的網址。
  • 1
    用了 open() ,但還沒傳送資料過去
  • 2
    用了 send()
  • 3
    loading 資料
  • 4
    撈回資料了,數據已接收完全

實際範例

透過 API 取得 JSON 資料

var xhr = new XMLHttpRequest(); xhr.open('get', 'http://xxx', true); xhr.send(null); xhr.onload = function(data) { console.log(xhr.responseText); };

POST(form)

var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://xxx', true); xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); xhr.send('email=test@gmail.com&password=123456');

POST(Json)

var account = { email: 'test@gmail.com', password:'12456' } var xhr = new XMLHttpRequest(); xhr.open('post','https://xxx.com',true); xhr.setRequestHeader('Content-type','application/json'); var data = JSON.stringify(account); xhr.send(data);