詞法結構
字符集:unicode
區分大小寫,關鍵字、變量、函數名、標示符必須采取大小寫一致
-
注釋
// 單行注釋 /* ~一段注釋~ */ // 另一段注釋 /* * 多行注釋 * 多行注釋 */
-
直接量
12 // 數字 1.2 // 小數 "hello world" // 一個字符串 'hi' // 另一種字符串 true // 布爾值 /^javascript$/gi //正則表達式直接量 null // 空 { x:1 ,y:2} // 對象 [ 1,2,3,4,5 ] //數組
-
可選的分號
- 在任何需要分割的地方使用
;
進行分隔 - 在任何可以省略分號的地方都將其省略
如果不使用分號,由于語句的分隔規則,會導致一些意想不到的情況,建議無論什么時候都使用分號進行分割
- 在任何需要分割的地方使用
類型、值和變量
js中的數據類型:
- 數字
- 字符串
- 布爾值
- null
- undefine
- 對象
js數據類型分為2類:原始類型
和 對象類型
原始類型:
- 數字
- 字符串
- 布爾值
- 特殊的原始值:‘null’ 和 ‘undefine’
對象類型:
- 屬性的集合
- 數組
- 函數
變量聲明
js中通過var
關鍵字來聲明一個變量
var a = 1;
var a,b;
var i=0,j=1,k=2;
如果未在var語句中,就給變量指定初始值,那么雖然聲明這個變量,但是給它存入一個值前,初始值是undefined
重復聲明一個變量合法,且沒有任何危害
變量作用域
全局變量擁有全局作用域
函數內聲明的變量和函數參數都是局部變量,只能在函數體內有定義
函數體內,局部變量優先級高于全局變量,同名局部變量覆蓋全局變量
var scope = "global";
function check() {
var scope = "local";
return scope; // 返回"local"
}
a = check(); // => "local"
聲明提前
js的函數作用域指的是,在函數內聲明的所有變量,在函數體內始終可以看到,變量在聲明之前甚至已經可以使用,這個特性稱為“聲明提前”
看下下面一段代碼,你可能會認為第一個打印"global",因為代碼沒有執行到var語句。其實,局部變量在函數體內始終有定義,也就是說,
函數體內局部變量覆蓋同名全局變量。但是只有執行到var語句時局部變量才會賦值,因此,上面的執行過程相當于,將函數變量聲明
“提前”函數體頂部,同時初始化位置仍然留在原來位置
var scope = "global";
function check() {
console.log(scope); // "undefined"
var scope = "local"; // 賦初始值
console.log(scope); // “local”
}
作為屬性的變量
js的全局變量,其實是定義了全局對象的一個屬性。
當使用var聲明一個變量,創建的這個屬性是不可以配置的。無法通過delete刪除
如果給一個未聲明的變量賦值,js會自動創建一個全局變量,是全局對象的正常可配置的屬性,能夠通過delete刪除
var some = 1;
gvar = 1;
delete some; // => false,變量沒被刪除
delete gvar; // => true,變量被刪除
數字
整型直接量
js不區分整數值和浮點數值,所有的數字均用浮點數值表示。js采用的時IEEE 754定義的64位浮點格式來表示數字,
然后需要注意,js中實際的操作(比如數組索引)則是基于32位整數的
支持的整型直接量
- 十進制 1000
- 十六進制 0x1F、0X39
部分ECMAScript實現是支持八進制的,但ECMAScript標準不支持八進制直接量,在ECMAScript6的嚴格模式下,八進制是明令禁止的
浮點型直接量
浮點型采用傳統實數寫法,可以采用指數計數法和更簡潔的記法
- 3.14
- 7.03E-12
- .3333333
算術運算
運算符支持:+
,-
,*
,/
,%
js還支持更為復雜的算術運算,通過作為Math對象的屬性定義的函數和常量來實現
Math.pow(2,53) // 2的53次冪
Math.round(.6) // => 1.0 四舍五入
Math.ceil(.6) // => 1.0 向上求整
Math.floor(.6) // => 0.0 向下求整
Math.abs(-4) // 絕對值
Math.max(x,y,z) // 返回最大值
Math.min(x,y,z) // 返回最小值
Math.random() // 生成一個0-1.0的隨機數
Math.PI // 圓周率
Math.E // e,自然對數
Math.sqrt(3) // 3的平方根
Math.sin(0) // 三角函數
Math.log(10) // 10的自然對數
Math.log(100)/Math.LN10 // 以10為底的對數
Math.log(512)/Math.LN2 // 以2為底的對數
Math.exp(3) // e的3次冪
正無窮大:Infinity
非數字值: NaN
Infinity // 將一個可讀可寫的變量初始化為Infinity
Number.POSITIVE_INFINITY // Infinity,只讀
1/0 // Infinity
Number.MAX_VALUE + 1 // Infinity
Number.NEGTIVE_INFINITY // 負無窮大
-Infinity // 負無窮大
-1/0 // 負無窮大
-Number.MAX_VALUE - 1 // 將一個可讀可寫變量初始化為NaN
NaN // 非數字值
Number.NaN // 非數字值,只讀
0/0 // NaN
Number.MIN_VALUE/2 // 下溢,計算結果為0
-Number.MIN_VALUE/2 // 負零
-1/Infinity // 負零
-0 // 負零,下溢,比js所能表示的0還小時,計算結果為0
負零和正零是相等的,除了作為除數之外
var zero = 0;
var negz = -0;
zero === negz; // => true
1/zero === 1/negz // => false
二進制浮點數和四舍五入錯誤
js可以精確的表示分數,比如1/2、1/8,但不能精確表示0.1這樣數字
var x = .3 - .2;
var y = .2 - .1;
x == y; // => false 兩個值不相等
x == .1 // => false ,0.3 - 0.2 不等于 0.1
y == .1 // => true, 0.2 - 0.1 等于 0.1
日期和時間
js語言核心包括Date()
構造函數,用來表示日期和時間
var then = new Date(2016.4.22); // 2016年4月22日
var later = new Date(2016,3,25,17,19,30) // 2016.4.25 17:19:30pm
var now = new Date(); // 當前時間和日期
var elapsed = now - then; // 日期減法,計算當前時間間隔的毫秒數
later.getFullYear(); // => 2016
later.getMonth(); // => 3 ,月份從0開始
later.getDate() // => 25,從1開始的天數
later.getDay() // => 1,星期幾,0代表星期日,1代表星期1
later.getHours() // => 17 當地時間17:19pm
later.getUTCHours() // => 9 我所在UTC-8 使用UTC表示小時的時間,基于時區,所以返回9
string (文本)
js是采用UTF-16編碼的Unicode字符集,string是由一組16位值組成的 不可變 的有序序列,每個字符
通常來自于Unicode字符集。
js通過字符串類型來表示文本。字符串的長度是其所含的16位值的個數。
js字符串的索引從0開始,空字符串長度為0
js字符串直接量是由單引號或者雙引號括起來的字符序列。
""
'test'
"3.14"
'name="myform"'
"Would't you prefer to say goobye?"
"this string\nhas two lines"
在ECMAScript3中,字符串直接量必須寫在一行,而在ECMAScript5中,字符串直接量可以拆成數行,每一行
通過反斜線(\\
)結束,反斜線和行結束符都不算字符串直接量的內容
"two\nlines"
"one\
two\
three"
轉義字符
在js中,反斜線有特殊用途,用來進行轉義,不是原來的意思了
轉義字符 | 含義 |
---|---|
\o | NUL字符(\u0000) |
\b | 退格符(\u0008) |
\t | 水平制表符 |
\n | 換行符(\u000A) |
\v | 垂直制表符(\u000B) |
\f | 換頁符(\u000C) |
\r | 回車符(\u000D) |
" | 雙引號(\u0022) |
' | 單引號(\u0027) |
\ | 反斜線(\u005C) |
\XXXXXXXX | 兩位16進制XXXXXXX指定的Latin-1字符 |
\uXXXXXXXXXXXXXX | 4位十六進制XXXXXXXXXXXXXX指定的Unicode字符 |
字符串使用
jsn 內置功能之一就是字符串連接,將+
用于字符串,則表示字符串連接
msg = "Hello" + " " + "World"
獲取字符串長度: s.length
除了length屬性,還提供許多調用方法:
var s = "hello,wolrd"
s.charAt(0) // "h":第一個字符
s.charAt(s.length-1) // "d":最后一個字符
s.substring(1,4) // "ell": 第2-4個字符
s.slice(1,4) // "ell": 第2-4個字符
s.slice(-3) // "rld": 最后三個字符
s.indexOf("l") // 2: l 首次出現的位置
s.lastIndexOf('l') // 10: l最后出現的位置
s.indexOf('l',3) // 3: 在位置3之后首次出現l的位置
s.split(",") // ["hello","world"] 分割成字符串
s.replace('h','H') // "Hello,world",將h替換成H
s.toUpperCase() // "HELLO,WORLD",轉成大寫字符串
模式匹配
js定義了RegExp()函數,用來創建文本匹配模式
的對象,稱為正則表達式
RegExp不是js的基本數據類型,和Date一樣,只是一種具有實用API的特殊對象,但是仍然具有直接量寫法,
可以直接在js中使用,在兩條斜線之間的文本構成正則表達式直接量。第二條斜線之后也可以跟隨一個或多個字母,
用來修飾匹配模式含義
/^HTML/ //匹配以HTML開始的字符串
/[1-9][0-9]*/ // 匹配一個非零數字,后面是任意個數字
/\bjavascript\b/i // 匹配單詞“javascript”,忽略大小寫
RegExp對象定義有用的方法,字符串也有接收RegExp參數的方法
var text = 'testing: 1, 2, 3';
var pattern = /\d+/g // 匹配所有包含一個或多個數字的實例
pattern.test(text) // true 匹配成功
text.serch(pattern) // 9,首次匹配成功的位置
text.match(pattern) // ["1","2","3"] 所有匹配組成的數組
text.replace(pattern,"#") //"testing,#, #, #"
text.split(/\D+/) //["","1","2","3"] 用非數字截取字符串
布爾值
布爾值:true
和false
比較語句結果通常是布爾值,a==4
任意js值都可以轉換為布爾值,下面轉換成false:
undefined
null
0
-0
NaN
"" // 空字符串
所有其他值,包括所有對象數組,都會轉換成true
檢測o是否是非null值,只有o不是null時才會執行if后面代碼
if( o !== null)...
o不是任何假值才會執行if后面代碼:
if( o)...
布爾值包含toString(),可以將字符串轉換為"true"
或"false"
布爾值運算符:&&
(與) 、 ||
(或) 、 !
(非)
null和undefined
null: 描述"空值"
undefined: 未定義的值,表明變量沒有初始化
盡管null和undefined不同,但是都表示值的空缺,往往可以互換
null == undefined
// => true
null和undefined 都不包含任何屬性和方法
全局對象
全局對象在js中有著重要用途,全局對象屬性是全局定義的符號,js程序可以直接使用
js解釋器啟動時,會創建一個的新的全局對象,并定義初始屬性:
- 全局屬性,比如undefined、Infinity和NaN
- 全局函數,比如isNaN(),parseInt()和eval()
- 構造函數,比如Date(),RegExp(),String(),Object()和Array()
- 全局對象,比如Math和JSON
全局對象的初始屬性并不是保留字,但是應該當作保留字對待,還有Windows對象定義一些額外的全局屬性,也應該保留
var global = this; // 定義一個引用的全局對象的全局變量
包裝對象
js 對象是一種復合值,屬性或已命名的集合。
通過.
符號來引用屬性值
當屬性值是一個方法時,稱其為方法。通過O.m()來調用對象O中的方法
字符串同樣具有屬性和方法:
var s = "hello world!";
var word = s.substring(s.indexOf(' ')+1, s.length); // word="world!"
字符串既然不是對象,為啥有屬性呢?
因為引用了字符串s的屬性,js會將字符串值通過調用new String(s)
的方式轉換成對象,這個對象繼承了
字符串的方法,并被用來處理屬性的引用。一但引用結束,這個新創建對象便被銷毀了
同字符串一樣,數字和布爾值也具有各自的方法:通過Number()
和Boolean
構造函數創建一個臨時對象,
這些方法的調用均來自這個臨時對象。
null和undefined沒有包裝對象,訪問他們的屬性,會造成一個類型錯誤
看個栗子,思考一下執行結果:
var s = "test"; // 創建一個字符串
s.len = 4; // 給s設置一個屬性值,創建一個臨時字符串對象,并且賦值len=4,然后銷毀這個臨時對象
var t = s.len; // 查詢這個屬性,通過原始的(沒有修改過的)字符串值創建一個新的字符串對象,嘗試讀取len,屬性不存在,t為undefined
存取字符串、數字、布爾值的屬性時臨時創建的對象叫做 包裝對象
包裝對象只是被看作一種實現細節,不需要特別關注。由于字符串、數字、和布爾值的屬性都是只讀的,
并且不能夠定義新屬性,與對象是有區別的
注意,可以通過String()
Number()
Boolean()
構造函數顯式創建包裝對象:
var s = "test", n = 1, b = true; // 一個字符串、數字、布爾值
var S = new String(s); // 一個字符串對象
var N = new Number(n); // 一個數字對象
var B = new Boolean(b); // 一個布爾值對象
js 會在必要時包裝對象轉換成原始值,因此上段代碼中對象S、N、B常常,但不總是表現和值s、n、b一樣。
==
將原始值和其包裝對象視為相等
===
將它們視為不相等,通過typeof
運算符可以看到原始值和包裝對象的不同
不可變的原始值和可變的對象引用
js 中原始值(undefined
,null
,布爾值
,數字
,字符串
)與對象(包括數組和函數)有著根本區別。
- 原始值時不可更改的,任何方法都無法更改(或者
突變
)一個原始值
對數字和布爾值來說顯然如此-改變數字值本身就說不通,對字符串來說不是很明顯。
字符串是由字符組成數組,我們希望通過指定索引來修改字符串中的字符。實際上js禁止這樣的行為,
字符串方法看上去返回修改后的字符串,實際上是返回新的字符串值
比如:
var s = "hello"; // 創建一個小寫組成的字符串
s.toUpperCase(); // 返回"HELLO",但是沒有改變s的值
s // s=“hello”,s原字符串并沒有改變
- 原始值的比較只是值的比較
原始值只有在值相等時才相等,對于數字、布爾值、null和undefined來說比較容易理解,對于字符串則不明白了
單獨比較2個字符串,只有2個字符串長度相等且每個索引的字符都相等,才是相等
- 對象是可變的,值是可修改的
栗子:
var o = { x:1 }; // 定義一個對象
o.x = 2; // 修改對象屬性值來修改對象
0.y = 3; // 再次修改對象,增加一個屬性-y
var a = [1,2,3] // 數組也是可修改的
a[0] = 0; // 更改數組的一個元素
a[3] = 4; // 給數組增加一個新元素
- 對象的比較不是值的比較,而是對象引用的比較
即使2個對象包含同樣的屬性和相同值,也不是相等的
各個索引元素完全相等的兩個數組也不相等。
var o = { x:1 }, p = { x:1 } // 具有同樣屬性和值的兩個對象
0 === p // false ,兩個單獨的對象永遠不相等
var a = [], b =[] // 兩個單獨的空數組
a === b // false 兩個單獨的數組永遠不相等
通常講,對象被稱為引用類型
,以便將js基本類型區分。對象值都是引用,對象的比較都是引用的比較
當且僅當他們引用同一個對象時,才會相等
var a = []; // 定義一個空的數組
var b = a; // 變量b引用a
b[0] = 1; // 通過b的引用修改a
a[0]; // 1,a的值也修改了
a === b; // true,a和b引用同一個數組,相等
如上所示,將對象或數組賦值給一個變量,僅僅是賦值的引用值:對象本身沒有被復制一次
如果想得到一個對象或者數組的副本,必須顯示復制對象每一個屬性或數組每一個元素
例如循環來完成數組復制:
var a = [1,2,3];
var b = [];
for(var i = 0; i < a.length; i++){
b[i] = a[i];
}
同樣的,如果想比較2個單獨的對象或者數組,必須比較全部的屬性或者元素
比較2個數組的函數:
function equalArrays(a,b){
if ( a.lengh != b.length) return false;
for(var i=0; i<a.length; i++ ){
if(a[i] !== b[i]) return false;
}
return true;
}
類型轉換
值 | 轉換為字符串 | 數字 | 布爾值 | 對象 |
---|---|---|---|---|
undefined | "undefined" | NaN | false | throws TypeError |
null | "null" | 0 | false | throw TypeError |
true | "true" | 1 | XXXXXXX | new Boolean(true) |
false | "false" | 0 | XXXXXXX | new Boolean(false) |
""(空字符串) | XXXXXXX | 0 | false | new String("") |
"1.2" | XXXXXXX | 1.2 | true | new String("1.2") |
"one" | XXXXXXX | NaN | true | new String("one") |
0 | "0" | XXXXXXX | false | new Number(0) |
-0 | "0" | XXXXXXX | false | new Number(-0) |
NaN | "NaN" | XXXXXXX | false | new Number(NaN) |
Infinity | "Infinity" | XXXXXXX | true | new Number(Infinity) |
-Infinity | "-Infinity" | XXXXXXX | true | new Number(-Infinity) |
1(無窮大,非零) | "1" | XXXXXXX | true | new Number(1) |
{} | 復雜 | 復雜 | true | XXXXXXX |
[] | "" | 0 | true | XXXXXXX |
[9] | “9” | 9 | true | XXXXXXX |
['a'] | 使用join()方法 | NaN | true | XXXXXXX |
function(){} | 復雜 | NaN | true | XXXXXXX |
原始值到對象的轉換也很簡單,原始值調用String()
,Number()
,Boolean()
構造函數,轉換為各自的包裝對象
null 和 undefined屬于例外,當做是一個對象的地方都會產生一個類型錯誤異常,不會進行正常類型轉換
轉換和相等性
js可以做靈活的類型轉換,相等運算符也跟相等的含義一樣,靈活多變
下面的比較結果都是true:
null == undefined; // 這兩個值被認為相等
"0" == 0; // 比較之前,字符串轉換成數字
0 == false; // 比較之前,布爾值轉換成數字
"0" == false; // 比較之前字符串和布爾值都轉換成數字
需要注意,一個值轉換成另一個值不代表兩個值相等。
如果在期望使用布爾值的地方使用undefined,會轉換成false,但不代表undefined == false
js中,if語句將undefined轉換成false,但 ==
運算符不會試圖將操作數轉換為布爾值
顯示類型轉換
為了使代碼更加清晰明了,經常使用顯示轉換,最簡單的方法就是不使用new運算符來調用Boolean()、Number()、
String(),Object()函數
Number("3") // '3'=>3
String(false) // false => "false"
Boolean([]) // => true
Object(3) // => new Number(3)
除了null和undefined以外都有toString方法,一般執行結果與String()返回結果一致
如果試圖把null 和 undefined 轉換為對象,會拋出一個類型錯誤。如果使用object()則不會拋出異常,僅僅
簡單的返回一個新創建的空對象
js中某些運算符會做隱式的類型轉換
-
+
運算符
一個操作數是數字,另一個操作數是字符串,則會把非字符串的操作數轉換成字符串然后拼接之后返回(非字符串操作數不會改變類型)
1+'1' // => '11'(String)
x + '' // => 等價于String(x)
-
!
運算符
會把操作數轉換為布爾值并取反。(操作數不會改變類型)
!!x // 等價于Boolean(x)
Number => String
- Number類定義的toString()方法可以接受表示轉換基數的可選參數,如果不指定,則轉換基于十進制
var n = 17;
binary_string = n.toString(2);
octal_string = "0" + n.toString(8);
hex_string = "0X" + n.toString(16);
- 控制小數點位置和有效數字位數
- 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'
String => Number
- Number() 接受傳入一個字符串,試圖將其轉換為一個整數或者浮點數直接量,基于十進制數轉換,且不能帶非法字符
- parseInt() 只解析整數,并且可以通過字符串前綴“0X”或"0x" 解析為16進制數
- parseFloat 解析浮點數和整數
parseInt("3 asd"); // => 3
parseInt("3.124"); // => 3
parseInt("0xff"); // => 255
parseInt("-0x7f"); // => -127
parseFloat("3.14 asdas") // => 3.14
parseFloat(".1") // => 0.1
parseInt(".1") // => NaN 整數不能以`.`開始
parseFloat("$111.11") // => NaN 數字不能以"$"開始
parseInt 可以接收第二個可選參數,指定數字轉換的基數,合法范圍 2~36
parseInt("11",2); // => 3
parseInt("ff",16); // => 255
parseInt("zz",36); // => 1295
parseInt("077",8); // => 63
parseInt("077",10); // => 77
對象轉換為原始值
- Object => Boolean
所有對象(包括數組和函數)都轉換為true
- Object => String
toString()
valueOf()
- Object => Number
valueOf()
toString()
js的對象到字符串轉換步驟:
- 如果對象有toString方法,則調用這個方法。如果返回一個原始值,則js將這個值轉換為字符串,返回字符串結果
- 如果對象沒有toString方法,或者這個方法不返回原始值,則調用valueOf方法。如果存在這個方法,則js
調用。如果返回時原始值,js將這個值轉換為字符串,然后返回字符串結果 - 如果沒有以上兩個方法,或者以上2個方法返回的不是原始值,會拋出一個類型錯誤異常
js的對象到數字轉換過程:
js轉換數字過程與字符串過程差不多,只是優先使用valueOf
- 如果對象有valueOf方法,則調用這個方法。如果返回一個原始值,則js將這個值轉換為字符串,返回字符串結果
- 如果對象沒有valueOf方法,或者這個方法不返回原始值,則調用toString方法。如果存在這個方法,則js
調用。如果返回時原始值,js將這個值轉換為字符串,然后返回字符串結果 - 如果沒有以上兩個方法,或者以上2個方法返回的不是原始值,會拋出一個類型錯誤異常
為什么空數組和單個數字數組會被轉換成0和單個數字?
- 數組繼承了Object默認的valueOf方法,返回一個對象而不是原始值,因此調用toString()方法,這個方法返回一個原始值
因此數組到數字的轉換為空字符串,空字符串轉換成數字0 - 含有一個元素的數組轉換成字符串結果和單個元素轉換字符串的結果一樣。轉換成單個數字的字符串,然后轉換成數字
"+"運算符的一個數字,一個操作數是對象,情況如何?
js將使用“特殊方法”將對象轉換成原始值,而不是其他運算符的方法執行對象到數字的轉換,“==”運算符與此類似。
如果將對象和原始值比較,則轉換會將對象到原始值的轉換方式進行
“+”、“==”應用在對象到原始值的轉換特殊情形
如果涉及到日期對象,而日期類是js中唯一的預先定義類型,定義了有意義的向字符串和數字的轉換。
對于所有非日期對象,對象到原始值的轉換基本上是對象到數字的轉換,也就是首先調用valueOf。
對于日期對象,則使用對象到字符串的轉換模式,通過valueOf或toString返回的原始值被直接使用,而不會被強制轉換為數字或字符串,與其他的轉換有一些不同
"<"運算符
所有關系運算符對對象做對象到原始值的轉換,但要出去日期對象情形。任何對象都好首先嘗試調用valueOf,然后調用toString。
不管得到的原始值能否被直接使用,都不會進一步轉換為數字或字符串。
"+" "==" "!=" 和關系運算符是唯一執行這種特殊的字符串到原始值的轉換方式的運算符
其他運算符到特定類型的轉換都很明確,而且對日期對象來講,也沒有特殊情形
var now = new Date();
typeof(now + 1); // "string","+"將日期轉換為字符串
typeof(now - 1); // "number" ,"-" 將使用對象到數字的轉換
now == now.toString(); // true ,隱式的和顯式的字符串轉換
now > (now - 1); // true,">"將日期轉換為數字