標準對象
Date
在JavaScript中,Date
對象用來表示日期和時間膛锭。
要獲取系統(tǒng)當(dāng)前時間晴氨,用:
var now = new Date();
now;
now.getFullYear();
now.getMonth(); // 5, 月份,注意月份范圍是0~11仆抵,5表示六月
now.getDate(); // 24, 表示24號
now.getDay(); // 3, 表示星期三
now.getHours(); // 19, 24小時制
now.getMinutes(); // 49, 分鐘
now.getSeconds(); // 22, 秒
now.getMilliseconds(); // 875, 毫秒數(shù)
now.getTime(); // 1435146562875, 以number形式表示的時間戳
方法一:如果要創(chuàng)建一個指定日期和時間的Date
對象跟继,可以用:
var d = new Date(2015, 5, 19, 20, 15, 30, 123);
d;// Fri Jun 19 2015 20:15:30 GMT+0800 (CST)
我們發(fā)現(xiàn)傳入的數(shù)據(jù)是5月,為什么結(jié)果確實June六月镣丑?
因為JavaScript的月份范圍用整數(shù)表示是0~11舔糖,0
表示一月,1
表示二月……莺匠,所以要表示6月金吗,我們傳入的是5
!
方法二:創(chuàng)建一個指定日期和時間的方法是解析一個符合ISO 8601格式的字符串:
var d = Date.parse('2015-06-24T19:49:22.875+08:00');
d; // 1435146562875
但它返回的不是Date
對象趣竣,而是一個時間戳摇庙。不過有時間戳就可以很容易地把它轉(zhuǎn)換為一個Date
:
var d = new Date(1435146562875);
d; // Wed Jun 24 2015 19:49:22 GMT+0800 (CST)
d.getMonth(); // 5
使用Date.parse()時傳入的字符串使用實際月份01 ~ 12,轉(zhuǎn)換為Date對象后getMonth()獲取的月份值為0 ~ 11遥缕。
時區(qū)
Date
對象表示的時間總是按瀏覽器所在時區(qū)顯示的卫袒,不過我們既可以顯示本地時間,也可以顯示調(diào)整后的UTC時間:
var d = new Date(1435146562875);
d.toLocalString();// '2015/6/24 下午7:49:22'单匣,本地時間(北京時區(qū)+8:00)夕凝,顯示的字符串與操作系統(tǒng)設(shè)定的格式有關(guān)
d.toUTCString(); // 'Wed, 24 Jun 2015 11:49:22 GMT',UTC時間封孙,與本地時間相差8小時
那么在JavaScript中如何進行時區(qū)轉(zhuǎn)換呢迹冤?實際上,只要我們傳遞的是一個number
類型的時間戳虎忌,我們就不用關(guān)心時區(qū)轉(zhuǎn)換泡徙。任何瀏覽器都可以把一個時間戳正確轉(zhuǎn)換為本地時間。
時間戳是一個自增的整數(shù)膜蠢,它表示從1970年1月1日零時整的GMT時區(qū)開始的那一刻堪藐,到現(xiàn)在的毫秒數(shù)。假設(shè)瀏覽器所在電腦的時間是準確的挑围,那么世界上無論哪個時區(qū)的電腦礁竞,它們此刻產(chǎn)生的時間戳數(shù)字都是一樣的,所以杉辙,時間戳可以精確地表示一個時刻模捂,并且與時區(qū)無關(guān)。
要獲取當(dāng)前時間戳蜘矢,可以用:
'use strict';
if (Date.now) {
console.log(Date.now()); //老版本IE沒有now()方法
} else {
console.log(new Date().getTime());
}
練習(xí)
小明為了和女友慶祝情人節(jié)狂男,特意制作了網(wǎng)頁,并提前預(yù)定了法式餐廳品腹。小明打算用JavaScript給女友一個驚喜留言:
'use strict';
var today = new Date();
if (today.getMonth() === 2 && today.getDate() === 14) {
alert('親愛的岖食,我預(yù)定了晚餐,晚上6點在餐廳見舞吭!');
}
//應(yīng)該把2改為1泡垃,因為轉(zhuǎn)換為Date對象后getMonth()獲取的月份值為0~11析珊。
RegExp
字符串是編程時涉及到的最多的一種數(shù)據(jù)結(jié)構(gòu),對字符串進行操作的需求幾乎無處不在蔑穴。比如判斷一個字符串是否是合法的Email地址忠寻,雖然可以編程提取@
前后的子串,再分別判斷是否是單詞和域名存和,但這樣做不但麻煩锡溯,而且代碼難以復(fù)用。
正則表達式是一種用來匹配字符串的強有力的武器哑姚。它的設(shè)計思想是用一種描述性的語言來給字符串定義一個規(guī)則,凡是符合規(guī)則的字符串芜茵,我們就認為它“匹配”了叙量,否則,該字符串就是不合法的九串。
所以我們判斷一個字符串是否是合法的Email的方法是:
- 創(chuàng)建一個匹配Email的正則表達式绞佩;
- 用該正則表達式去匹配用戶的輸入來判斷是否合法。
因為正則表達式也是用字符串表示的猪钮,所以品山,我們要首先了解如何用字符來描述字符。
在正則表達式中烤低,如果直接給出字符肘交,就是精確匹配。用\d
可以匹配一個數(shù)字扑馁,\w
可以匹配一個字母或數(shù)字涯呻,所以:
-
'00\d'
可以匹配'007'
,但無法匹配'00A'
腻要; -
'\d\d\d'
可以匹配'010'
复罐; -
'\w\w'
可以匹配'js'
;
.
可以匹配任意字符雄家,所以:
-
'js.'
可以匹配'jsp'
效诅、'jss'
、'js!'
等等趟济。
要匹配變長的字符乱投,在正則表達式中,用*
表示任意個字符(包括0個)咙好,用+
表示至少一個字符篡腌,用?
表示0個或1個字符,用{n}
表示n個字符勾效,用{n,m}
表示n-m個字符:
來看一個復(fù)雜的例子:\d{3}\s+\d{3,8}
嘹悼。
我們來從左到右解讀一下:
-
\d{3}
表示匹配3個數(shù)字叛甫,例如'010'
; -
\s
可以匹配一個空格(也包括Tab等空白符)杨伙,所以\s+
表示至少有一個空格其监,例如匹配' '
,'\t\t'
等限匣; -
\d{3,8}
表示3-8個數(shù)字抖苦,例如'1234567'
。
綜合起來米死,上面的正則表達式可以匹配以任意個空格隔開的帶區(qū)號的電話號碼锌历。
如果要匹配'010-12345'
這樣的號碼呢?由于'-'
是特殊字符峦筒,在正則表達式中究西,要用'\'
轉(zhuǎn)義,所以物喷,上面的正則是\d{3}\-\d{3,8}
卤材。
但是,仍然無法匹配'010 - 12345'
峦失,因為帶有空格扇丛。所以我們需要更復(fù)雜的匹配方式。
字符 | 精確匹配 |
---|---|
\d | 數(shù)字 |
\w | 一個字母或數(shù)字 |
. | 任意字符 |
* | 表示任意個字符(包括0個) |
+ | 表示至少一個字符 |
? | 表示0個或1個字符 |
{n, m} | 表示n ~ m個字符 |
\s | 匹配一個空格(也包括Tab等空白符) |
進階
要做更精確地匹配尉辑,可以用[]
表示范圍帆精,比如:
-
[0-9a-zA-Z\_]
可以匹配一個數(shù)字、字母或者下劃線材蹬; -
[0-9a-zA-Z\_]+
可以匹配至少由一個數(shù)字实幕、字母或者下劃線組成的字符串,比如'a100'
堤器,'0_Z'
昆庇,'js2015'
等等; -
[a-zA-Z\_\$][0-9a-zA-Z\_\$]*
可以匹配由字母或下劃線闸溃、組成的字符串表蝙,也就是JavaScript允許的變量名;
-
[a-zA-Z\_\$][0-9a-zA-Z\_\$]{0, 19}
更精確地限制了變量的長度是1-20個字符(前面1個字符+后面最多19個字符)乓旗。
A|B
可以匹配A或B府蛇,所以(J|j)ava(S|s)cript
可以匹配'JavaScript'
、'Javascript'
屿愚、'javaScript'
或者'javascript'
汇跨。
^
表示行的開頭务荆,^\d
表示必須以數(shù)字開頭。
$
表示行的結(jié)束穷遂,\d$
表示必須以數(shù)字結(jié)束函匕。
你可能注意到了,js
也可以匹配'jsp'
蚪黑,但是加上^js$
就變成了整行匹配盅惜,就只能匹配'js'
了。
RegExp
有了準備知識忌穿,我們就可以在JavaScript中使用正則表達式了抒寂。
JavaScript有兩種方式創(chuàng)建一個正則表達式:
第一種方式是直接通過/正則表達式/
寫出來,第二種方式是通過new RegExp('正則表達式')
創(chuàng)建一個RegExp對象掠剑。
兩種寫法是一樣的:
var re1 = /ABC\-001/;
var re2 = new RegExp('ABC\\-001');
re1;// /ABC\-001/
re2;// /ABC\-001/
注意蓬推,如果使用第二種寫法,因為字符串的轉(zhuǎn)義問題澡腾,字符串的兩個\\
實際上是一個\
。
先看看如何判斷正則表達式是否匹配:
var re = /^d{3}\-\d{3,8}$/;
re.test('010-12345'); //true
re.test('010-1234x'); //false
re.test('010 12345'); //false
RegExp對象的test()
方法用于測試給定的字符串是否符合條件糕珊。
切分字符串
用正則表達式切分字符串比用固定的字符更靈活动分,請看正常的切分代碼:
'a b c'.split(' '); //['a', 'b', '', '', 'c']
嗯,無法識別連續(xù)的空格红选,用正則表達式試試:
'a b c'.split(/\s+/); //['a', 'b', 'c']
無論多少個空格都可以正常分割澜公。加入,
試試:
'a,b, c d'.split(/[\s\,]+/); // ['a', 'b', 'c', 'd']
再加入;
試試:
'a,b;; c d'.split(/[\s\,\;]+/); //['a', 'b', 'c', 'd']
分組
除了簡單地判斷是否匹配之外,正則表達式還有提取子串的強大功能喇肋。用()
表示的就是要提取的分組(Group)坟乾。比如:
^(\d{3})-(\d{3,8})$
分別定義了兩個組,可以直接從匹配的字符串中提取出區(qū)號和本地號碼:
var re = /^(\d{3})-(\d{3,8})$/;
re.exec('010-12345'); // ['010-12345', '010', '12345']
re.exec('010 12345'); //null
如果正則表達式中定義了組蝶防,就可以在RegExp
對象上用exec()
方法提取出子串來甚侣。
exec()
方法在匹配成功后,會返回一個Array
间学,第一個元素是正則表達式匹配到的整個字符串殷费,后面的字符串表示匹配成功的子串。
exec()
方法在匹配失敗時返回null
低葫。
提取子串非常有用详羡。來看一個更復(fù)雜的例子:
var re = /^(0[0-9]|1[0-9]|2[0-3]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9]|[0-9])$/;
re.exec('19:05:30'); // ['19:05:30', '19', '05', '30']
這個正則表達式可以直接識別合法的時間。但是有些時候嘿悬,用正則表達式也無法做到完全驗證实柠,比如識別日期:
var re = /^(0[1-9]|1[0-2]|[0-9])-(0[1-9]|1[0-9]|2[0-9]|3[0-1]|[0-9])$/;
對于'2-30'
,'4-31'
這樣的非法日期善涨,用正則還是識別不了窒盐,或者說寫出來非常困難草则,這時就需要程序配合識別了。
貪婪匹配
需要特別指出的是登钥,正則匹配默認是貪婪匹配畔师,也就是匹配盡可能多的字符。舉例如下牧牢,匹配出數(shù)字后面的0
:
var re = /^(d+)(0*)$/;
re.exec('102300'); // ['102300', '102300', '']
由于\d+
采用貪婪匹配看锉,直接把后面的0
全部匹配了,結(jié)果0*
只能匹配空字符串了塔鳍。
必須讓\d+
采用非貪婪匹配(也就是盡可能少匹配)伯铣,才能把后面的0
匹配出來,加個?
就可以讓\d+
采用非貪婪匹配:
var re = /^(d+?)(0*)$/;
re.exec('102300'); // ['102300', '1023', '00']
全局搜索
var r1 = /test/g;
// 等價于:
var r2 = new RegExp('test', 'g');
全局匹配可以多次執(zhí)行exec()
方法來搜索一個匹配的字符串轮纫。當(dāng)我們指定g
標志后腔寡,每次運行exec()
,正則表達式本身會更新lastIndex
屬性掌唾,表示上次匹配到的最后索引:
var s = 'JavaScript, VBScript, JScript and ECMAScript';
var re=/[a-zA-Z]+Script/g;
// 使用全局匹配:
re.exec(s); // ['JavaScript']
re.lastIndex; // 10
re.exec(s); // ['VBScript']
re.lastIndex; // 20
re.exec(s); // ['JScript']
re.lastIndex; // 29
re.exec(s); // ['ECMAScript']
re.lastIndex; // 44
re.exec(s); // null放前,直到結(jié)束仍沒有匹配到
全局匹配類似搜索,因此不能使用/^...$/
糯彬,那樣只會最多匹配一次凭语。
正則表達式還可以指定i
標志,表示忽略大小寫撩扒,m
標志似扔,表示執(zhí)行多行匹配。
練習(xí)
請嘗試寫一個驗證Email地址的正則表達式搓谆。版本一應(yīng)該可以驗證出類似的Email:
'use strict';
var re = /^ $/;
//答案:var re = /^[a-zA-Z0-9\.]+@\w+\.\w+$/;
// 測試:
var
i,
success = true,
should_pass = ['someone@gmail.com', 'bill.gates@microsoft.com', 'tom@voyager.org', 'bob2015@163.com'],
should_fail = ['test#gmail.com', 'bill@microsoft', 'bill%gates@ms.com', '@voyager.org'];
for (i = 0; i < should_pass.length; i++) {
if (!re.test(should_pass[i])) {
console.log('測試失敗: ' + should_pass[i]);
success = false;
break;
}
}
for (i = 0; i < should_fail.length; i++) {
if (re.test(should_fail[i])) {
console.log('測試失敗: ' + should_fail[i]);
success = false;
break;
}
}
if (success) {
console.log('測試通過!');
}
版本二可以驗證并提取出帶名字的Email地址:
'use strict';
var re = /^ $/;
//答案:var re=/^<([\w\s]+)>\s+([\w]+@\w+\.\w+)$/;
// 測試:
var r = re.exec('<Tom Paris> tom@voyager.org');
if (r === null || r.toString() !== ['<Tom Paris> tom@voyager.org', 'Tom Paris', 'tom@voyager.org'].toString()) {
console.log('測試失敗!');
}
else {
console.log('測試成功!');
}
JSON
JSON是JavaScript Object Notation的縮寫炒辉,它是一種數(shù)據(jù)交換格式。
道格拉斯·克羅克福特長期擔(dān)任雅虎的高級架構(gòu)師泉手,自然鐘情于JavaScript黔寇。他設(shè)計的JSON實際上是JavaScript的一個子集。在JSON中斩萌,一共有這么幾種數(shù)據(jù)類型:
number:和JavaScript的
number
完全一致啡氢;boolean:就是JavaScript的
true
或false
;string:就是JavaScript的
string
术裸;null:就是JavaScript的
null
倘是;array:就是JavaScript的
Array
表示方式——[]
;object:就是JavaScript的
{ ... }
表示方式袭艺。
以及上面的任意組合搀崭。
并且,JSON還定死了字符集必須是UTF-8,表示多語言就沒有問題了瘤睹。為了統(tǒng)一解析升敲,JSON的字符串規(guī)定必須用雙引號""
,Object的鍵也必須用雙引號""
轰传。
由于JSON非常簡單驴党,很快就風(fēng)靡Web世界,并且成為ECMA標準获茬。幾乎所有編程語言都有解析JSON的庫港庄,而在JavaScript中,我們可以直接使用JSON恕曲,因為JavaScript內(nèi)置了JSON的解析鹏氧。
把任何JavaScript對象變成JSON,就是把這個對象序列化成一個JSON格式的字符串佩谣,這樣才能夠通過網(wǎng)絡(luò)傳遞給其他計算機把还。
如果我們收到一個JSON格式的字符串,只需要把它反序列化成一個JavaScript對象茸俭,就可以在JavaScript中直接使用這個對象了吊履。
序列化
讓我們先把小明這個對象序列化成JSON格式的字符串:
'use strict';
var xiaoming = {
name: '小明',
age: 14,
gender: true,
height: 1.65,
grade: null,
'middle-school': '\"W3C\" Middle School',
skills: ['JavaScript', 'Java', 'Python', 'Lisp']
};
var s = JSON.stringify(xiaoming);
console.log(s);
要輸出得好看一些,可以加上參數(shù)调鬓,按縮進輸出:
JSON.stringify(xiaoming, null, ' ');
結(jié)果:
{
"name": "小明",
"age": 14,
"gender": true,
"height": 1.65,
"grade": null,
"middle-school": "\"W3C\" Middle School",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}
第二個參數(shù)用于控制如何篩選對象的鍵值率翅,如果我們只想輸出指定的屬性,可以傳入Array
:
JSON.stringify(xiaoming, ['name', 'skills'], ' ');
結(jié)果:結(jié)果:
{
"name": "小明",
"skills": [
"JavaScript",
"Java",
"Python",
"Lisp"
]
}
還可以傳入一個函數(shù)袖迎,這樣對象的每個鍵值對都會被函數(shù)先處理:
function convert(key, value) {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
}
JSON.stringify(xiaoming, convert, ' ');
上面的代碼把所有屬性值都變成大寫:
{
"name": "小明",
"age": 14,
"gender": true,
"height": 1.65,
"grade": null,
"middle-school": "\"W3C\" MIDDLE SCHOOL",
"skills": [
"JAVASCRIPT",
"JAVA",
"PYTHON",
"LISP"
]
}
如果我們還想要精確控制如何序列化小明,可以給xiaoming
定義一個toJSON()
的方法腺晾,直接返回JSON應(yīng)該序列化的數(shù)據(jù):
var xiaoming = {
name: '小明',
age: 14,
gender: true,
height: 1.65,
grade: null,
'middle-school': '\"W3C\" Middle School',
skills: ['JavaScript', 'Java', 'Python', 'Lisp'],
toJSON: function () {
return { // 只輸出name和age燕锥,并且改變了key:
'Name': this.name,
'Age': this.age
};
}
};
JSON.stringify(xiaoming); // '{"Name":"小明","Age":14}'
反序列化
拿到一個JSON格式的字符串,我們直接用JSON.parse()
把它變成一個JavaScript對象:
JSON.parse('[1, 2, 3, true]'); //[1, 2, 3, true]
JSON.parse('{"name":"小明“, "age":14}');// Object {name: '小明', age: 14}
JSON.parse('true'); //true
JSON.parse('123.45'); //123.45
JSON.parse()
還可以接收一個函數(shù)悯蝉,用來轉(zhuǎn)換解析出的屬性:
'use strict';
var obj = JSON.parse('{"name":"小明", "age":14}', function (key, value) {
if (key === 'name') {
return value + '同學(xué)';
}
return value;
});
console.log(JSON.stringify(obj)); //{name: '小明同學(xué)', age: 14}