1. 程式人生 > 實用技巧 >【JS高階程式設計(第4版)學習筆記】第三章 語言基礎

【JS高階程式設計(第4版)學習筆記】第三章 語言基礎

ECMAScript 的語法很大程度上借鑑了 C 語言和其他類 C 語言,如 Java和 Perl。

ECMAScript 中一切都區分大小寫。無論是變數、函式名還是操作符,都區分大小寫。


3.1識別符號

ECMA識別符號可以由一個或多個下列字元組成:第一個字元必須是一個字母、下劃線( _ )或美元符號($),剩下的其它字元可以是字母、下劃線、美元符號或數字。

ECMA識別符號使用駝峰大小寫形式,即第一個單詞的首字母小寫,後面每個單詞的首字母大寫。

關鍵字、保留字、true、false和null不能作為識別符號。

3.2註釋

ECMAScript採用C語言風格的註釋,包括單行註釋和塊註釋、如:

//單行註釋

/*這是多行

註釋*/

3.3嚴格模式

ECMAScript5增加了嚴格模式(strict mode)的概念。嚴格模式是一種不同的JavaScript解析和執行模型,ECMAScript3的一些不規範寫法在這種模式下會被處理,對於不安全的活動將丟擲錯誤。要對整個指令碼啟用嚴格模式,在指令碼開頭加上一行:"use strict";

也可以單獨指定一個函式在嚴格模式下執行,只要把這個預處理指令放到函式體開頭即可:

function doSomething(){

  "use strict";

  //函式體

}

3.4變數

ECMAScript變數是鬆散型別的,意思是變數可以用於儲存任何型別的資料。有3個關鍵字可以宣告變數:var,const和let.其中,var在ECMAScript的所有版本中都可以使用,而const和let只能在ECMAScript6及更晚的版本中使用。

3.4.1 var 關鍵字

var關鍵字可以用它儲存任何型別的值:var 變數名

在不初始化的情況下,變數會儲存一個特殊值undefined。ECMAScript實現變量出事話,可以同時定義變數並設定它的值。

var a = 1;

不僅可以改變儲存的值,還可以改變值的型別

a = "hello";

1.var宣告作用域

使用var操作符定義的變數會成為包含它的函式的區域性變數。比如,使用var在一個函式內部定義一個變數就意味著該變數將在函式退出時被銷燬。

function test() {
var message = "hi"; // 區域性變數
}
test();
console.log(message); 
// 出錯!

如果在函式內定義變數時省略var操作符,可以建立一個全域性變數

function test() {
message = "hi"; //  全域性變數
}
test();
console.log(message); // "hi"

2.宣告提升(預解析)

        // 1.我們js引擎執行js 分為兩步:預解析 程式碼執行
        // (1)預解析 js引擎會把js裡面所有的var 還有function 提升到當前作用域的最前面
        // (2)程式碼執行 按照程式碼書寫的順序從上往下執行
        // 2.預解析分為 變數預解析(變數提升) 和函式預解析(函式提升)
        // (1)變數提升 就是把所有的變數宣告提升到當前的作用域最前面 不提升賦值操作
        // (2)函式提升  就是把所有的函式宣告提升到當前作用域的最前面 不呼叫函式
        // fn();// 11
        // function fn(){
        //     console.log(11);
        // }

        // 預解析案例
        // 案例1
        var num = 10;
        fun();

        function fun() {
            console.log(num); //undefined
            var num = 20;
        }
        // 案例2
        var num = 10;

        function fn() {
            console.log(num); //undefined
            var num = 20;
            console.log(num); //20
        }
        fn();
        // 案例3
        var a = 18;
        f1();

        function f1() {
            var b = 9;
            console.log(a); //undefined
            console.log(b); //9
            var a = '123';
        }
        // 案例4
        f2();
        console.log(a);
        console.log(b);
        console.log(c);

        function f2() {
            var a = b = c = 9;
            console.log(a);
            console.log(b);
            console.log(c);
        }
        // 等價為以下程式碼
        function f2() {
            var a;
            a = b = c = 9;
            // 相當於var a = 9;b = 9;c=9; b和c直接賦值 沒有var 宣告 當全域性變數看
            //集體宣告 var a = 9,b=9,c=9;與上面不一樣
            console.log(a); //9
            console.log(b); //9
            console.log(c); //9
        }
        f2();
        console.log(c); //9
        console.log(b); //9
        console.log(a); //報錯

3.4.2 let宣告

let跟var的作用差不多,但有著非常重要的區別。最明顯的區別是let宣告的範圍是塊作用域,而var宣告的範圍是函式作用域。

1.暫時性死區

let與var的另一個區別就是let宣告的變數不會在作用域中提升.

// age 不會被提升
console.log(age); // ReferenceError:age 沒有定義
let age = 26;

2.全域性宣告

與var關鍵字不同,使用let在全域性作用域中宣告的變數不會成為window物件的屬性

var name = 'Matt';
console.log(window.name); // 'Matt'
let age = 26;
console.log(window.age); // undefined

3.for 迴圈中的 let 宣告

在使用 let 宣告迭代變數時,JavaScript 引擎在後臺會為每個迭代迴圈宣告一個新的迭代變數。
每個 setTimeout 引用的都是不同的變數例項,所以 console.log 輸出的是我們期望的值,也就是循
環執行過程中每個迭代變數的值。

for (let i = 0; i < 5; ++i) {
setTimeout(() => console.log(i), 0)
}
// 會輸出 0、1、2、3、4

這種每次迭代宣告一個獨立變數例項的行為適用於所有風格的 for 迴圈,包括 for-in 和 for-of迴圈。

3.4.3 const宣告

const的行為與let基本相同,唯一一個重要的區別是用它宣告變數時必須同時初始化變數,且嘗試修改const宣告的變數會導致執行時錯誤。(即const宣告建立為常量)

但是如果const變數引用的是一個物件,那麼修改這個物件內部的屬性並不違反const的限制

const person = {};
person.name = 'Matt'; // ok

只想用 const 宣告一個不會被修改的 for 迴圈變數,那也是可以的。每次迭代只是建立一個新變數。這對 for-of 和 for-in 迴圈特別有意義:

let i = 0;
for (const j = 7; i < 5; ++i) {
console.log(j);
}
// 7, 7, 7, 7, 7
for (const key in {a: 1, b: 2}) {
console.log(key);
}
// a, b
for (const value of [1,2,3,4,5]) {
console.log(value);
}
// 1, 2, 3, 4, 5

不能用const來宣告迭代變數(因為迭代變數會自增)

const宣告的作用域也是塊

3.5宣告風格及最佳實踐


1.不使用var:只使用let和const有助於提升程式碼質量,因為變數有了明確的作用域、宣告位置及不變的值

2.const優先,let次之

3.6資料型別

ECMAScript 有 6 種簡單資料型別(也稱為原始型別): Undefined 、 Null 、 Boolean 、 Number 、String 和 Symbol 。 Symbol (符號)是 ECMAScript 6 新增的。還有一種複雜資料型別叫 Object (對象)。 Object 是一種無序名值對的集合。

3.6.1 typeof操作符

typeof操作符可以確定任意變數的資料型別.

對一個值使用 typeof 操作符會返回下列字串之一:
"undefined" 表示值未定義;
"boolean" 表示值為布林值;
"string" 表示值為字串;
"number" 表示值為數值;
"object" 表示值為物件(而不是函式)或 null ;
"function" 表示值為函式;
"symbol" 表示值為符號。

        var num = 10;
        console.log(typeof num);//number
        var str = 'pig';
        console.log(typeof str);//string
        var flag = true;
        console.log(typeof flag);//boolean
        var vari = undefined;
        console.log(typeof vari);//undefined
        var timer = null;
        console.log(typeof timer);//object

3.6.2 Undefined型別

當使用var或let聲明瞭變數但沒有初始化時,就相當於給變數富裕了undefined值。

3.6.3 Null型別

邏輯上講,null值表示一個空物件指標,這也算給typeof傳一個null會返回"object"的原因

3.6.4 Boolean型別

有兩個字面值:true和false。 這兩個布林值不同於數值。因此true不等於1,false不等於0

3.6.5 Nmber型別

最基本的數值字面量格式是十進位制整數,整數也可以用八進位制(以8為基數,第一個數字必須是零)或十六進位制(以16為基數,數值字首為0x)

let intNum = 55; // 整數
let octalNum1 = 070; // 八進位制的 56
let octalNum2 = 079; // 無效的八進位制值,當成 79 處理
let octalNum3 = 08; // 無效的八進位制值,當成 8 處理
let hexNum1 = 0xA; // 十六進位制 10
let hexNum2 = 0x1f; // 十六進位制 31

由於記憶體的限制,ECMAScript 並不支援表示這個世界上的所有數值。ECMAScript 可以表示的最小數值儲存在 Number.MIN_VALUE 中,這個值在多數瀏覽器中是 5e324;可以表示的最大數值儲存在Number.MAX_VALUE 中,這個值在多數瀏覽器中是 1.797 693 134 862 315 7e+308。如果某個計算得到的數值結果超出了 JavaScript 可以表示的範圍,那麼這個數值會被自動轉換為一個特殊的 Infinity (無窮)值。任何無法表示的負數以 Infinity (負無窮大)表示,任何無法表示的正數以 Infinity (正無窮大)表示。

因此,要確定一個值是不是有限大(即介於 JavaScript 能表示的最小值和最大值之間),可以使用 isFinite() 函式

NaN:Not a Number .用於表示本來要返回的數值的操作失敗了(而不是丟擲失誤)。用isNaN()可以判斷引數是否"不是數值"

3.6.6 String型別

String(字串)資料型別表示零或多個16位Unicode字元序列。字串可以使用雙引號(")、單引號(')或反引號(`)標示

1.字元字面量

字面量 含義
\n 換行
\t 製表
\b 退格
\r 回車
\f 換頁
\\ 反斜槓( \ )
\' 單引號( ' ),在字串以單引號標示時使用,例如 'He said, \'hey.\''
\" 雙引號( " ),在字串以雙引號標示時使用,例如 "He said, \"hey.\""
\` 反引號( ` ),在字串以反引號標示時使用,例如 `He said, \`hey.\``

字串的長度可以通過其length屬性獲取。

2.轉換為字串

        // 1.把其它型轉換為字串型  變數.toString() null和undefined值沒有toString()方法
        var num = 18;
        console.log(num.toString());
        //2.利用String() 強制轉換
        console.log(String(num));
        // 3.利用 + 拼接字串實現轉換為字串型
        console.log(num + '');

3.模板字面量

可以跨行定義字串。在定義模板時特別有用。

let myMultiLineString = 'first line\nsecond line';
let myMultiLineTemplateLiteral = `first line
second line`;
console.log(myMultiLineString);
// first line
// second line"

console.log(myMultiLineTemplateLiteral);
// first line
// second line

console.log(myMultiLineString === myMultiLinetemplateLiteral); // true

4.字串插值

模板字面量最常用的一個特性是支援字串插值。字串插值通過在${}中使用一個JavaScript表示式實現,所有插入的值都會用toString()強制轉型為字串,巢狀的模板字串無須轉義

let value = 5;
let exponent = 'second';
let interpolatedTemplateLiteral =
`${ value } to the ${ exponent } power is ${ value * value }`;
console.log(interpolatedTemplateLiteral); // 5 to the second power is 25
console.log(`Hello, ${ `World` }!`); // Hello, World!

5.原始字串

使用模板字面量也可以直接獲取原始的模板字面量內容(如換行符或Unicode字元),而不是被轉換後的字元表示。為此,可以使用預設的String.raw標籤函式

// Unicode 示例
// \u00A9 是版權符號
console.log(`\u00A9`); // ©
console.log(String.raw`\u00A9`); // \u00A9
// 換行符示例
console.log(`first line\nsecond line`);
// first line
// second line
console.log(String.raw`first line\nsecond line`); // "first line\nsecond line"

3.6.7 Object型別

ECMAScript中的物件其實就是一組資料和功能的集合。物件通過 new 操作符後跟物件型別的名稱來建立。開發者可以通過建立 Object 型別的例項來建立自己的物件,然後再給物件新增屬性和方法。

3.6.8 Symbol型別

ECMAScript6新增的資料型別。符號是原始值,且符號例項是唯一、不可變的。符號的用途是確保物件屬性使用唯一識別符號,不會發生屬性衝突的危險。

3.7操作符

3.7.1 一元操作符: 遞增/遞減操作符(++/--)、一元加和減(+=/-=)

3.7.2 位操作符

1.按位非(~):返回數值的一補數(對數值取反並減1)

let num1 = 25; // 二進位制 00000000000000000000000000011001
let num2 = ~num1; // 二進位制 11111111111111111111111111100110
console.log(num2); // -26

2.按位與(&):將兩個數的每一個位對齊,對每一位執行相應的與操作(都是1返回1,在任何一位是0時返回0)

let result = 25 & 3;
console.log(result); // 1
/*
25 = 0000 0000 0000 0000 0000 0000 0001 1001
 3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
AND = 0000 0000 0000 0000 0000 0000 0000 0001
*/

3.按位或(|):至少一位是1時返回1,兩位都是0時返回0

let result = 25 | 3;
console.log(result); // 27
/*
25 = 0000 0000 0000 0000 0000 0000 0001 1001
 3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
OR = 0000 0000 0000 0000 0000 0000 0001 1011
*/

4.按位異或(^):只在一位上是1時返回1(相同為0,相異為1)

let result = 25 ^ 3;
console.log(result); // 26
/*
25 = 0000 0000 0000 0000 0000 0000 0001 1001
 3 = 0000 0000 0000 0000 0000 0000 0000 0011
---------------------------------------------
XOR = 0000 0000 0000 0000 0000 0000 0001 1010
*/

5.左移(<<):按照指定的位數將數值的所有位向左移動

let oldValue = 2; // 等於二進位制 10
let newValue = oldValue << 5; // 等於二進位制 1000000,即十進位制 64

6.有符號右移(>>):按照指定的位數將數值的所有位向右移動,同時保留符號(正或負)

let oldValue = 64; // 等於二進位制 1000000
let newValue = oldValue >> 5; // 等於二進位制 10,即十進位制 2

7.無符號右移(>>>):按照指定的位數將數值的所有位向右移動(對於負數,有時候差異會非常大)

let oldValue = 64; // 等於二進位制 1000000
let newValue = oldValue >>> 5; // 等於二進位制 10,即十進位制 2

3.7.3 布林操作符

1.邏輯非(!):將運算元轉換為布林值,再取反

邏輯非操作符會遵循如下規則。
如果運算元是物件,則返回 false 。
如果運算元是空字串,則返回 true 。
如果運算元是非空字串,則返回 false 。
如果運算元是數值 0,則返回 true 。
如果運算元是非 0 數值(包括 Infinity ),則返回 false 。
如果運算元是 null ,則返回 true 。
如果運算元是 NaN ,則返回 true 。
如果運算元是 undefined ,則返回 true 。

2.邏輯與(&&):邏輯與操作符可用於任何型別的運算元,不限於布林值。如果有運算元不是布林值,則邏輯與並不一定會返回布林值,而是遵循如下規則。

如果第一個運算元是物件,則返回第二個運算元。
如果第二個運算元是物件,則只有第一個運算元求值為 true 才會返回該物件。
如果兩個運算元都是物件,則返回第二個運算元。
如果有一個運算元是 null ,則返回 null 。
如果有一個運算元是 NaN ,則返回 NaN 。
如果有一個運算元是 undefined ,則返回 undefined 。

3.邏輯或(||):與邏輯與類似,如果有一個運算元不是布林值,那麼邏輯或操作符也不一定返回布林值。它遵循如下規則。

如果第一個運算元是物件,則返回第一個運算元。
如果第一個運算元求值為 false ,則返回第二個運算元。
如果兩個運算元都是物件,則返回第一個運算元。
如果兩個運算元都是 null ,則返回 null 。
如果兩個運算元都是 NaN ,則返回 NaN 。
如果兩個運算元都是 undefined ,則返回 undefined 。

3.7.4 乘性操作符

1.乘法操作符(*):可用於計算兩個數值的乘積

2.除法操作符(/):用於計算第一個運算元除以第二運算元的商

3.取模操作符(%):用於求餘數

3.7.5 指數操作符

ECMAScript 7新增了指數操作符, Math.pow() 現在有了自己的操作符 ** ,結果是一樣的:

console.log(Math.pow(3, 2); // 9
console.log(3 ** 2); // 9
console.log(Math.pow(16, 0.5); // 4
console.log(16** 0.5); // 4

3.7.6 加性操作符

(1)加法操作符+

如果兩個運算元都是數值,加法操作符執行加法運算並根據如下規則返回結果:
如果有任一運算元是 NaN ,則返回 NaN ;
如果是 Infinity 加 Infinity ,則返回 Infinity ;
如果是 -Infinity 加 -Infinity ,則返回 -Infinity ;
如果是 Infinity 加 -Infinity ,則返回 NaN ;
如果是 +0 加 +0 ,則返回 +0 ;
如果是 -0 加 +0 ,則返回 +0 ;
如果是 -0 加 -0 ,則返回 -0 。

(2)減法操作符-

與加法操作符一樣,減法操作符也有一組規則用於處理 ECMAScript 中不同型別之間的轉換。
如果兩個運算元都是數值,則執行數學減法運算並返回結果。
如果有任一運算元是 NaN ,則返回 NaN 。
如果是 Infinity 減 Infinity ,則返回 NaN 。
如果是 -Infinity 減 -Infinity ,則返回 NaN 。
如果是 Infinity 減 -Infinity ,則返回 Infinity 。
如果是 -Infinity 減 Infinity ,則返回 -Infinity 。
如果是 +0 減 +0 ,則返回 +0 。
如果是 +0 減 -0 ,則返回 -0 。
如果是 -0 減 -0 ,則返回 +0 。
如果有任一運算元是字串、布林值、 null 或 undefined ,則先在後臺使用 Number() 將其轉換為數值,然後再根據前面的規則執行數學運算。如果轉換結果是 NaN ,則減法計算的結果是NaN 。

3.7.7 關係操作符

小於<、大於>、小於等於≤、大於等於≥ 這幾個操作符都返回布林值

3.7.8 相等操作符

1.等於和不等於

表示式 結果
null == undefined true
"NaN" == NaN false
5 == NaN false
NaN == NaN false
NaN != NaN true
false == 0 true
true == 1 true
true == 2 false
undefined == 0 false
null == 0 false
"5" == 5 true

2.全等和不全等

全等和不全等操作符與相等和不相等操作符類似,只不過它們在比較相等時不轉換運算元。全等操作符由 3 個等於號( === )表示,只有兩個運算元在不轉換的前提下相等才返回 true ,比如:

let result1 = ("55" == 55); // true,轉換後相等
let result2 = ("55" === 55); // false,不相等,因為資料型別不同

3.7.9 條件操作符

條件表示式?表示式1:表示式2

3.8語句

ECMA-262 描述了一些語句(也稱為流控制語句),而 ECMAScript 中的大部分語法都體現在語句中。

3.8.1 if語句

//if 語句是使用最頻繁的語句之一,語法如下:
if (condition) statement1 else statement2
//可以像這樣連續使用多個 if 語句:
if (condition1) statement1 else if (condition2) statement2 else statement3

3.8.2 do-while語句

do-while 語句是一種後測試迴圈語句,即迴圈體中的程式碼執行後才會對退出條件進行求值。換句話說,迴圈體內的程式碼至少執行一次。 do-while 的語法如下:

do {
statement
} while (expression);

3.8.3 while語句

while 語句是一種先測試迴圈語句,即先檢測退出條件,再執行迴圈體內的程式碼。因此, while 迴圈體內的程式碼有可能不會執行。下面是 while 迴圈的語法:

while(expression) statement

3.8.4 for語句

for(初始化;條件表示式;迴圈後表示式){

  statement;

}

初始化、條件表示式和迴圈後表示式都不是必需的.

for (;;) { // 無窮迴圈
doSomething();
}

3.8.5 for-in語句

用於列舉物件中的非符號鍵屬性

for(property in expression) statement

3.8.6 for-of語句

用於遍歷可迭代物件的元素

for(property of expression) statement

3.8.7 標籤語句

用於給語句加標籤,典型應用是巢狀迴圈

label:statement

start: for (let i = 0; i < count; i++) {
console.log(i);
}

3.8.8 break和continue語句

break語句:立即退出整個迴圈體

continue語句:跳過當前迴圈的剩餘語句,進入下一次迴圈

3.8.9 with語句

將程式碼作用域設定為特定的物件

with(expression) statement;

主要場景是針對一個物件反覆操作,這時候將程式碼作用域設定為該物件能提供便利

3.8.10 switch語句

expression可以用於所有資料型別;條件的值不需要是常量,也可以是變數或表示式

switch(expression){

case value1:statement;

      break;

case value2:statement;

      break;

……

default:statement;

}

3.9函式

函式對任何語言來說都是核心元件,因為它們可以封裝語句,然後在任何地方、任何時間執行。ECMAScript 中的函式使用 function 關鍵字宣告,後跟一組引數,然後是函式體。

function functionName(arg0, arg1,...,argN) {
statements
}

要注意的是,只要碰到 return 語句,函式就會立即停止執行並退出。因此, return 語句後面的程式碼不會被執行

function sum(num1, num2) {
return num1 + num2;
console.log("Hello world"); //  不會執行
}

嚴格模式對函式也有一些限制:
函式不能以 eval 或 arguments 作為名稱;
函式的引數不能叫 eval 或 arguments ;
兩個命名引數不能擁有同一個名稱。
如果違反上述規則,則會導致語法錯誤,程式碼也不會執行。