詞法結構
- JavaScript程序是用Unicode字符集編寫的
- JavaScript是區分大小寫的, 而HTML不區分大小寫.
- JavaScript會忽略程序中標識之間的空格.
- JavaScript支持"http://"和"/.../"兩種注釋.
- 直接量: 程序中直接使用的數據值. JavaScript中的直接量為: 數值, 字符串, 布爾型和正則表達式.
- JavaScript的標識符必須以字母, 下劃線或者美元符號開始, 后續跟字母, 數字, 下劃線或者美元符號.
- JavaScript中關于填補分號的規則: 只要在缺少了分號就無法正確解析代碼的時候, JavaScript才會填補分號.
類型, 值和變量
概述
- JavaScript的數據類型分為兩類: 原始類型和對象類型. JavaScript的原始類型包括數字, 字符串和布爾值. 而對象是屬性的集合, 每個屬性都由"名/值對"組成.
- JavaScript中有兩個特殊的原始值:** null和undefined**, 它們不是數字,字符串和布爾值. 它們通常分別代表了各自特殊類型的唯一的成員. 原始類型, null和undefined均為不可變類型.
- 普通的JavaScript對象是"命名值"的無序集合, 可以使用Object.keys()獲取其keys(新版本可以使用Object.values()獲取其values). 而JavaScript定義了一種有序集合, 為數組.
var o={
name:'張三',
sex:'男',
sayHello:function(){
conosle.log("sayHello");
}
};
console.log(Object.keys(o));
console.log(Object.values(o));
結果:
Array [ "name", "sex", "sayHello" ]
Array [ "張三", "男", o.sayHello() ]
- JavaScript定義了另一種特殊對象--函數. 函數是具有與它相關聯的可執行代碼的對象, 通過調用函數來運行可執行代碼, 并返回運算結果.
- 如果函數用來初始化(使用new運算符)一個新建的對象, 我們稱之為構造函數. 每個構造函數定義了一類對象--由構造函數初始化的對象組成. 類可以看做是對象類型的子類型. 除了數組類和函數類之外, JavaScript語言核心還定義了三種有用的類: 日期(Date)類定義了代表日期的對象. 正則(RegExp)類定義了表示正則表達式的對象. 錯誤(Error)類定義了那些表示JavaScript程序中運行時錯誤和語法錯誤的對象.
- JavaScript解釋器有自己的內存管理機制, 可以自動對內存進行垃圾回收.
- JavaScript變量是無類型的, 變量可以被賦予任何類型的值. 使用var關鍵字來聲明變量, JavaScript采用詞法作用域, 不在任何函數內聲明的變量稱作全局變量, 它在JavaScript程序中的任何地方都是可見的. 在函數內聲明的變量具有函數作用域, 并且只在函數內可見.
數字
- JavaScript不區分整數值和浮點數值,** 所有數字均以浮點數值表示.**
- JavaScript中除以0并不報錯, 只是返回無窮大(Infinity)/負無窮大(-Infinity). 而0 / 0是沒有任何意義的, 所以用NaN表示(NaN代表非數字, 可用isNaN()判斷, 例如isNaN("hello")為true).
- JavaScript中浮點數依舊存在精度問題:(不能去比較浮點數是否相等)
var x=0.3-0.2;
var y=0.2-0.1;
console.log(x);//0.09999999999999998
console.log(y);//0.1
x == y -->false
x == 0.1-->false
y == 0.1-->true
0.07*100-->7.000000000000001
文本
轉譯字符
轉義字符 | 含義
----|------|----
\o | NUL字符
\b | 退格符
\t| 水平制表符
\n| 換行符
\v| 垂直制表符
\f| 換頁符
\r| 回車符
"| 雙引號
'| 單引號
\|反斜線
\xXX| 由兩位十六進制數xx指定的Latin-1字符
\uXXXX| 由四位十六進制XXXX指定的Unicode字符
字符串的使用
var s = "hello world"
s.charAt(0) ==> "h": 第一個字符
s.charAt(s.length - 1) ==> "d": 最后一個字符
s.substring(1, 4) ==> "ell": 第2~4個字符
s.slice(1, 4) ==> "ell": 同上
s.slice(-3) ==> "rld": 最后三個字符
s.indexOf("l") ==> 2: 字符l首次出現的位置
s.lastIndexOf("l") ==> 9: 字符l最后出現的位置
s.indexOf("l", 3) ==> 3: 在位置3及之后首次出現字符l的位置.
s.split(" ") ==> ["hello", "world"]: 分割字符串
s.replace("h", "H") ==> "Hello world": 替換
s.toUpperCase() ==> "HELLO WORLD": 轉換成大寫
布爾值
轉換為false的變量: undefined, null, NaN, 0, -0, ""
null和undefined
null代表空指針, 通常用來表示數字, 字符串和對象的"無值狀態"
undefined代表未初始化.
全局對象
全局屬性: 比如undefined, Infinity和NaN
全局函數: 比如isNaN(), parseInt()和eval()
構造函數: 比如Date(), RegExp(), String(), Object()和Array()
全局對象: 比如Math和JSON
包裝對象
對于字符串, 以下代碼是正確的:
var s = "hello world"
var word = s.substring(1, 4)
//此時s已被封裝成了對象,但是這句話執行完后,該對象立即銷毀
既然字符串不是對象, 它為什么會有substring方法呢? 因為引用字符串s的屬性, JavaScript就會將字符串通過調用new String(s)的方式裝換成對象, 一旦屬性引用結束, 這個新創建的對象就會銷毀(同理于Number()和Boolean()):
var s = "test"
s.len = 4 //給對象設置屬性
var t = s.len
t ==> undefined
而:
"hello" == new String("hello") ==> true//自動封裝
"hello" === new String("hello") ==> false//全等,一個值,一個是對象
不可變的原始值和可變的對象引用
原始值: undefined, null, 數字, 布爾值, 字符串. 它們均不可改變.
對象: 數組和函數, 可改變
原始值的比較只要通過"=="即可(原始值所存儲的內存地址是相同的, 而"=="是用于比較值是否相同, 所以對于原始值來說, 值相同+地址相同 == 它們相同)
對象的本質是引用, 所以只有引用同一個基對象(相同的內存地址)時, 它們才相同:
var o = {x: 1}, p = {x: 1}
o === p ==> false
o == p ==> false
q = o ==> Object {x: 1}//將o的引用賦值給q,此時他們指向同一個對象
q["y"] = 2 //q對象增加一個y=2的屬性,此時0對象也會增加該屬性
q === o ==> true
這里會造成一個困惑是: 為什么 o == p ==> false?
因為o和p的值本質上并不相同, 因為o/p中存儲的是{x: 1}的引用, 而非具體的值. 而{x: 1}和另一個{x: 1}的引用是不相等的.
備注: 我們一般比較對象是否具有某個屬性, in代表屬性存在于實例+原型中, 而hasOwnProperty代表屬性是否存在于實例中.
類型轉換
值 | 字符串 | 數字 | 布爾值 | 對象 |
---|---|---|---|---|
undefined | "undefined" | NaN | false | throws TypeError |
null | "null" | 0 | false | throws TypeError |
true | "true" | 1 | new Boolean(true) | |
false | "false" | 0 | new Boolean(false) | |
"" | 0 | false | new String("") | |
"1.2" | 1.2 | true | new String("1.2") | |
"one" | NaN | true | new String("one") | |
0 | "0" | false | new Number(0) | |
-0 | "0" | false | new Number(-0) | |
NaN | "NaN" | false | new Number(NaN) | |
Infinity | "Infinity" | true | new Number(Infinity) | |
-Infinity | "-Infinity" | true | new Number(-Infinity) | |
1 | "1" | true | new Number(1) | |
{} | 后面解釋 | 后面解釋 | true | |
[] | "" | 0 | true | |
[9] | "9" | 9 | true | |
['a'] | 使用join方法 | NaN | true | |
['a','b'] | "a,b" | NaN | true | |
function(){} | 后面解釋 | NaN | true |
注意:無窮大Infinity首字母必須大寫,不然認為是變量
轉換和相等性
進行"=="判斷時, JavaScript會進行類型轉換.
null == undefined ==> true
"0" == 0 ==> true
這里類型轉換代表的意思是: a == b, 則a轉換為b類型, 或者b轉換為a類型, 再次進行比較. 所以對于null == undefined, 本質上是undefined轉換為一個Object, 然后在和null進行比較.
而"0" == 0, 是將數字0轉換為字符串"0", 然后再進行比較.
但可以成功進行類型轉換, 不一定表示它們相等, 如undefined可轉換為false, 但undefined == false的結果為false.
顯示類型轉換
JavaScript在需要字符串情況下, 會將變量轉換為字符串; 其次, 在需要數字情況下, 會將變量轉換為數字.
Number類定義的toString()方法可以接收表示轉換基數的可選參數. 如果不指定此參數, 轉換規則將是基于十進制:
var n = 17
n.toString() ==> "17"
n.toString(2) ==> "10001"
n.toString(8) ==> "21"
n.toString(16) ==> "11"
toFixed(): 根據小數點后的指定位數將數字轉換為字符串, 它從不使用指數計數法.
toExponential(): 使用指數計數法將數字轉換為指數形式的字符串, 其中小數點前只有一位, 小數點后的位數則由參數指定.
toPrecision(): 根據指定的有效數字位數將數字轉換成字符串. 如果有效數字的位數少于數字部分的位數, 則轉為為指數形式.
var n = 123456.789
n.toFixed(0) ==> "123457"
n.toFixed(2) ==> "123456.79"
n.toFixed(5) ==> "123456.78900"
n.toExponential(1) ==> "1.2e+5"
n.toExponential(3) ==> "1.235e+5"
n.toPrecision(4) ==> "1.235e+5"
n.toPrecision(7) ==> "123456.8"
n.toPrecision(10) ==> "123456.7890"
parseInt(): 盡可能的將字符串轉換為整數, 可接收第二個可選參數, 這個參數指定數字轉換的基數.
parseFloat(): 盡可能的將字符串轉換為浮點數.
console.log(parseInt("111ads")) //111
對象轉換為原始值
toString(): 返回一個反映這個對象的字符串.
valueOf(): 如果存在任意原始值, 它就默認將對象轉換為表示它的原始值.
JavaScript中對象到字符串的轉換經歷以下步驟:
如果對象具有toString()方法, 則調用這個方法. 如果它返回一個原始值, JavaScript將這個值轉換為字符串, 并返回這個字符串結果.
如果對象沒有toString()方法, 或者這個方法并不返回一個原始值, 那么JavaScript會調用valueOf()方法, 如果存在這個方法, 則JavaScript調用它. 如果返回值是原始值, JavaScript將這個值轉換為字符串, 并返回這個字符串結果.
否則, JavaScript拋出類型錯誤.
var o={
toString:function(){
return 9;
},
valueOf:function(){
return 10;
}
};
console.log(""+o);//結果是“10”
對象到數字的轉換經歷以下步驟:
如果對象具有valueOf()方法, 返回一個原始值, 則JavaScript將這個原始值轉換為數字.
否則, 如果對象具有toString()方法, 返回一個原始值, 則JavaScript將其轉換并返回(如果返回的原始值是boolean類型,會轉成0或1)
否則, JavaScript拋出類型錯誤.
var o={
toString:function(){
return 9;
},
valueOf:function(){
return 10;
}
};
console.log(+o);//結果是10
變量和作用域
JavaScript沒有塊級作用域, 它使用了函數作用域.
在JavaScript中, 使用var聲明的全局變量是不可配置的(變量本身具有屬性, 如是否可刪除, 編輯, 是否只讀等):
var a = "hello"
b = "world" //相當于給window對象添加了屬性b
delete a ==> false
delete b ==> true
a ==> "hello"
b ==> VM920:1 Uncaught ReferenceError: b is not defined(…)
表達式和運算符
表達式
數組初始化時, 如果使用逗號(","), 則初始化為undefined:
var arr = [1,,,2]
arr.length ==> 4
arr ==> [1, undefined × 2, 2]
如果針對對象來說, 使用字面量來初始化, 那么其key/value中的key代表的是字符串:
var key = "hello"
var d1 = {key: "world"}
d1 ==> Object {key: "world"}
這里d1的聲明等價于:
var d1 = {"key": "world"}
但如果使用索引來初始化, 則不一樣:
var key = "hello"
var d2 = {}
d2[key] = "world"
d2 ==> Object {hello: "world"}
數組本身就是一個支持下表索引的對象:
var arr = [12, 13, 14]
arr["1"] ==> 13
arr[1] ==> 13
使用new構造出一個對象不同于使用字面量構造出一個對象, 考慮如下的代碼:
var MY_APP_1 = function() {
this.firstMethod = function() {};
this.secondMethod = function() {};
};
var MY_APP_2 = {
firstKey: functon() {},
secondKey: function() {}
};
主要的不同點在于: 通過new, 我們可以創建多個MY_APP_1實例, 而每個實例均綁定到內部的this; 而對于MY_APP_2來說, 它僅僅只是一個變量而已.
針對MY_APP_1來說, 還有以下特殊的幾點:
firstMethod/secondMethod是綁定到具體的實例上, 非綁定狀態下它們并不存在.
在創建具體實例后, 如果沒有綁定一個具體的對象, 則this默認綁定到window對象上:
var app1 = new MY_APP_1();
app1.firstMethod();
// ERROR
// window.firstMethod();
var app2 = MY_APP_1();
// ERROR
// app2.firstMethod();
window.firstMethod();
運算符
- JavaScript運算符通常會根據操作數進行類型轉換, 所以"3" * "5"是合法的, 為數字15.
- 加號既可用于字符串連接, 也可用于數字的相加, 但優先于字符串連接. 所以將數組轉換為字符串時, 我們可以編寫如下的代碼:
var arr = [1,2,3]
"" + arr ==> "1,2,3"
但我們如果僅僅將一個字符串轉換為數字, 也可以使用如下的技巧:
+"3" ==> 3
1.in運算符用于判斷屬性是否存在于對象實例+原型中.
2.instanceof運算符用于判斷左邊對象是否為右邊類的實例.
3.typeof運算符用于判斷對象的類型:
x | typeof x |
---|---|
undefined | "undefined" |
null | "object" |
true 或false | "boolean" |
任意數字或NaN | "number" |
任意字符串 | "string" |
任意函數 | "function" |
任意內置對象(非函數) | "object" |
任意宿主對象 | 由編譯器自身決定 |
- delete用于刪除一個對象的屬性, 對數組來說相當于賦值undefined:
var o = {x: 1, y:2}
delete o.x
o ==> Object {y: 2}
var arr = [11, 12, 13]
delete arr[1]
arr ==> [11, undefined × 1, 13]
語句
空語句(單獨一個分號)通常用在初始化一個數組中, 例如:
for (var i = 0; i < a.length; a[i++] = 0) ;
var和function都是用來聲明語句的, 而function通常有兩種寫法:
var f = function(x) { return x + 1; } //將表達式賦值給一個變量
function f(x) { return x + 1; }
for/in循環并不會遍歷對象的所有屬性, 只有"可枚舉"的屬性才會遍歷到.
"use strict"
- 在嚴格模式中禁止使用with語句.
- 在嚴格模式中, 所有的變量都要先聲明, 否則將會拋出一個引用錯誤異常.
- 在嚴格模式中, 調用的函數中的一個this值是undefined.
- 在嚴格模式中, 當通過call()/apply()來調用函數時, 其中的this值就是通過call()或apply()傳入的第一個參數(在非嚴格模式中, null和undefined值被全局對象和轉換為對象的非對象值所代替)
- 在嚴格模式中, 給只讀屬性賦值和給不可擴展的對象創建新成員都將拋出一個類型錯誤異常.
- 在嚴格模式中, 傳入eval()的代碼不能在調用程序所在的上下文中聲明變量或定義函數, 而在非嚴格模式中是可以這樣做的.
- 在嚴格模式中, 函數里的arguments對象擁有傳入函數值的靜態副本, 即不可修改:
"use strict"
function f(arr) {
f.arguments[0][0] = 11;
}
var arr = [1, 2, 3];
f(arr);
console.log(arr);
這里代碼將會報錯.
針對delete, 后跟非法的標識符將拋出一個語法錯誤異常; 如果試圖刪除一個不可配置的屬性將會拋出一個類型錯誤異常.