標(biāo)簽: 我的筆記
---學(xué)習(xí)資料:http://javascript.ruanyifeng.com/
1. 導(dǎo)論
JavaScript的發(fā)明目的,就是作為瀏覽器的內(nèi)置腳本語言,為網(wǎng)頁開發(fā)者提供操控瀏覽器的能力。
近年來,JavaScript的使用范圍,慢慢超越了瀏覽器,正在向通用的系統(tǒng)語言發(fā)展。
“任何能夠用JavaScript實現(xiàn)的應(yīng)用,最終都必將用JavaScript實現(xiàn)。”
2. 基本語法
2.1 基本句法和變量
2.1.1 語句
-
JavaScript程序的執(zhí)行單位為行(line),一般一行就是一個語句。
var a = 1 + 3 ;
這是一條語句,其中“1+3”叫做表達式。
語句和表達式的區(qū)別在于:前者主要為了進行某種操作,后者則是為了得到返回值。
語句以分號結(jié)尾(表達式不需要分號結(jié)尾,否則會變成無任何意義的語句),一個分號就表示一個語句結(jié)束。多個語句可以寫在一行內(nèi)。
分號前面可以沒有任何內(nèi)容
;,這叫做空語句。
2.1.2 變量
-
變量是對“值”的引用,變量有聲明和賦值兩個步驟。
var a = 1;
相當(dāng)于:
var a;
a = 1;
-
只聲明變量而不賦值,則該變量的值為undefined。
var a;
a //undefined -
不聲明而直接使用會報錯:
x //ReferenceError: x is not defined
- JavaScript允許省略var,這樣創(chuàng)建的是全局變量,建議總是使用var。
2.1.3 變量提升
- JavaScript引擎的工作方式:先解析代碼,將所有的變量聲明提升(hoisting)到代碼的頭部,再一行一行地運行。
比如:
console.log(a); //undefined
var a = 1; //var a;被提升
上面代碼不會打印出1,但不會報錯。因為存在變量提升,所以真正運行的是:
var a;
console.log(a);
a = 1;
- 請注意,變量提升只對var命令聲明的變量有效,如果變量不是用var命令聲明的就不會發(fā)生變量提升。因為JavaScript引擎將其視為對頂層對象的b屬性進行賦值。
比如:
console.log(b); //ReferenceError: b is not defined
b = 1; //沒有變量聲明被提升
2.1.4 標(biāo)識符
- 標(biāo)識符用來識別具體對象。最常見的標(biāo)識符就是變量名和函數(shù)名。
- JavaScript語言的標(biāo)識符對大小寫敏感。
- 標(biāo)識符的命名規(guī)則:
- 第1個字符可以是任意Unicode字母,以及美元符號($)和下劃線(_)。
- 其它字符,還可以用數(shù)字。
- JavaScript的保留字不能用作標(biāo)識符。
- 保留字有:var、void、class、if、for、new、null、true、return 等。
- 還有特別含義的標(biāo)識符:Infinity、NaN、undefined。
2.1.5 注釋
- 單行注釋,用//起頭
- 多行注釋,放在/* 和 */之間
比如:
// 單行注釋
/*
多行
注釋
*/
2.1.6 區(qū)塊
一組大括號包起來的多條語句,稱為“區(qū)塊”(block)。
與大多數(shù)編程語言不一樣,JavaScript的區(qū)塊不構(gòu)成單獨的作用域(scope)。
比如:
{
var a = 1;
}
a // 1
類似的C#代碼則會報錯:
{
var a = 1;
}
Console.WriteLine(a); //當(dāng)前上下文中不存在名稱“a”
2.1.7 條件語句
a. if
if (m === 3)
m += 1;
if (m === 0) {
// ...
} else if (m === 1) {
// ...
} else {
// ...
}
else代碼塊總是跟隨離自己最近的那個if語句。
b. switch
建議case內(nèi)部必須有break
因為case內(nèi)部如果沒有break就會接著執(zhí)行下一個case部分(而且不判斷case條件)。switch語句部分和case語句部分,都可以使用表達式。
switch后面的表達式與case后面的表示式比較時,采用的是嚴格相等運算符(===),而不是相等運算符(==)。
這意味著比較時不會發(fā)生類型轉(zhuǎn)換:
console.log(4=="4");
console.log(4==="4");
switch(1 + 3) {
case "4":
console.log("string 4");
break;
case 2 + 2:
console.log("number 4");
case 2 + 3:
console.log("number 5");
break;
default:
console.log("default");
}
上面代碼會輸出:
true
false
number 4 //因為4==="4"是false,而4===4是true
number 5 //因為case 2 + 2 語句內(nèi)部沒有使用break
- switch結(jié)構(gòu)不利于代碼重用,往往可以用對象形式重寫。
比如:
var o = {
banana: function (){ console.log("banana"); },
apple: function (){ console.log("apple"); },
default: function (){ console.log("default"); }
};
var fruit="apple";
if (o[fruit]){
o[fruit]();
} else {
o['default']();
}
輸出apple
2.1.8 循環(huán)語句
a. while循環(huán)
如果條件為真,就不斷循環(huán)。
var i = 0;
while (i<100){
console.log('i當(dāng)前為:' + i);
i++;
}
b. for循環(huán)
for語句分成三步:
初始化(initialize):確定循環(huán)的初始值,只執(zhí)行一次;
檢測(test):檢測循環(huán)條件,只要為真就進行后續(xù)操作;
-
遞增(increment):當(dāng)循環(huán)體執(zhí)行完畢后,執(zhí)行遞增操作,然后重新檢測。
for (var i=0; i < arr.length; i++) {
console.log(arr[i]);
}
for語句表達式的三個部分可以省略任何一個或全部省略。
var i=0;
for (; i < 10; ) {
console.log(i++);
}
所有for循環(huán),都可以改寫成while循環(huán)。
c. do...while循環(huán)
- do...while循環(huán)與while循環(huán)類似,唯一的區(qū)別就是先運行一次循環(huán)體,然后判斷循環(huán)條件。
- 另外,while語句后面的分號不能省略:
至少運行一次,這是這種結(jié)構(gòu)最大的特點。
do {
statement
} while(expression);
d. break語句和continue語句
- break語句用于跳出代碼塊或循環(huán)。
- continue語句用于立即終止本次循環(huán),開始下一次循環(huán)。
e. 標(biāo)簽(label)
不建議使用。
2.1.9 結(jié)尾的分號
a. 不使用分號結(jié)尾的語句
}結(jié)尾的語句不需要分號結(jié)束,如:for、while、if, switch, try、函數(shù)
因此,do...while需要分號結(jié)束
b. 分號的自動添加
JavaScript引擎在應(yīng)該寫卻沒寫的情況下,會自動添加分號。
但這難以預(yù)測,因此不應(yīng)省略該有的分號。
2.2 數(shù)據(jù)類型
2.2.1 概述
六個類別和兩個特殊值。
原始類型:
數(shù)值(number) //整數(shù)和小數(shù)
字符串(string)
布爾值(boolean)
合成類型:
對象(object)
數(shù)組(array)
函數(shù)(function)
兩個特殊值null和undefined
2.2.2 typeof運算符
JavaScript有三種方法,可以確定一個值到底是什么類型。
typeof運算符
instanceof運算符
Object.prototype.toString方法
- 原始類型分別返回number、string、boolean
- 函數(shù)返回function
- undefined返回undefined
利用這一點來檢查一個沒有聲明的變量,而不報錯。
if (typeof v === "undefined"){
// ...
}
- 除此以外,都返回object。
對象、數(shù)組、null
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
說明數(shù)組本質(zhì)上只是一種特殊的對象。
null的類型也是object,這是歷史原因造成的,它是一個類似于undefined的特殊值。
區(qū)分數(shù)組和對象
console.log(({}) instanceof Array); // false
console.log([] instanceof Array); // true
console.log(({}) instanceof Object); // true
console.log([] instanceof Object); // true
2.2.3 null和undefined
- 幾乎等價。
如:
console.log(undefined == null); // true
console.log(undefined === null); // false
大致可以區(qū)分:
null表示"沒有對象",即該處不應(yīng)該有值。
undefined表示"缺少值",即此處應(yīng)該有值,但是還未定義。null的特殊之處在于:typeof null 結(jié)果為Object
2.2.4 布爾值
如果JavaScript預(yù)期某個位置應(yīng)該是布爾值,會將該位置上現(xiàn)有的值自動轉(zhuǎn)為布爾值。
轉(zhuǎn)換規(guī)則是除了下面6個值被轉(zhuǎn)為false,其他值都視為true。
- undefined
- null
- false
- 0
- NaN
- ""(空字符串)
2.2.5 類型轉(zhuǎn)換
JavaScript是一種動態(tài)類型語言,變量是沒有類型的,可以隨時賦予任意值。但是,數(shù)據(jù)本身和各種運算是有類型的,因此運算時變量需要轉(zhuǎn)換類型。
a. 強制轉(zhuǎn)換
強制轉(zhuǎn)換主要指使用Number、String和Boolean三個構(gòu)造函數(shù),手動將各種類型的值,轉(zhuǎn)換成數(shù)字、字符串或者布爾值。
Number函數(shù)
1、原始類型:
- 數(shù)值:轉(zhuǎn)換后還是原來的值。
- 字符串:如果可以被解析為數(shù)值,則轉(zhuǎn)換為相應(yīng)的數(shù)值,否則得到NaN??兆址D(zhuǎn)為0。
- 布爾值:true轉(zhuǎn)成1,false轉(zhuǎn)成0。
- undefined:轉(zhuǎn)成NaN。 null:轉(zhuǎn)成0。
Number函數(shù)和parseInt函數(shù)的區(qū)別:
- parseInt逐個解析字符,而Number函數(shù)整體轉(zhuǎn)換字符串的類型。
- Number會忽略八進制的前導(dǎo)0,而parseInt不會。
- Number函數(shù)會自動過濾一個字符串前導(dǎo)和后綴的空格。
示例:
parseInt('011') // 9
parseInt('42 cats') // 42
parseInt('0xcafebabe') // 3405691582
Number('011') // 11
Number('42 cats') // NaN
Number('0xcafebabe') // 3405691582
2、對象
- 1.先調(diào)用對象自身的valueOf方法,如果該方法返回原始類型的值(數(shù)值、字符串和布爾值),則直接對該值使用Number方法,不再進行后續(xù)步驟。
- 2.如果valueOf方法返回復(fù)合類型的值,再調(diào)用對象自身的toString方法,如果toString方法返回原始類型的值,則對該值使用Number方法,不再進行后續(xù)步驟。
- 3.如果toString方法返回的是復(fù)合類型的值,則報錯。
String函數(shù)
1、原始類型:
- 數(shù)值:轉(zhuǎn)為相應(yīng)的字符串。
- 字符串:轉(zhuǎn)換后還是原來的值。
- 布爾值:true轉(zhuǎn)為“true”,false轉(zhuǎn)為“false”。
- undefined:轉(zhuǎn)為“undefined”。 null:轉(zhuǎn)為“null”。
2、對象:
- 先調(diào)用toString方法,如果toString方法返回的是原始類型的值,則對該值使用String方法,不再進行以下步驟。
- 如果toString方法返回的是復(fù)合類型的值,再調(diào)用valueOf方法,如果valueOf方法返回的是原始類型的值,則對該值使用String方法,不再進行以下步驟。
- 如果valueOf方法返回的是復(fù)合類型的值,則報錯。
String方法的這種過程正好與Number方法相反。
Boolean函數(shù)
1、原始類型:
以下六個值的轉(zhuǎn)化結(jié)果為false,其他的值全部為true。
- undefined
- null
- -0
- +0
- NaN
- ''(空字符串)
2、對象:
所有對象的布爾值都是true,甚至連false對應(yīng)的布爾對象也是true。
空對象{}和空數(shù)組[]也會被轉(zhuǎn)成true。
Boolean(new Boolean(false)) // true
Boolean([]) // true
Boolean({}) // true
b. 自動轉(zhuǎn)換
有時JavaScript會自動轉(zhuǎn)換數(shù)據(jù)類型,比如:
不同類型的數(shù)據(jù)進行互相運算;
對非布爾值類型的數(shù)據(jù)求布爾值,等。
1、自動轉(zhuǎn)換為布爾值
除了以下6個值,其他都是自動轉(zhuǎn)為true:
- undefined
- null
- -0
- +0
- NaN
- ''(空字符串)
:
if (!undefined && !null && !0 && !NaN && !''){
console.log('true');
}
// true
2、自動轉(zhuǎn)換為字符串
字符串的自動轉(zhuǎn)換,主要發(fā)生在加法運算時。
'5' + 1 // '51'
'5' + true // "5true"
'5' + {} // "5[object Object]"
'5' + [] // "5"
'5' + function (){} // "5function (){}"
'5' + undefined // "5undefined"
'5' + null // "5null"
3、自動轉(zhuǎn)換為數(shù)值
當(dāng)JavaScript遇到預(yù)期為數(shù)值的地方,就會將參數(shù)值自動轉(zhuǎn)換為數(shù)值,轉(zhuǎn)換規(guī)則與“強制轉(zhuǎn)換為數(shù)值”相同。
除了加法運算符有可能把運算子轉(zhuǎn)為字符串,其他運算符都會把兩側(cè)的運算子自動轉(zhuǎn)成數(shù)值。
'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
'1' - 1 // 0
'5' * [] // 0
false / '5' // 0
'abc' - 1 // NaN
JavaScript的兩個一元算術(shù)運算符也會把運算子自動轉(zhuǎn)為數(shù)值。
+'abc' // NaN
-'abc' // NaN
+true // 1
-false // 0
c. 加法運算符的類型轉(zhuǎn)化
加法運算符(+)需要特別討論,因為它可以完成兩種運算(加法和字符連接),所以不僅涉及到數(shù)據(jù)類型的轉(zhuǎn)換,還涉及到確定運算類型。
2、三種情況
- 運算子之中有字符串
則另一個被轉(zhuǎn)為字符串,然后執(zhí)行字符串連接運算。
- 運算子都為數(shù)值或布爾值,則執(zhí)行加法運算
布爾值轉(zhuǎn)為數(shù)值(true為1,false為0)。
true + 5 // 6
true + true // 2
- 運算子之中存在對象
太復(fù)雜,略。
2.3 運算符
2.3.1 算數(shù)運算符
加法運算符(Addition):x + y
加法運算符(+)需要注意的地方是,它除了用于數(shù)值的相加,還能用于字符串的連接。減法運算符(Subtraction): x - y
乘法運算符(Multiplication): x * y
除法運算符(Division):x / y
余數(shù)運算符(Remainder):x % y
運算結(jié)果的正負號由第一個運算子的正負號決定(有負數(shù)的話建議先使用絕對值)
12 % 5 // 2
-1 % 2 // -1
1 % -2 // 1
// 判斷偶數(shù)
function isOdd(n) {
return Math.abs(n % 2) === 1;
}
自增運算符(Increment):++x 或者 x++
自減運算符(Decrement):--x 或者 x--
求負運算符(Negate):-x
數(shù)值運算符(Convert to number): +x
作用是將任何值轉(zhuǎn)為數(shù)值(與Number函數(shù)作用相同)。
+true // 1
+[] // 0
+{} // NaN
2.3.2 賦值運算符
=、 += 、 -= 、 *= 、 /= 、 %= 、 >>= 、 <<= 、 >>>= 、 &= 、 |= 、 ^=
2.3.2 比較運算符
== 相等
=== 嚴格相等
!= 不相等
!== 嚴格不相等
< 小于
<= 小于或等于
大于
= 大于或等于
相等運算符(==)比較兩個“值”是否相等,嚴格相等運算符(===)比較它們是否為“同一個值”。
兩者的一個重要區(qū)別是,如果兩個值不是同一類型,嚴格相等運算符(===)直接返回false,而相等運算符(==)會將它們轉(zhuǎn)化成同一個類型,再用嚴格相等運算符進行比較。
我們先看嚴格相等運算符。
嚴格相等運算符
1、不同類型的值
如果兩個值的類型不同,直接返回false。
2、同一類的原始類型值
同一類型的原始類型的值(數(shù)值、字符串、布爾值)比較時,值相同就返回true,值不同就返回false。
1 === 0x1 // true
- NaN與任何值都不相等(包括自身)。
- 正0等于負0。
3、同一類的復(fù)合類型值
復(fù)合類型(對象、數(shù)組、函數(shù))的數(shù)據(jù)比較時,是比較它們是否指向同一個對象(內(nèi)存地址)。
({}) === {} // false
[] === [] // false
(function (){}) === function (){} // false
var v1 = {};
var v2 = v1;
v1 === v2 // true
4、undefined和null
undefined 和 null 與自身嚴格相等。
undefined === undefined // true
null === null // true
由于變量聲明后默認值是undefined,因此兩個只聲明未賦值的變量是相等的。
var v1;
var v2;
v1 === v2 // true
相等運算符
相等運算符在比較相同類型的數(shù)據(jù)時,與嚴格相等運算符完全一樣。
在比較不同類型的數(shù)據(jù)時,相等運算符會先將數(shù)據(jù)進行類型轉(zhuǎn)換,然后再用嚴格相等運算符比較。類型轉(zhuǎn)換規(guī)則如下:
1、原始類型的值
原始類型的數(shù)據(jù)會轉(zhuǎn)換成數(shù)值類型再進行比較。
1 == true // true
"true" == true // false
'' == 0 // true
'1' == true // true
// 注意:字符串轉(zhuǎn)為數(shù)字時,省略前置和后置的空格
'\n 123 \t' == 123 // true
' 123 ' == 123 // true
' 123' == 123 // false
2、對象與原始類型值比較
對象(這里指廣義的對象,包括數(shù)值和函數(shù))與原始類型的值比較時,對象轉(zhuǎn)化成原始類型的值,再進行比較。
[1] == 1 // true
[1] == "1" // true
[1] == true // true
3、undefined和null
undefined和null與其他類型的值比較時,結(jié)果都為false,它們互相比較時結(jié)果為true。
false == null // false
0 == null // false
undefined == null // true
4、相等運算符的缺點
相等運算符隱藏的類型轉(zhuǎn)換,會帶來一些違反直覺的結(jié)果。
'' == '0' // false
0 == '' // true
0 == '0' // true
false == 'false' // false
false == '0' // true
false == undefined // false
false == null // false
null == undefined // true
' \t\r\n ' == 0 // true
上面這些表達式都很容易出錯,因此不要使用相等運算符(==),最好只使用嚴格相等運算符(===)。
布爾運算符
布爾運算符用于將表達式轉(zhuǎn)為布爾值。
1、取反運算符(!)
對于非布爾值的數(shù)據(jù),取反運算符會自動將其轉(zhuǎn)為布爾值。規(guī)則是,以下六個值取反后為true,其他值取反后都為false。
undefined
null
false
0(包括+0和-0)
NaN
空字符串("")
兩次取反就是將一個值轉(zhuǎn)為布爾值的簡便寫法。
!!x
// 等同于
Boolean(x)
2、且運算符(&&)
“短路”機制
3、或運算符(||)
4、三元條件運算符( ? :)
"t" ? true : false // true
5、位運算符
5.1 簡介
或運算(or)|
與運算(or)&
否運算(not)~
異或運算(xor)?
左移運算(left shift)<<
右移運算(right shift)>>
帶符號位的右移運算(zero filled right shift)>>>
略。
其他運算符
1、圓括號運算符() 表示求值:
有2種用法:
把表達式放在圓括號之中,作用是求值;
跟在函數(shù)的后面,作用是調(diào)用函數(shù)。
(2) // 2
function f(){return 1;}
(f) // function f(){return 1;}
f() // 1
// 語句沒有返回值所以不能放在括號里
(var a =1) // SyntaxError: Unexpected token var
2、void運算符 作用是執(zhí)行一個表達式,然后返回undefined。
void (0) // undefined
這個運算符主要是用在超級鏈接中插入代碼,目的是返回undefined可以防止網(wǎng)頁跳轉(zhuǎn)。
javascript:void window.open("http://example.com/")
比如:
<a href="#" onclick="f();return false;">文字</a>
void運算符可以取代上面兩種寫法。
<a href="javascript:void(0)" onclick="f();">文字</a>
3、逗號運算符
逗號運算符用于對兩個表達式求值,并返回后一個表達式的值。
"a", "b" // "b"
var x = 0;
var y = (x++, 10);
x // 1
y // 10
運算順序
運算符是有優(yōu)先級的
圓括號的優(yōu)先級最高(建議總是使用圓括號)
左結(jié)合與右結(jié)合
大部分運算符是左結(jié)合,
少數(shù)是右結(jié)合,最主要的是:=和三元運算符(?:)。
w = x = y = z;
q = a?b:c?d:e?f:g;
相當(dāng)于:
w = (x = (y = z));
q = a?b:(c?d:(e?f:g));
2.4 數(shù)值
數(shù)字都以64位浮點數(shù)形式儲存。
2.4.1 精度和取值范圍
整數(shù)在絕對值<=253(Math.pow(2, 53))內(nèi)的都可以精確表示。
浮點數(shù)取值范圍在21024到2-1024(開區(qū)間)之間。
由于浮點數(shù)不是精確的值,所以涉及小數(shù)的比較和運算要特別小心。
0.1 + 0.2 === 0.3
// false
0.3 / 0.1
// 2.9999999999999996
2.4.2 數(shù)值的表示法
可以用字面形式直接表示,也可以采用科學(xué)計數(shù)法表示。
123e3 // 123000
123e-3 // 0.123
正常情況下,所有數(shù)值都為十進制。
十六進制必須以0x或0X開頭。
八進制數(shù)必須以0開頭(八進制很容易造成混亂)。
2.4.3 特殊數(shù)值
1、零
JavaScript提供零的3種寫法:0、+0、-0。它們是等價的。
但是,如果正零和負零分別當(dāng)作分母,它們返回的值是不相等的。
(1/+0) === (1/-0) // false
因為除以正零得到+Infinity,除以負零得到-Infinity,這兩者是不相等的(關(guān)于Infinity詳見后文)。
2、NaN
NaN表示“非數(shù)字”(Not a Number)。
NaN不等于任何值,包括它本身。
NaN與任何數(shù)(包括它自己)的運算,得到的都是NaN。
isNaN函數(shù)可以用來判斷一個值是否為NaN。
但是,isNaN只對數(shù)值有效,如果傳入其他值,會被先轉(zhuǎn)成數(shù)值。也就是說,isNaN為true的值,有可能不是NaN,而是一個字符串。
isNaN("Hello") // true
因此,使用isNaN之前,最好判斷一下數(shù)據(jù)類型。
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
或者利用NaN是唯一不等于自身的值這個特點,進行判斷。
function myIsNaN(value) {
return value !== value;
}
3、Infinity
Infinity表示“無窮”。除了0除以0得到NaN,其他任意數(shù)除以0,得到Infinity。
1 / -0 // -Infinity
1 / +0 // Infinity
運算結(jié)果超出JavaScript可接受范圍,也會返回?zé)o窮。
JavaScript的數(shù)學(xué)運算幾乎沒有可能拋出錯誤。
isFinite函數(shù)返回一個布爾值,檢查某個值是否為正常值,而不是Infinity。
isFinite(Infinity) // false
isFinite(NaN) // false
2.4.4 與數(shù)值相關(guān)的全局方法
1、parseInt
將字符串或小數(shù)轉(zhuǎn)化為整數(shù)。字符串頭部空格會被自動去除。一旦遇到不能轉(zhuǎn)化的字符就停止繼續(xù)轉(zhuǎn)化。
如果字符串的第一個字符不能轉(zhuǎn)化為數(shù)字(正負號除外),返回NaN。
parseInt(' 1a23') // 1
parseInt("abc") // NaN
parseInt(".3") // NaN
parseInt還可以接受第二個參數(shù)(2到36之間),表示被解析的值的進制。這意味著,可以用parseInt方法進行進制的轉(zhuǎn)換。
parseInt(1000, 2) // 8
2、parseFloat
將字符串轉(zhuǎn)為浮點數(shù)。同樣,字符串頭部空格會被自動去除。一旦遇到不能轉(zhuǎn)化的字符就停止繼續(xù)轉(zhuǎn)化。
如果字符串的第一個字符不能轉(zhuǎn)化為浮點數(shù),返回NaN。
parseFloat(" 3.14"); // 3.14
parseFloat("a2") // NaN
parseFloat("") // NaN
parseFloat將空字符串轉(zhuǎn)為NaN。
parseFloat的轉(zhuǎn)換結(jié)果不同于Number函數(shù)。
parseFloat(true) // NaN
Number(true) // 1
parseFloat(null) // NaN
Number(null) // 0
parseFloat('') // NaN
Number('') // 0
parseFloat('123.45#') // 123.45
Number('123.45#') // NaN
2.5 字符串
2.5.1 概述
字符串默認只能寫在一行內(nèi),如果必須分成多行,可以在每一行的尾部使用反斜杠(ECMAScript 5新添加),而且反斜杠的后面必須是換行符,不能有其他字符(比如空格)。
var longString = "Long \
string";
連接運算符(+)可以連接多個單行字符串。
var longString = "Long " + "string";
轉(zhuǎn)義
\n 換行符
\r 回車鍵
\t 制表符
\' 單引號
\" 雙引號
\\ 反斜杠
字符串與數(shù)組
字符串可以被視為字符數(shù)組,因此可以使用數(shù)組的方括號運算符。
'hello'[0] // "h"
'hello'[5] // undefined
但是,實際上,字符串是類似數(shù)組的對象,且無法改變字符串之中的單個字符。length屬性也是無法改變的。
var s = 'hello';
delete s[0];
s[1] = 'a';
s.length = 3;
s // "hello"
字符串內(nèi)部的單個字符無法改變和增刪,這些操作會默默地失敗。字符串的length也無法改變,但不會報錯。
字符串也無法添加新屬性:
var s = "Hello World";
s.x = 123;
s.x // undefined
2.5.2 字符集
JavaScript內(nèi)部所有字符都用Unicode表示。所有字符都可以寫成"\uxxxx"的形式。
var s = '\u00A9';
s // "?"
注意:UTF-16有兩種長度:對于U+0000到U+FFFF之間的字符,長度為16位(即2個字節(jié));對于U+10000到U+10FFFF之間的字符,長度為32位(即4個字節(jié))。
var s = "\uD834\uDF06"
s // "??"
s.length // 2
s.charAt(0) // ""
s.charCodeAt(0) // 55348
由于JavaScript引擎(ES5)不能自動識別編號大于0xFFFF的Unicode字符,導(dǎo)致所有字符串處理函數(shù)遇到這類字符,都會產(chǎn)生錯誤的結(jié)果,所以必須判斷字符是否落在0xD800到0xDFFF這個區(qū)間。
下面是能夠正確處理字符串遍歷的函數(shù):
function getSymbols(string) {
var length = string.length;
var index = -1;
var output = [];
var character;
var charCode;
while (++index < length) {
character = string.charAt(index);
charCode = character.charCodeAt(0);
if (charCode >= 0xD800 && charCode <= 0xDBFF) {
output.push(character + string.charAt(++index));
} else {
output.push(character);
}
}
return output;
}
替換(String.prototype.replace)、截取子字符串(String.prototype.substring, String.prototype.slice)等其他字符串操作,都必須做類似的處理。
2.5.3 Base64轉(zhuǎn)碼
window.btoa("Hello World")
// "SGVsbG8gV29ybGQ="
window.atob("SGVsbG8gV29ybGQ=")
// "Hello World"
這兩個方法不適合非ASCII碼的字符,瀏覽器會報錯。
window.btoa('你好')
// InvalidCharacterError: An invalid or illegal character was specified, such as in an XML name.
要將非ASCII碼字符轉(zhuǎn)為Base64編碼,必須中間插入一個瀏覽器轉(zhuǎn)碼的環(huán)節(jié),再使用這兩個方法。
function b64Encode( str ) {
return window.btoa(unescape(encodeURIComponent( str )));
}
function b64Decode( str ) {
return decodeURIComponent(escape(window.atob( str )));
}
// 使用方法
b64Encode('你好') // "5L2g5aW9"
b64Decode('5L2g5aW9') // "你好"
2.6 對象
2.6.1 概述
所謂對象,就是一種無序的數(shù)據(jù)集合,由若干個“鍵值對”(key-value)構(gòu)成。
JavaScript的所有數(shù)據(jù)都可以被視為對象。
var o = {
id: 123,
"na me": "Hello"
};
鍵名加不加引號都可以。但是,如果鍵名不符合標(biāo)識名的條件(比如包含數(shù)字、字母、下劃線以外的字符,或者第一個字符為數(shù)字),也不是正整數(shù),則必須加上引號。
如果一個屬性的值為函數(shù),通常把這個屬性稱為“方法”。
2.6.1.1 生成方法
var o1 = {};
var o2 = new Object();
var o3 = Object.create(null);
2.6.1.2 讀取屬性
有兩種方法,一種是使用點運算符,還有一種是使用方括號運算符。
var o = {
id: "Hello",
0.7: "World"
};
o.id // "Hello"
o["id"] // "Hello"
o["i" + "d"] // "Hello"
o["0.7"] // "World"
o[0.7] // "World"
注意,如果使用方括號運算符,鍵名必須放在引號里面,否則會被當(dāng)作變量處理。但是,數(shù)字鍵可以不加引號,因為會被當(dāng)作字符串處理。
2.6.1.3 檢查變量是否聲明
讀取一個不存在的鍵,會返回undefined,而不是報錯。
可以使用in運算符判斷屬性是否存在(也可以判斷變量是否聲明)。
"id" in o // true
0.8 in o // false
o in window // true
在瀏覽器環(huán)境,所有全局變量都是window對象的屬性。
2.6.1.4 寫入屬性
同樣使用:點運算符和方括號運算符。
o.p = "abc";
o["p"] = "abc";
JavaScript允許屬性的“后綁定”,也就是說,你可以在任意時刻新增屬性,沒必要在定義對象的時候,就定義好屬性。
var o = { p:1 };
// 等價于
var o = {};
o.p = 1;
2.6.1.5 查看所有屬性(Object.keys)
Object.keys(o);
// ["id", "0.8"]
2.6.1.6 屬性的刪除(delete)
delete o.p // true
o.p // undefined
一旦使用delete命令刪除某個屬性,再讀取該屬性就會返回undefined,而且Object.keys方法返回的該對象的所有屬性中,也將不再包括該屬性。
注意:如果刪除一個不存在的屬性,delete不報錯,而且返回true。
只有一種情況,delete命令會返回false,那就是該屬性存在,且不得刪除。
var o = Object.defineProperty({}, "p", {
value: 123,
configurable: false
});
o.p // 123
delete o.p // false
另外,需要注意的是,delete命令只能刪除對象本身的屬性,不能刪除繼承的屬性。delete命令也不能刪除var命令聲明的變量,只能用來刪除對象的屬性和方法。
2.6.1.7 對象的引用
如果不同的變量名指向同一個對象,那么它們都是這個對象的引用,都指向同一個內(nèi)存地址。修改其中一個變量,會影響到其他所有變量。
var o1 = {};
var o2 = o1;
o1.a = 1;
o2.a // 1
這種引用只局限于對象,對于原始類型的數(shù)據(jù)則是值的拷貝。
2.6.1.8 in運算符
in運算符用于檢查對象是否包含某個屬性(注意,檢查的是鍵名,不是鍵值)。
var o = { p: 1 };
'p' in o // true
該運算符對數(shù)組也適用。
var a = ["hello", "world"];
0 in a // true
'0' in a // true
在JavaScript語言中,所有全局變量都是頂層對象(瀏覽器環(huán)境的頂層對象為window)的屬性,因此可以用in運算符判斷一個全局變量是否存在。
if ('x' in window) { return 1; }
in運算符對繼承的屬性也有效。
var o = new Object();
o.hasOwnProperty('toString') // false
'toString' in o // true
in運算符只能用來檢驗可枚舉(enumerable)的屬性。
2.6.1.9 for...in循環(huán)
var o = {a:1, b:2, c:3};
for (i in o){
console.log(o[i]);
}
for...in循環(huán)遍歷的是對象所有可enumberable的屬性,其中不僅包括定義在對象本身的屬性,還包括對象繼承的屬性。
如果只想遍歷對象本身的屬性,可以使用hasOwnProperty方法做一個判斷。
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}
2.6.2 類似數(shù)組的對象
在js中,有些對象被稱為“類似數(shù)組的對象”,它們可以使用length屬性,但是它們并不是數(shù)組,無法使用一些數(shù)組的方法。
var a = {
0:'a',
1:'b',
length:2
};
a[0] // 'a'
a.length // 3
變量a就是一個類似數(shù)組的對象,但它無法使用數(shù)組特有的一些方法,比如pop和push方法。而且,length屬性不是動態(tài)值。
典型的類似數(shù)組的對象是:函數(shù)的arguments對象,以及大多數(shù)DOM元素集,還有字符串。
// arguments對象
function args() { for(var i=0;i<arguments.length;i++) alert(arguments[i]); }
args('a', 'b');
// DOM元素集
var elts = document.getElementsByTagName('h3');
// 字符串
'abc'[1] // 'b'
'abc' instanceof Array // false
通過函數(shù)的call方法,可以用slice方法將類似數(shù)組的對象,變成真正的數(shù)組。
var arr = Array.prototype.slice.call("abc");
遍歷類似數(shù)組的對象,可以采用for循環(huán),也可以采用數(shù)組的forEach方法。
// for循環(huán)
for(var i=0;i<"abc".length;i++){
console.log(i+'. '+"abc"[i]);
};
// forEach方法
Array.prototype.forEach.call("abc", function (elem, i) {
console.log(i+'. '+elem);
});
2.6.3 with語句(不建議使用)
with語句的作用是操作同一個對象的多個屬性時,提供一些書寫的方便。
o.p1 = 1;
o.p2 = 2;
// 等同于
with (o){
p1 = 1;
p2 = 2;
}
注意:在with區(qū)塊內(nèi)部依然是全局作用域。這意味著,如果你要在with語句內(nèi)部,賦值對象某個屬性,這個屬性必須已經(jīng)存在,否則你就是聲明了一個全局變量(綁定對象不明確)。
2.7 數(shù)組
2.7.1 數(shù)組的定義
數(shù)組(array)是按次序排列的一組值,單個值稱為元素。
- 編號從0開始。
- 可變長??上榷x再賦值。
- 元素可以是任何類型。
例如:
var arr = [];
arr[0] = {a:1}; // 對象
arr[1] = [1,2,3]; // 數(shù)組
arr[2] = function (){return true;}; // 函數(shù)
如果數(shù)組的元素還是數(shù)組,就形成了多維數(shù)組。
var a = [[1,2],[3,4],"x"];
console.log(a[0][0]);
console.log(a[2]);
2.7.2 數(shù)組與對象的關(guān)系
本質(zhì)上,數(shù)組也屬于對象。
typeof [1,2,3] // "object"
上面代碼表明,數(shù)組只是一種特殊的對象,所以typeof運算符返回數(shù)組的類型是object。
數(shù)組是字典結(jié)構(gòu)的一個變種,它的鍵是0開始的整數(shù)。
var arr = ['a', 'b', 'c'];
arr['0'] // 'a'
arr[0] // 'a'
上面代碼分別用數(shù)值和字符串作為鍵名,結(jié)果都能讀取數(shù)組。
需要注意的是,這一條在賦值時也成立,即如果一個值可以被轉(zhuǎn)換為整數(shù),則以該值為鍵名,等于以對應(yīng)的整數(shù)為鍵名。
var a = [];
a[0] = 0;
a['1000'] = 'abc';
console.log(a.length); // 1001
對于數(shù)字的鍵名,不能使用點結(jié)構(gòu),所以arr.0的寫法不合法。因此數(shù)組只能用方括號arr[0]表示。
2.7.3 length屬性
- 數(shù)組的length是一個動態(tài)的值。它是最大鍵值+1.
- length屬性是可寫的。設(shè)置length可以改變數(shù)組的大小,空位都填充為undefined。
如果設(shè)置length為不合法的值,JavaScript會報錯。 - 可以為數(shù)組添加屬性(因為數(shù)組本質(zhì)上是對象),但這不影響length的值
示例:
var a = [];
a[1] = 'abc';
a['10'] = 'abc'; //a.length是10
a.length=3; //a.length是3
console.log(a); // [undefined, "abc", undefined]
a.id="ddd"; //a.length是3
[].length = -1 // RangeError: Invalid array length
[].length = 'abc' // RangeError: Invalid array length
2.7.4 數(shù)組的空位
delete命令可以刪除值,形成空位。
注意:使用delete命令刪除值,不影響length屬性。空位通過空值生成,還是通過顯式設(shè)為undefined生成,有細微的差別。
如果通過空值生成,使用數(shù)組的forEach方法或者for...in結(jié)構(gòu)進行遍歷,空位就會被跳過。
var a = [0,1,,3,undefined];
delete a[1];
console.log(a.length); // 5
a.forEach(function (x, i) { console.log(x) }) // 0,3,undefined
for (var i in a){console.log(a[i])} // 0,3,undefined
2.7.5 in運算符,for...in循環(huán)
- in可以堅持是否存在某個鍵
檢查某個鍵是否存在的運算符in,適用于對象,也適用于數(shù)組。 - for-in循環(huán),可以遍歷數(shù)組的所有元素
注意:for-in會遍歷數(shù)組所有的鍵,即使是非數(shù)字鍵。 - 還可以用for或while遍歷數(shù)組
示例:
var a = [0,,undefined];
console.log(0 in a); // true
console.log(1 in a); // false
console.log(2 in a); // true
a.id = function (){};
for (var i in a){console.log(a[i])} // 0,undefined,function (){}
for(var i = 0; i < a.length; i++){console.log(a[i]);} // 0,undefined,undefined
2.7.6 Array構(gòu)造函數(shù)
數(shù)組還可以使用JavaScript內(nèi)置的Array構(gòu)造函數(shù)創(chuàng)建。
var a = new Array(2);
console.log(a); // [undefined, undefined]
var a = new Array(1,2);
console.log(a); // [1, 2]
2.8 函數(shù)
2.8.1 概述
2.8.1.1 函數(shù)的聲明
1、function命令聲明函數(shù)
function print(){
// ...
}
2、函數(shù)表達式
var print = function (){
// ...
};
采用函數(shù)表達式聲明函數(shù)時,function命令后面不帶有函數(shù)名。
如果加上函數(shù)名,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效。
3、Function構(gòu)造函數(shù)
var add = new Function("x","y","return (x+y)");
最后一個參數(shù)是add函數(shù)的“函數(shù)體”,其他參數(shù)都是add函數(shù)的參數(shù)。
4、函數(shù)的重復(fù)聲明
重復(fù)聲明同一個函數(shù),則后面的聲明會覆蓋前面的聲明。
由于存在函數(shù)名的提升,所以前面的函數(shù)聲明完全是無效的。
2.8.1.2 圓括號運算符和return語句
調(diào)用函數(shù)時,要使用圓括號運算符。圓括號之中,可以加入函數(shù)的參數(shù)。
參數(shù)數(shù)量可以不匹配!
function add(x,y) {
return x+y;
}
console.log(add(1,2,3)) // 3
console.log(add(1)) // NaN
return語句不是必需的,如果沒有的話,該函數(shù)就不返回任何值,或者說返回undefined。
函數(shù)調(diào)用自身,這就是遞歸。
2.8.1.3 第一等公民
JavaScript的函數(shù)與其他數(shù)據(jù)類型處于同等地位,可以使用其他數(shù)據(jù)類型的地方就能使用函數(shù)。
比如,可以把函數(shù)賦值給變量和對象的屬性,也可以當(dāng)作參數(shù)傳入其他函數(shù),或者作為函數(shù)的結(jié)果返回。
這表示函數(shù)與其他數(shù)據(jù)類型的地方是平等,所以又稱函數(shù)為第一等公民。
2.8.1.4 函數(shù)名的提升
JavaScript引擎將函數(shù)名視同變量名,所以采用function命令聲明函數(shù)時,整個函數(shù)會被提升到代碼頭部。
f();
var f = function (){ alert("aa";) };
但是,如果采用賦值語句定義函數(shù)就會報錯,因為提升的只是賦值語句左邊的變量,函數(shù)并未被提升。
f();
var f = function (){}; // TypeError: undefined is not a function
2.8.1.5 不能在條件語句中聲明函數(shù)
不得在非函數(shù)的代碼塊中聲明函數(shù),最常見的情況就是if和try語句。
按照語言規(guī)范,這是不合法的。但是,實際情況是各家瀏覽器往往并不報錯,能夠運行。
2.8.1.6 name屬性
大多數(shù)JavaScript引擎支持非標(biāo)準(zhǔn)的name屬性。該屬性返回緊跟在function關(guān)鍵字之后的那個函數(shù)名。
function f1() {}
f1.name // 'f1'
var f2 = function () {};
f2.name // ''
2.8.2 函數(shù)作用域
2.8.2.1 定義
Javascript只有兩種作用域:一種是全局作用域,變量在整個程序中一直存在;另一種是函數(shù)作用域,變量只在函數(shù)內(nèi)部存在。
函數(shù)內(nèi)部定義的變量,會在該作用域內(nèi)覆蓋同名全局變量。
var w = 1;
var w2 = 1;
(function f(){
w=2;
var w2 = 2;
var n = 1;
})();
console.log(w); // 2
console.log(w2); // 1
console.log(n); // ReferenceError: n is not defined
2.8.2.2 函數(shù)內(nèi)部的變量提升
與全局作用域一樣,函數(shù)作用域內(nèi)部也會產(chǎn)生“變量提升”現(xiàn)象。var命令聲明的變量,不管在什么位置,變量聲明都會被提升到函數(shù)體的頭部。
2.8.3 參數(shù)
2.8.3.1 概述
函數(shù)可以定義參數(shù)。
函數(shù)對象的length屬性,返回函數(shù)定義中參數(shù)的個數(shù)。
function f(a,b) {}
f.length // 2
2.8.3.2 參數(shù)的省略
函數(shù)調(diào)用時,提供多少參數(shù)都不會報錯。被省略的參數(shù)的值就變?yōu)閡ndefined。多的廢棄。
沒有辦法只省略靠前的參數(shù),想那樣只有顯式傳入undefined。
f(undefined,1)
2.8.3.3 默認值
如果要為參數(shù)設(shè)置默認值,可以使用方法:
function f(a){
(a !== undefined && a != null)?(a = a):(a = 1);
return a;
}
console.log(f('')); // ""
console.log(f(0)); // 0
console.log(f()); // 1
console.log(f(null)); // 1
因為undefined、0、空字符、null等的布爾值都是false。
2.8.3.4 傳值方式
js的函數(shù)參數(shù)傳遞方式是值傳遞,這意味著,在函數(shù)體內(nèi)修改參數(shù)值,不會影響到函數(shù)外部。
但是:對于復(fù)合類型的變量來說,屬性值是通過地址讀取的。所以在函數(shù)體內(nèi)修改復(fù)合類型變量的屬性值,會影響到函數(shù)外部。
var o = { p:1 };
function f(obj){
obj.p = 2;
}
f(o);
o.p // 2
var a = [1,2,3];
function f(a){
a[0]=4;
}
f(a);
a // [4,2,3]
如果需要對某個變量達到按址傳遞的效果,可以將它寫成全局對象的屬性。
var a = 1;
var b = 2;
function f(x,y){
x *= 10;
window[y] *= 10;
}
f(a,'b'); // 注意:第二個參數(shù)不能是b,這樣的話函數(shù)f內(nèi)會去找window["2"]這個屬性。
console.log(a); // 1
console.log(b); // 20
2.8.3.5 同名參數(shù)
如果有同名的參數(shù),則取最后出現(xiàn)的那個值。
但是用arguments可以取到正確的參數(shù)。
f(1,2);
function f(a, a){
console.log(a+a); //4
console.log(arguments[0] + arguments[1]); //3
}
2.8.3.6 arguments對象
- arguments可以在函數(shù)體內(nèi)部讀取所有參數(shù)
也可設(shè)置,嚴格模式不允許這種用法。 - arguments對象的length屬性,表示函數(shù)調(diào)用時到底帶幾個參數(shù)
- arguments對象的callee屬性,返回它所對應(yīng)的原函數(shù)
(注意:不是那個指向函數(shù)的變量)。
示例:
var f = function f2(a) {
console.log(arguments[0]); // a
console.log(arguments[1]); // b
console.log(arguments.length); // 3
console.log(arguments.callee === f); // true
console.log(arguments.callee === f2); // true
};
f("a","b","c");
2.8.4 函數(shù)的其他知識點
2.8.4.1 閉包
閉包是函數(shù)與其生成時所在的作用域?qū)ο蟮囊环N結(jié)合。
閉包可以讀取函數(shù)內(nèi)部變量,還可以使得內(nèi)部變量記住上一次調(diào)用時的運算結(jié)果。
function f(start) {
return function () {
return start++;
}
}
var inc = f(5);
console.log(inc()); // 5
console.log(inc()); // 6
console.log(inc()); // 7
2.8.4.2 立即調(diào)用的函數(shù)表達式(IIFE)
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();
這兩種寫法都是以圓括號開頭,引擎就會認為后面跟的是一個表示式,而不是函數(shù)定義。
2.8.5 eval命令
eval命令的作用是,將字符串當(dāng)作語句執(zhí)行。
放在eval中的字符串,應(yīng)該有獨自存在的意義,不能用來與eval以外的命令配合使用。
eval('var a = 1;');
eval('return;'); //報錯
2.9 錯誤處理機制
2.9.1 Error對象js
一旦發(fā)生錯誤,js引擎會拋出一個Error對象的實例,然后程序就此中斷。
Error對象有3個基本的屬性:name、message、stack。
(function catchit() {
try {
throw new Error('出錯了.'); // Error
} catch(e) {
console.log(e.name); // Error
console.log(e.message); //出錯了
console.log(e.stack); // print stack trace
}
})();
2.9.2 JavaScript的原生錯誤類型
Error對象是最一般的錯誤類型,還存在Error的6個派生對象。
- SyntaxError是解析代碼時發(fā)生的語法錯誤。
- ReferenceError是引用一個不存在的變量時發(fā)生的錯誤。
- TypeError是變量或參數(shù)不是預(yù)期類型時發(fā)生的錯誤。
比如,對字符串、布爾值、數(shù)值等原始類型的值使用new命令。因為new命令的參數(shù)應(yīng)該是一個構(gòu)造函數(shù)。 - RangeError是當(dāng)一個值超出有效范圍時發(fā)生的錯誤。
主要有:一是數(shù)組長度為負數(shù),二是Number對象的方法參數(shù)超出范圍 - URIError是URI相關(guān)函數(shù)的參數(shù)不正確時拋出的錯誤,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()這六個函數(shù)。
- EvalError是eval函數(shù)沒有被正確執(zhí)行時,會拋出EvalError錯誤。
示例:
var 1a; // SyntaxError 變量名錯誤
new Array(-1) // RangeError 數(shù)組長度為負
new 123 // TypeError 123不是函數(shù)
decodeURI('%2') // URIError
2.9.3 自定義錯誤
function UserError(message) {
this.message = message || "默認信息";
this.name = "UserError";
}
UserError.prototype = new Error();
UserError.prototype.constructor = UserError;
自定義一個錯誤對象UserError,讓它繼承Error對象。然:
new UserError("這是自定義的錯誤!");
2.9.4 throw語句
throw語句的作用是中斷程序執(zhí)行,拋出一個錯誤。它接受一個表達式作為參數(shù)。
throw "Error!";
2.9.5 try...catch結(jié)構(gòu)和finally代碼塊
try {
throw new Error('出錯了!');
} catch (e) {
console.log(e.name + ": " + e.message);
console.log(e.stack);
}finally {
console.log('無論是否出錯都執(zhí)行');
}
甚至可以省略catch代碼塊,只使用finally代碼塊。
try {
....
} finally {
console.log('無論是否出錯都執(zhí)行');
}