JavaScript基礎知識4(數組、函數、參數、作用域、具名和匿名函數、邏輯運算符短路、轉化布爾類型)
- 數組
- 數組是什么?
- 數組的基本使用
- 定義數組和數組單元
- 訪問數組和數組索引
- 數據單元值類型
- 數組長度屬性
- 操作數組
- 函數
- 聲明和調用
- 聲明(定義)
- 調用
- 參數
- 形參和實參
- 形參的默認值
- 返回值
- 如何返回多個值
- 作用域
- 1. 全局作用域
- 2. 函數作用域
- 3. 塊級作用域
- 示例:全局作用域、函數作用域和塊級作用域
- 變量提升(Hoisting)
- 塊級作用域和函數作用域對比
- 總結
- 變量的訪問原則
- 具名函數和匿名函數
- 具名函數
- 定義和使用
- 匿名函數
- 定義和使用
- 匿名函數和具名函數的比較
- 示例對比
- 使用場景
- 總結
- 函數表達式
- 邏輯運算符短路
- 短路特性示例
- `&&`(邏輯與)
- `||`(邏輯或)
- 實際應用
- 默認值
- 條件執行
- 示例代碼
- 總結
- 轉化為Boolean型
數組
知道什么是數組及其應用的場景,掌握數組聲明及訪問的語法。
數組是什么?
數組:(Array)是一種可以按順序保存數據的數據類型
**使用場景:**如果有多個數據可以用數組保存起來,然后放到一個變量中,管理非常方便
數組的基本使用
定義數組和數組單元
<script>// 1. 語法,使用 [] 來定義一個空數組// 定義一個空數組,然后賦值給變量 classes// let classes = [];// 2. 定義非空數組let classes = ['小明', '小剛', '小紅', '小麗', '小米']
</script>
通過 []
定義數組,數據中可以存放真正的數據,如小明、小剛、小紅等這些都是數組中的數據,我們這些數據稱為數組單元,數組單元之間使用英文逗號分隔。
訪問數組和數組索引
使用數組存放數據并不是最終目的,關鍵是能夠隨時的訪問到數組中的數據(單元)。其實 JavaScript 為數組中的每一個數據單元都編了號,通過數據單元在數組中的編號便可以輕松訪問到數組中的數據單元了。
我們將數據單元在數組中的編號稱為索引值,也有人稱其為下標。
索引值實際是按著數據單元在數組中的位置依次排列的,注意是從下標 0
開始的
觀察上圖可以數據單元【小明】對應的索引值為【0】,數據單元【小紅】對應的索引值為【2】
<script>let classes = ['小明', '小剛', '小紅', '小麗', '小米']// 1. 訪問數組,語法格式為:變量名[索引值]document.write(classes[0]) // 結果為:小明document.write(classes[1]) // 結果為:小剛document.write(classes[4]) // 結果為:小米// 2. 通過索引值還可以為數組單重新賦值document.write(classes[3]) // 結果為:小麗// 重新為索引值為 3 的單元賦值classes[3] = '小小麗'document.wirte(classes[3]); // 結果為: 小小麗
</script>
數據單元值類型
數組做為數據的集合,它的單元值可以是任意數據類型
<script>// 6. 數組單值類型可以是任意數據類型// a) 數組單元值的類型為字符類型let list = ['HTML', 'CSS', 'JavaScript']// b) 數組單元值的類型為數值類型let scores = [78, 84, 70, 62, 75]// c) 混合多種類型let mixin = [true, 1, false, 'hello']
</script>
數組長度屬性
重申一次,數組在 JavaScript 中并不是新的數據類型,它屬于對象類型。
<script>// 定義一個數組let arr = ['html', 'css', 'javascript']// 數組對應著一個 length 屬性,它的含義是獲取數組的長度console.log(arr.length) // 3
</script>
操作數組
數組做為對象數據類型,不但有 length
屬性可以使用,還提供了許多方法:
1. push 動態向數組的尾部添加一個單元
2. unshit 動態向數組頭部添加一個單元
3. pop 刪除最后一個單元
4. shift 刪除第一個單元
5. splice 動態刪除任意單元
使用以上4個方法時,都是直接在原數組上進行操作,即成功調任何一個方法,原數組都跟著發生相應的改變。并且在添加或刪除單元時 length
并不會發生錯亂。
<script>// 定義一個數組let arr = ['html', 'css', 'javascript']// 1. push 動態向數組的尾部添加一個單元arr.push('Nodejs')console.log(arr)arr.push('Vue')// 2. unshit 動態向數組頭部添加一個單元arr.unshift('VS Code')console.log(arr)// 3. splice 動態刪除任意單元arr.splice(2, 1) // 從索引值為2的位置開始刪除1個單元console.log(arr)// 4. pop 刪除最后一個單元arr.pop()console.log(arr)// 5. shift 刪除第一個單元arr.shift()console.log(arr)
</script>
函數
理解函數的封裝特性,掌握函數的語法規則
聲明和調用
函數可以把具有相同或相似邏輯的代碼“包裹”起來,通過函數調用執行這些被“包裹”的代碼邏輯,這么做的優勢是有利于精簡代碼方便復用。
聲明(定義)
聲明(定義)一個完整函數包括關鍵字、函數名、形式參數、函數體、返回值5個部分
在JavaScript中,你可以使用以下兩種方式來聲明函數:
-
函數聲明(Function Declaration):
function functionName(parameters) {// 函數體 }
使用函數聲明方式可以在任何地方聲明函數,包括在其他函數內部。函數聲明會被提升(hoisted),這意味著你可以在函數聲明之前調用該函數。
-
函數表達式(Function Expression):
var functionName = function(parameters) {// 函數體 };
使用函數表達式方式將函數賦值給一個變量。函數表達式可以是匿名的,也可以是具名的。函數表達式在代碼執行到達時創建,因此你必須在函數表達式之后才能調用該函數。
以下是一個示例,展示了如何使用函數聲明和函數表達式來定義函數:
// 函數聲明
function greet(name) {console.log('Hello, ' + name + '!');
}greet('Alice'); // 調用函數// 函數表達式(匿名)
var sayHello = function() {console.log('Hello!');
};sayHello(); // 調用函數// 函數表達式(具名)
var multiply = function(a, b) {return a * b;
};var result = multiply(5, 3); // 調用函數并接收返回值
console.log(result); // 輸出結果
無論你選擇使用函數聲明還是函數表達式,都可以根據需要來定義和調用函數。請根據你的具體情況選擇適合的方式。
調用
聲明(定義)的函數必須調用才會真正被執行,使用 ()
調用函數。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JavaScript 基礎 - 聲明和調用</title>
</head>
<body><script>// 聲明(定義)了最簡單的函數,既沒有形式參數,也沒有返回值function sayHi() {console.log('嗨~')}// 函數調用,這些函數體內的代碼邏輯會被執行// 函數名()sayHi()// 可以重復被調用,多少次都可以sayHi()</script>
</body>
</html>
注:函數名的命名規則與變量是一致的,并且盡量保證函數名的語義。
小案例: 小星星
<script>// 函數聲明function sayHi() {// document.write('hai~')document.write(`*<br>`)document.write(`**<br>`)document.write(`***<br>`)document.write(`****<br>`)document.write(`*****<br>`)document.write(`******<br>`)document.write(`*******<br>`)document.write(`********<br>`)document.write(`*********<br>`)}// 函數調用sayHi()sayHi()sayHi()sayHi()sayHi()</script>
參數
通過向函數傳遞參數,可以讓函數更加靈活多變,參數可以理解成是一個變量。
聲明(定義)一個功能為打招呼的函數
- 傳入數據列表
- 聲明這個函數需要傳入幾個數據
- 多個數據用逗號隔開
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JavaScript 基礎 - 函數參數</title>
</head>
<body><script>// 聲明(定義)一個功能為打招呼的函數// function sayHi() {// console.log('嗨~')// }// 調用函數// sayHi()// 這個函數似乎沒有什么價值,除非能夠向不同的人打招呼// 這就需要借助參數來實現了function sayHi(name) {// 參數 name 可以被理解成是一個變量console.log(name)console.log('嗨~' + name)}// 調用 sayHi 函數,括號中多了 '小明'// 這時相當于為參數 name 賦值了sayHi('小明')// 結果為 小明// 再次調用 sayHi 函數,括號中多了 '小紅'// 這時相當于為參數 name 賦值了sayHi('小紅') // 結果為 小紅</script>
</body>
</html>
總結:
- 聲明(定義)函數時的形參沒有數量限制,當有多個形參時使用
,
分隔 - 調用函數傳遞的實參要與形參的順序一致
形參和實參
形參:聲明函數時寫在函數名右邊小括號里的叫形參(形式上的參數)
實參:調用函數時寫在函數名右邊小括號里的叫實參(實際上的參數)
形參可以理解為是在這個函數內聲明的變量(比如 num1 = 10)實參可以理解為是給這個變量賦值
開發中盡量保持形參和實參個數一致
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JavaScript 基礎 - 函數參數</title>
</head>
<body><script>// 聲明(定義)一個計算任意兩數字和的函數// 形參 x 和 y 分別表示任意兩個數字,它們是兩個變量function count(x, y) {console.log(x + y);}// 調用函數,傳入兩個具體的數字做為實參// 此時 10 賦值給了形參 x// 此時 5 賦值給了形參 ycount(10, 5); // 結果為 15</script>
</body>
</html>
形參的默認值
用戶不輸入實參,可以給形參默認值,可以默認為0,這樣程序更嚴謹,可以如下操作:
function getSum(x = 0, y = 0) {
document .write(x + y)
}
getSum() // 結果是0,而不是N
getSum(1, 2) //結果是3
說明:這個默認值只會在缺少實參參數傳遞時才會被執行,所以有參數會優先執行傳遞過來的實參,否則默認為undefined
返回值
函數的本質是封裝(包裹),函數體內的邏輯執行完畢后,函數外部如何獲得函數內部的執行結果呢?要想獲得函數內部邏輯的執行結果,需要通過 return
這個關鍵字,將內部執行結果傳遞到函數外部,這個被傳遞到外部的結果就是返回值。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JavaScript 基礎 - 函數返回值</title>
</head>
<body><script>// 定義求和函數function count(a, b) {let s = a + b// s 即為 a + b 的結果// 通過 return 將 s 傳遞到外部return s}// 調用函數,如果一個函數有返回值// 那么可將這個返回值賦值給外部的任意變量let total = count(5, 12)</script>
</body>
</html>
總結:
- 在函數體中使用return 關鍵字能將內部的執行結果交給函數外部使用
- 函數內部只能出現1 次 return,并且 return 下一行代碼不會再被執行,所以return 后面的數據不要換行寫
- return會立即結束當前函數
- 函數可以沒有return,這種情況默認返回值為 undefined
如何返回多個值
在 JavaScript 中,可以使用數組、對象或解構賦值的方式來返回多個值。
方法一:使用數組
function returnMultipleValues() {var value1 = 10;var value2 = 'Hello';var value3 = true;var result = [value1, value2, value3];return result;
}// 調用函數并獲取返回的多個值
var values = returnMultipleValues();
var value1 = values[0];
var value2 = values[1];
var value3 = values[2];
在這個例子中,returnMultipleValues
函數返回一個包含三個值的數組 result
。通過調用該函數并將返回值賦給變量 values
,我們可以通過索引來獲取每個值。
方法二:使用對象
function returnMultipleValues() {var value1 = 10;var value2 = 'Hello';var value3 = true;var result = {value1: value1,value2: value2,value3: value3};return result;
}// 調用函數并獲取返回的多個值
var values = returnMultipleValues();
var value1 = values.value1;
var value2 = values.value2;
var value3 = values.value3;
在這個例子中,returnMultipleValues
函數返回一個包含三個值的對象 result
。通過調用該函數并將返回值賦給變量 values
,我們可以通過屬性名來獲取每個值。
方法三:使用解構賦值
function returnMultipleValues() {var value1 = 10;var value2 = 'Hello';var value3 = true;return [value1, value2, value3];
}// 調用函數并使用解構賦值獲取返回的多個值
var [value1, value2, value3] = returnMultipleValues();
在這個例子中,returnMultipleValues
函數返回一個包含三個值的數組。通過調用該函數并使用解構賦值,我們可以直接將返回值的每個元素分配給對應的變量。
作用域
作用域(Scope)在編程中指的是變量、函數和對象在代碼中的可訪問性范圍。JavaScript 中的作用域主要分為以下幾種:
- 全局作用域(Global Scope)
- 函數作用域(Function Scope)
- 塊級作用域(Block Scope)
1. 全局作用域
在最外層定義的變量或函數擁有全局作用域,它們可以在代碼中的任何地方訪問。
var globalVar = "I am global";function test() {console.log(globalVar); // 可以訪問全局變量
}test(); // 輸出:I am global
console.log(globalVar); // 輸出:I am global
2. 函數作用域
在函數內部定義的變量只能在該函數內部訪問,它們具有函數作用域。
function test() {var localVar = "I am local";console.log(localVar); // 可以訪問局部變量
}test(); // 輸出:I am local
console.log(localVar); // 報錯:localVar 未定義
3. 塊級作用域
使用 let
和 const
關鍵字在塊(如 if
語句、for
循環等)內部定義的變量,具有塊級作用域,只能在塊內部訪問。var
聲明的變量不具備塊級作用域。
if (true) {let blockVar = "I am block scoped";console.log(blockVar); // 可以訪問塊級變量
}
console.log(blockVar); // 報錯:blockVar 未定義
示例:全局作用域、函數作用域和塊級作用域
var globalVar = "global";function functionScope() {var functionVar = "function scoped";if (true) {let blockVar = "block scoped";console.log(globalVar); // 可以訪問全局變量console.log(functionVar); // 可以訪問函數內變量console.log(blockVar); // 可以訪問塊級變量}console.log(blockVar); // 報錯:blockVar 未定義
}functionScope();console.log(globalVar); // 輸出:global
console.log(functionVar); // 報錯:functionVar 未定義
console.log(blockVar); // 報錯:blockVar 未定義
變量提升(Hoisting)
JavaScript 的變量提升指的是變量聲明被提升到其作用域的頂部。在實際執行時,變量聲明會被提升到作用域的頂部,但賦值不會被提升。只有 var
聲明的變量會被提升,而 let
和 const
聲明的變量不會。
console.log(hoistedVar); // 輸出:undefined
var hoistedVar = "I am hoisted";function hoistingExample() {console.log(hoistedFunctionVar); // 輸出:undefinedvar hoistedFunctionVar = "I am hoisted in function";
}hoistingExample();
塊級作用域和函數作用域對比
function blockVsFunctionScope() {if (true) {var functionScoped = "I am function scoped";let blockScoped = "I am block scoped";}console.log(functionScoped); // 可以訪問,輸出:I am function scopedconsole.log(blockScoped); // 報錯:blockScoped 未定義
}blockVsFunctionScope();
總結
- 全局作用域:變量在任何地方都可以訪問。
- 函數作用域:變量只能在函數內部訪問。
- 塊級作用域:使用
let
和const
聲明的變量只能在塊內部訪問。 - 變量提升:
var
聲明的變量會被提升,但let
和const
聲明的變量不會。
理解作用域有助于編寫清晰和錯誤更少的代碼。正確地使用不同作用域的變量聲明方式,可以提高代碼的可維護性和可讀性。
變量的訪問原則
- 只要是代碼,就至少有一個作用域
- 寫在函數內部的局部作用域
- 如果函數中還有函數, 那么在這個作用域中就又可以誕生一個作用域
- 訪問原則:在能夠訪問到的情況下先局部,局部沒有在找全局
- 變量訪問原則是什么?
?采取就近原則的方式來查找變量最終的值
具名函數和匿名函數
具名函數
具名函數是指有名字的函數,可以通過其名字在代碼的任何地方調用。具名函數通常用于遞歸和需要重復調用的場景。
定義和使用
function greet() {console.log("Hello, World!");
}greet(); // 調用函數,輸出 "Hello, World!"
具名函數也可以用于遞歸調用:
function factorial(n) {if (n <= 1) {return 1;}return n * factorial(n - 1);
}console.log(factorial(5)); // 輸出 120
匿名函數
匿名函數是沒有名字的函數,通常用作立即執行函數表達式(IIFE)、回調函數或賦值給變量。匿名函數在定義時不能通過名字調用,但可以通過其賦值的變量或在特定上下文中使用。
定義和使用
- 賦值給變量:
let greet = function() {console.log("Hello, World!");
};greet(); // 調用函數,輸出 "Hello, World!"
- 立即執行函數表達式(IIFE):
(function() {console.log("This is an IIFE!");
})(); // 立即執行,輸出 "This is an IIFE!"(function(x,y) {console.log(x+y);
})(1,2); //輸出3
無需調用,立即執行,其實本質已經調用了
多個立即執行函數之間用分號隔開
- 作為回調函數:
setTimeout(function() {console.log("This is a callback function!");
}, 1000); // 1秒后輸出 "This is a callback function!"
匿名函數和具名函數的比較
- 命名:具名函數有函數名,而匿名函數沒有。
- 可讀性:具名函數通過名字調用,代碼可讀性較高;匿名函數通常用于一次性任務或回調。
- 調試:具名函數在調試時有函數名標識,更易于跟蹤;匿名函數在調試工具中顯示為匿名。
- 遞歸:具名函數可以自調用,而匿名函數在沒有賦值給變量的情況下無法自調用。
示例對比
具名函數示例:
function add(a, b) {return a + b;
}console.log(add(3, 4)); // 輸出 7
匿名函數示例:
let add = function(a, b) {return a + b;
};console.log(add(3, 4)); // 輸出 7
使用場景
- 具名函數:適用于需要重復調用、遞歸或提高代碼可讀性的場景。
- 匿名函數:適用于一次性任務、回調函數或立即執行的場景。
總結
具名函數和匿名函數各有其優勢和適用場景。具名函數通過名字調用,適用于遞歸和提高代碼可讀性的場景;匿名函數適用于回調、立即執行和賦值給變量的場景。根據具體需求選擇合適的函數類型,可以編寫出更加簡潔和高效的代碼。
函數表達式
// 聲明
let fn = function() { console.log('函數表達式')
}
// 調用
fn()
邏輯運算符短路
&&
(邏輯與):如果第一個操作數為 false
,則整個表達式的結果一定為 false
,因此不再計算第二個操作數。
||
(邏輯或):如果第一個操作數為true
,則整個表達式的結果一定為true
,因此不再計算第二個操作數。
短路特性示例
&&
(邏輯與)
如果第一個操作數為 false
,表達式短路,直接返回第一個操作數的值,不再計算第二個操作數。
console.log(false && "Hello"); // 輸出 false
console.log(0 && "World"); // 輸出 0
console.log(null && "JavaScript"); // 輸出 null
console.log(undefined && "Programming"); // 輸出 undefined
如果第一個操作數為 true
,表達式繼續計算并返回第二個操作數的值。
console.log(true && "Hello"); // 輸出 "Hello"
console.log(1 && "World"); // 輸出 "World"
console.log("Non-empty string" && "JavaScript"); // 輸出 "JavaScript"
||
(邏輯或)
如果第一個操作數為 true
,表達式短路,直接返回第一個操作數的值,不再計算第二個操作數。
console.log(true || "Hello"); // 輸出 true
console.log(1 || "World"); // 輸出 1
console.log("Non-empty string" || "JavaScript"); // 輸出 "Non-empty string"
如果第一個操作數為 false
,表達式繼續計算并返回第二個操作數的值。
console.log(false || "Hello"); // 輸出 "Hello"
console.log(0 || "World"); // 輸出 "World"
console.log(null || "JavaScript"); // 輸出 "JavaScript"
console.log(undefined || "Programming"); // 輸出 "Programming"
實際應用
短路特性在實際編程中非常有用,可以用來簡化代碼和實現條件邏輯。
默認值
使用 ||
運算符提供默認值:
let name = userInput || "Guest";
如果 userInput
是 null
、undefined
、false
、0
或空字符串,name
將被賦值為 "Guest"
。
條件執行
使用 &&
運算符進行條件執行:
user && user.updateProfile();
只有在 user
存在(非 null
和非 undefined
)的情況下,才會調用 updateProfile
方法。
示例代碼
function getUserName(user) {return user && user.name || "Anonymous";
}let user1 = { name: "Alice" };
let user2 = null;console.log(getUserName(user1)); // 輸出 "Alice"
console.log(getUserName(user2)); // 輸出 "Anonymous"
在這個例子中,如果 user
為 null
或 undefined
,getUserName
函數將返回 "Anonymous"
。如果 user
存在并且有 name
屬性,則返回 user.name
。
總結
&&
(邏輯與):如果第一個操作數為false
,則短路并返回第一個操作數,否則返回第二個操作數。||
(邏輯或):如果第一個操作數為true
,則短路并返回第一個操作數,否則返回第二個操作數。
短路特性有助于提高代碼執行效率,并且可以用來簡化條件邏輯和提供默認值。在編寫 JavaScript 代碼時,充分利用這些特性可以使代碼更加簡潔和高效。
轉化為Boolean型
真值和假值
在布爾轉換中,某些值會被認為是 “真值”(truth),其他值會被認為是 “假值”(false)。
假值(Falsy)列表
以下值在布爾上下文中會被轉換為 false:
- false
- 0
- -0
- 0n(BigInt 零值)
- “”(空字符串)
- null
- undefined
- NaN
其他值則都為真值(truth)
隱式轉換:
- 有字符串的加法’’ ‘’+1,結果是“1"
- 減法- (像大多數數學運算一樣)只能用于數字,它會使空字符串""轉換為0
- null 經過數字轉換之后會變為0
- undefined 經過數字轉換之后會變為NaN