JavaScript 的一些基礎知識
JavaScript基本語法
調試
打開 Chrome 開發工具
Win
F12Mac
Command + Option + I
輸入代碼、測試執行
var str = ‘evenyao‘
console.log(str)
語句
(以下內容參考自阮一峰js教程)
JavaScript程序的執行單位為行(line),也就是一行一行地執行。一般情況下,每一行就是一個語句。
語句(statement)是為了完成某種任務而進行的操作,比如下面就是一行賦值語句:
var a = 1 + 3;
這條語句先用var
命令,聲明了變量a
,然後將1 + 3
的運算結果賦值給變量a
1 + 3
叫做表達式(expression),指一個為了得到返回值的計算式。語句和表達式的區別在於,前者主要為了進行某種操作,一般情況下不需要返回值;後者則是為了得到返回值,一定會返回一個值。
凡是JavaScript語言中預期為值的地方,都可以使用表達式。比如,賦值語句的等號右邊,預期是一個值,因此可以放置各種表達式。一條語句可以包含多個表達式。
語句以分號結尾,一個分號就表示一個語句結束。多個語句可以寫在一行內。
var a = 1 + 3 ; var b = ‘abc‘;
分號前面可以沒有任何內容,JavaScript引擎將其視為空語句。
;;;
上面的代碼就表示3個空語句。(關於分號的更多介紹,請看後文《代碼風格》一節。)
表達式不需要分號結尾。一旦在表達式後面添加分號,則JavaScript引擎就將表達式視為語句,這樣會產生一些沒有任何意義的語句。
1 + 3;
‘abc‘;
上面兩行語句有返回值,但是沒有任何意義,因為只是返回一個單純的值,沒有任何其他操作。
變量
概念
變量是對“值”的引用,使用變量等同於引用一個值。每一個變量都有一個變量名。
var a = 1;
上面的代碼先聲明變量a
,然後在變量a
與數值1之間建立引用關系,也稱為將數值1“賦值”給變量a
。以後,引用變量a
就會得到數值1。最前面的var
,是變量聲明命令。它表示通知解釋引擎,要創建一個變量a
變量的聲明和賦值,是分開的兩個步驟,上面的代碼將它們合在了一起,實際的步驟是下面這樣。
var a;
a = 1;
如果只是聲明變量而沒有賦值,則該變量的值是undefined
。undefined
是一個JavaScript關鍵字,表示“無定義”。
var a;
a // undefined
如果變量賦值的時候,忘了寫var
命令,這條語句也是有效的。
var a = 1;
// 基本等同
a = 1;
但是,不寫var
的做法,不利於表達意圖,而且容易不知不覺地創建全局變量,所以建議總是使用var
命令聲明變量。
嚴格地說,
var a = 1
與a = 1
,這兩條語句的效果不完全一樣,主要體現在delete
命令無法刪除前者。不過,絕大多數情況下,這種差異是可以忽略的。
如果一個變量沒有聲明就直接使用,JavaScript會報錯,告訴你變量未定義。
x
// ReferenceError: x is not defined
上面代碼直接使用變量x
,系統就報錯,告訴你變量x
沒有聲明。
可以在同一條var
命令中聲明多個變量。
var a, b;
JavaScript 是一種動態類型語言,也就是說,變量的類型沒有限制,可以賦予各種類型的值。
var a = 1;
a = ‘hello‘;
上面代碼中,變量a
起先被賦值為一個數值,後來又被重新賦值為一個字符串。第二次賦值的時候,因為變量a
已經存在,所以不需要使用var
命令。
如果使用var
重新聲明一個已經存在的變量,是無效的。
var x = 1;
var x;
x // 1
上面代碼中,變量x
聲明了兩次,第二次聲明是無效的。
但是,如果第二次聲明的同時還賦值了,則會覆蓋掉前面的值。
var x = 1;
var x = 2;
// 等同於
var x = 1;
var x;
x = 2;
變量提升
JavaScript引擎的工作方式是,先解析代碼,獲取所有被聲明的變量,然後再一行一行地運行。這造成的結果,就是所有的變量的聲明語句,都會被提升到代碼的頭部,這就叫做變量提升(hoisting)。
console.log(a);
var a = 1;
上面代碼首先使用console.log
方法,在控制臺(console)顯示變量a的值。這時變量a
還沒有聲明和賦值,所以這是一種錯誤的做法,但是實際上不會報錯。因為存在變量提升,真正運行的是下面的代碼。
var a;
console.log(a);
a = 1;
最後的結果是顯示undefined
,表示變量a
已聲明,但還未賦值。
請註意,變量提升只對var
命令聲明的變量有效,如果一個變量不是用var
命令聲明的,就不會發生變量提升。
console.log(b);
b = 1;
上面的語句將會報錯,提示“ReferenceError: b is not defined”,即變量b
未聲明,這是因為b
不是用var
命令聲明的,JavaScript引擎不會將其提升,而只是視為對頂層對象的b
屬性的賦值。
標識符
標識符(identifier)是用來識別具體對象的一個名稱。最常見的標識符就是變量名,以及後面要提到的函數名。JavaScript語言的標識符對大小寫敏感,所以a
和A
是兩個不同的標識符。
標識符有一套命名規則,不符合規則的就是非法標識符。JavaScript引擎遇到非法標識符,就會報錯。
簡單說,標識符命名規則如下:
- 第一個字符,可以是任意Unicode字母(包括英文字母和其他語言的字母),以及美元符號(
$
)和下劃線(_
)。 - 第二個字符及後面的字符,除了Unicode字母、美元符號和下劃線,還可以用數字
0-9
。
下面這些都是合法的標識符。
arg0
_tmp
$elem
π
下面這些則是不合法的標識符。
1a // 第一個字符不能是數字
23 // 同上
*** // 標識符不能包含星號
a+b // 標識符不能包含加號
-d // 標識符不能包含減號或連詞線
中文是合法的標識符,可以用作變量名。
var 臨時變量 = 1;
JavaScript有一些保留字,不能用作標識符:arguments、break、case、catch、class、const、continue、debugger、default、delete、do、else、enum、eval、export、extends、false、finally、for、function、if、implements、import、in、instanceof、interface、let、new、null、package、private、protected、public、return、static、super、switch、this、throw、true、try、typeof、var、void、while、with、yield。
另外,還有三個詞雖然不是保留字,但是因為具有特別含義,也不應該用作標識符:Infinity
、NaN
、undefined
。
註釋
源碼中被JavaScript引擎忽略的部分就叫做註釋,它的作用是對代碼進行解釋。Javascript提供兩種註釋:一種是單行註釋,用//起頭;另一種是多行註釋,放在/* 和 */之間。
// 這是單行註釋
/*
這是
多行
註釋
*/
此外,由於歷史上JavaScript兼容HTML代碼的註釋,所以<!--
和-->
也被視為單行註釋。
x = 1; <!-- x = 2;
--> x = 3;
上面代碼中,只有x = 1
會執行,其他的部分都被註釋掉了。
需要註意的是,-->
只有在行首,才會被當成單行註釋,否則就是一個運算符。
function countdown(n) {
while (n --> 0) console.log(n);
}
countdown(3)
// 2
// 1
// 0
上面代碼中,n --> 0
實際上會當作n-- > 0
,因此輸出2、1、0。
區塊
JavaScript使用大括號,將多個相關的語句組合在一起,稱為“區塊”(block)。
與大多數編程語言不一樣,JavaScript的區塊不構成單獨的作用域(scope)。也就是說,區塊中的變量與區塊外的變量,屬於同一個作用域。
{
var a = 1;
}
a // 1
上面代碼在區塊內部,聲明並賦值了變量a
,然後在區塊外部,變量a
依然有效,這說明區塊不構成單獨的作用域,與不使用區塊的情況沒有任何區別。所以,單獨使用的區塊在JavaScript中意義不大,很少出現。區塊往往用來構成其他更復雜的語法結構,比如for
、if
、while
、function
等。
JavaScript 數據類型
概述
JavaScript 語言的每一個值,都屬於某一種數據類型。JavaScript 的數據類型,共有六種。(ES6 又新增了第七種 Symbol 類型的值)
- 數值(number):整數和小數(比如1和3.14)
- 字符串(string):字符組成的文本(比如"Hello World")
- 布爾值(boolean):
true
(真)和false
(假)兩個特定值 undefined
:表示“未定義”或不存在,即由於目前沒有定義,所以此處暫時沒有任何值null
:表示無值,即此處的值就是“無”的狀態。- 對象(object):各種值組成的集合
通常,我們將數值、字符串、布爾值稱為原始類型(primitive type)的值,即它們是最基本的數據類型,不能再細分了。而將對象稱為合成類型(complex type)的值,因為一個對象往往是多個原始類型的值的合成,可以看作是一個存放各種值的容器。至於undefined
和null
,一般將它們看成兩個特殊值。
對象又可以分成三個子類型。
- 狹義的對象(object)
- 數組(array)
- 函數(function)
typeof運算符
JavaScript有三種方法,可以確定一個值到底是什麽類型。
typeof
運算符instanceof
運算符Object.prototype.toString
方法
instanceof
運算符和Object.prototype.toString
方法,將在後文相關章節介紹。這裏著重介紹typeof
運算符。
typeof
運算符可以返回一個值的數據類型,可能有以下結果。
(1)原始類型
數值、字符串、布爾值分別返回number
、string
、boolean
。
typeof 123 // "number"
typeof ‘123‘ // "string"
typeof false // "boolean"
(2)函數
函數返回function
。
function f() {}
typeof f
// "function"
(3)undefined
undefined
返回undefined
。
typeof undefined
// "undefined"
利用這一點,typeof可以用來檢查一個沒有聲明的變量,而不報錯。
v
// ReferenceError: v is not defined
typeof v
// "undefined"
上面代碼中,變量v
沒有用var
命令聲明,直接使用就會報錯。但是,放在typeof
後面,就不報錯了,而是返回undefined
。
實際編程中,這個特點通常用在判斷語句。
// 錯誤的寫法
if (v) {
// ...
}
// ReferenceError: v is not defined
// 正確的寫法
if (typeof v === "undefined") {
// ...
}
(4)其他
除此以外,其他情況都返回object
。
typeof window // "object"
typeof {} // "object"
typeof [] // "object"
typeof null // "object"
從上面代碼可以看到,空數組([]
)的類型也是object
,這表示在JavaScript內部,數組本質上只是一種特殊的對象。
既然typeof
對數組(array)和對象(object)的顯示結果都是object
,那麽怎麽區分它們呢?instanceof
運算符可以做到。
var o = {};
var a = [];
o instanceof Array // false
a instanceof Array // true
instanceof
後續講解。
布爾值
布爾值代表“真”和“假”兩個狀態。“真”用關鍵字true
表示,“假”用關鍵字false
表示。布爾值只有這兩個值。
下列運算符會返回布爾值:
- 兩元邏輯運算符:
&&
(And),||
(Or) - 前置邏輯運算符:
!
(Not) - 相等運算符:
===
,!==
,==
,!=
- 比較運算符:
>
,>=
,<
,<=
如果JavaScript預期某個位置應該是布爾值,會將該位置上現有的值自動轉為布爾值。轉換規則是除了下面六個值被轉為false
,其他值都視為true
。
undefined
null
false
0
NaN
""
或‘‘
(空字符串)
布爾值往往用於程序流程的控制,請看一個例子。
if (‘‘) {
console.log(true);
}
// 沒有任何輸出
上面代碼的if
命令後面的判斷條件,預期應該是一個布爾值,所以JavaScript自動將空字符串,轉為布爾值false
,導致程序不會進入代碼塊,所以沒有任何輸出。
需要特別註意的是,空數組([]
)和空對象({}
)對應的布爾值,都是true
。
if ([]) {
console.log(true);
}
// true
if ({}) {
console.log(true);
}
// true
更多關於數據類型轉換的介紹,參見《數據類型轉換》一節。
運算符
JavaScript中運算符主要用於連接簡單表達式,組成一個復雜的表達式。常見的有算數表達式、比較表達式、邏輯表達式、賦值表達式等,也有單目運算符,指操作原始表達式。大多數運算符都由標點符號組成(+、>=、!),也有關鍵字表示的運算符,如typeof、delete、instanceof等。
一些運算符可以作用於任何數據類型(typeof),但大部分操作符希望操作數是特定的類型,而且大部分操作符會計算出(我們也常說返回)一個特定類型的值(typeof返回的全是string)。在JavaScript中運算符通常會根據需要對操作數進行類型轉換,乘法操作符希望操作數是數字,但是 "3" * "5"
也是合法的,JavaScript會自動將其轉換為數字計算,返回Number 15。
有些操作符對不同的數據類型有不同的含義,比如 +
- 在兩個操作數都是數字的時候,會做加法運算
- 兩個參數都是字符串或在有一個參數是字符串的情況下會把另外一個參數轉換為字符串做字符串拼接
- 在參數有對象的情況下會調用其valueOf或toString
- 在只有一個字符串參數的時候會嘗試將其轉換為數字
- 在只有一個數字參數的時候返回其正數值
console.log(2+4);//6
console.log("2"+"4");//"24"
console.log(2+"4");//"24"
console.log(2+new Date());//"2Mon Jan 20 2014 17:15:01 GMT+0800 (China Standard Time)"
console.log(+"4");//4
遠算符類型
算數運算符
- 加法運算符(Addition):x + y
- 減法運算符(Subtraction): x - y
- 乘法運算符(Multiplication): x * y
- 除法運算符(Division):x / y
- 余數運算符(Remainder):x % y
- 自增運算符(Increment):++x 或者 x++
- 自減運算符(Decrement):--x 或者 x--
- 求負運算符(Negate):-x
- 數值運算符(Convert to number): +x
賦值運算符
賦值運算符用於給變量賦值,最常見的賦值運算符,當然就是等號,表達式x=y
表示將y賦值給x。除此之外,JavaScript還提供其他11個賦值運算符。
x += y // 等同於 x = x + y
x -= y // 等同於 x = x - y
x *= y // 等同於 x = x * y
x /= y // 等同於 x = x / y
x %= y // 等同於 x = x % y
x >>= y // 等同於 x = x >> y
x <<= y // 等同於 x = x << y
x >>>= y // 等同於 x = x >>> y
x &= y // 等同於 x = x & y
x |= y // 等同於 x = x | y
x ^= y // 等同於 x = x ^ y
比較運算符
比較運算符比較兩個值,然後返回一個布爾值,表示是否滿足比較條件。JavaScript提供了8個比較運算符。
==
相等===
嚴格相等!=
不相等!==
嚴格不相等<
小於<=
小於或等於>
大於>=
大於或等於
布爾運算符
!
取反運算符&&
且運算符||
或運算符condition? true case : false case
三元條件運算符
位運算符
- 或運算(or):符號為
|
,表示兩個二進制位中有一個為1,則結果為1,否則為0。 - 與運算(and):符號為
&
,表示兩個二進制位都為1,則結果為1,否則為0。 - 否運算(not):符號為
~
,表示將一個二進制位變成相反值。 - 異或運算(xor):符號為
ˆ
,表示兩個二進制位中有且僅有一個為1時,結果為1,否則為0。 - 左移運算(left shift):符號為
<<
- 右移運算(right shift):符號為
>>
- 帶符號位的右移運算(zero filled right shift):符號為
>>>
其它
小括號
在JavaScript中,圓括號是一種運算符,它有兩種用法:如果把表達式放在圓括號之中,作用是求值;如果跟在函數的後面,作用是調用函數。
把表達式放在圓括號之中,將返回表達式的值。
void
void運算符的作用是執行一個表達式,然後返回undefined。
逗號運算符
逗號運算符用於對兩個表達式求值,並返回後一個表達式的值。
運算符優先級與結合性
優先級什麽意思大家都清楚,結合性是指多個具有同樣優先級的運算符表達式中的運算順序。有的運算符是左結合的,即運算從左到右執行,下面兩個運算是一樣的
w = x + y + z;
w = (x + y) + z;
有的運算符是右結合的
w = x = y = z;
w = (x = (y = z));
w = a: b: c ? d : e? f : g;
w = a? b : (c? d: (e? f : g));
運算符的優先級《JavaScript權威指南》中有個表闡述的很好(我去掉了位運算部分),其中R/L代表結合性是右結合還是左結合,num->num表示操作符期望的數據類型和計算結果類型,lval指左值
運算符 | 操作 | 結合性 | 類型 |
---|---|---|---|
++ | 自增 | R | lval->num |
-- | 自減 | R | lval->num |
- | 求反 | R | num->num |
+(一個操作數) | 轉換為數字 | R | num->num |
~ | 按位求反 | R | int->int |
! | 邏輯非 | R | bool->bool |
delete | 刪除屬性 | R | lval->bool |
typeof | 檢測數據類型 | R | any->str |
void | 返回undefined | R | any->undefined |
*、/、% | 乘、除、求余 | L | num,num->num |
+、- | 加、減 | L | num,num->num |
+ | 字符串拼接 | L | str,str->str |
<、<=、>、>= | 數字大小或字母表順序 | L | num/str,num/str->bool |
instanceof | 對象類型 | L | obj,function->bool |
in | 測試屬性是否存在 | L | str,obj->bool |
== | 判斷相等 | L | any,any->bool |
!= | 判斷不等 | L | any,any->bool |
=== | 判斷恒等 | L | any,any->bool |
!== | 判斷非恒等 | L | any,any->bool |
&& | 邏輯與 | L | any,any->any |
|| | 邏輯或 | L | any,any->any |
?: | 條件運算符 | R | bool,any,any->any |
=賦值 *=、/=、+=、-= | 賦值 運算且賦值 | R | lval,any->any |
, | 忽略第一個操作數,返回第二個操作數 | L | any,any->any |
MDN 參考
有幾個我們需要註意的地方
-
typeof的優先級相當的高,比加減乘除神馬的都高,所以雖然是操作符,在在復雜表達式的時候我們還是習慣加括號,看個例子
typeof 2*3;//NaN typeof (2*3);//"number" typeof 2+3;// "number3"
-
++
、--
是右結合的操作符(優先級最高的幾個都是右結合),而且比加減乘除優先級高。同時自增、自減運算符的運算數得是左值(可以放在賦值符號左邊的值),而不能是常數4++; //ReferenceError: Invalid left-hand side expression in postfix operation var a=0,b=0; a+++b;//0 a;//1,++優先級比+高,所以相當於(a++)+b b;//0
-
賦值運算符的優先級相當的低
a = b == c; //等同於a = (b==c)
-
邏輯非
!
也在優先級隊列的前端,比加減乘除高,但邏輯與、邏輯或優先級很低,不如加減乘除!2*0; //0, 等價於(!2)*0
-
一個關於邏輯運算符的有意思地方是其“短路”功能,相信大家都有所了解,但有些題目不那麽單純,會結合表達式計算值來考察
1 && 3; 1 && "foo" || 0; 1 || "foo" && 0
了解了邏輯運算符的“短路”特點,在知道原始表達式的“返回值”就是本身,題目就很簡單了
相等
我們知道可以使用==
或===
判斷兩個值的相等性,其中區別相信大家清楚,===
是嚴格意義的相等,只需註意NaN和NaN不等就行了。而使用==
的時候,javascript會幫我們做類型轉換,造成一些匪夷所思的結果,那麽使用==
的時候會在哪些情況下做類型轉換,又會換成什麽樣子?
- 如果兩個值類型相同,則執行嚴格相等的運算
- 如果兩個值的類型不同
- 如果一個是null,一個是undefined,那麽相等
- 如果一個是數字,一個是字符串,先將字符串轉為數字,然後比較
- 如果一個值是true/false則將其轉為1/0比較
- 如果一個值是對象,一個是數字或字符串,則嘗試使用valueOf和toString轉換後比較
- 其它就不相等了
null==undefined;
NaN==NaN
"1"==true
JavaScript 的一些基礎知識