JavaScript學習筆記【DAY3(2020.8.19)週三】
函式:函式就是一個工具,可以將程式碼放在裡面,隨時執行。
就是封裝了一段可被重複呼叫執行的程式碼塊。通過此程式碼塊可以實現大量程式碼的重複使用。
函式的定義
第一種定義方式: 函式宣告
function 函式名() {
要執行的程式碼
}
第二種定義方式: 函式表示式
var 變數 = function() {
要執行的程式碼
}
注: 第二種方式的本質 其實是定義了一個變數,並將這個函式的地址交給該變數。於是,函式名就是這個變數名。
第三種定義方式:new Function()
var f = new Function('a', 'b', 'console.log(a + b)');
f(1, 2);
var fn = new Function('引數1','引數2'..., '函式體')
注意
/*Function 裡面引數都必須是字串格式
第三種方式執行效率低,也不方便書寫,因此較少使用
所有函式都是 Function 的例項(物件)
函式也屬於物件
*/
函式的呼叫
函式宣告定義出來的函式
既可以在函式之前
也可以在函式之後
函式表示式定義的函式
只能夠在後面呼叫
// 呼叫函式
函式名(); // 通過呼叫函式名來執行函式體程式碼
呼叫的時候千萬不要忘記新增小括號
口訣:函式不呼叫,自己不執行
注意:宣告函式本身並不會執行程式碼,只有呼叫函式時才會執行函式體程式碼。
/* 1. 普通函式 */
function fn() {
console.log('人生的巔峰');
}
fn();
/* 2. 物件的方法 */
var o = {
sayHi: function() {
console.log('人生的巔峰');
}
}
o.sayHi();
/* 3.建構函式*/
function Star() {};
new Star();
/* 4. 繫結事件函式*/
btn.onclick = function() {}; // 點選了按鈕就可以呼叫這個函式
/* 5. 定時器函式*/
setInterval(function() {}, 1000); 這個函式是定時器自動1秒鐘呼叫一次
/* 6. 立即執行函式(自呼叫函式)*/
(function() {
console.log('人生的巔峰');
})();
函式的封裝:函式的封裝是把一個或者多個功能通過函式的方式封裝起來,對外只提供一個簡單的函式介面
簡單理解:封裝類似於將電腦配件整合組裝到機箱中 ( 類似快遞打包)
函式的引數
函式整體由 關鍵字、 函式名、 形參列表、 函式體組成。
形參列表中,可以放置 形式引數 。簡稱: 形參。
// 定義一個函式,計算兩個數字的和
function sum(num1, num2) {
// 形參num1 "代表" 將來函式執行的時候,要傳遞的一個引數
// 形參num2 "代表" 將來函式執行的時候,要傳遞的另一個引數
console.log(num1 + num2);
}
// 函式執行了
sum(1, 2); // 輸出3 1和2是實參
當函式執行的時候,可以往圓括號內填寫資料,這種行為,叫做 傳參, 用於函式執行中使用,這樣的引數叫做 實際引數,簡稱 實參。
函式的引數關係
當函式定義時,可以定義形參,當函式執行時,可以傳遞實參。 如果函式在執行時,傳遞的引數與形參不一致,分類如下
當形參比實參多: 多餘的形參的值為undefined
function sum(a, b) {
console.log(a);
console.log(b);
}
sum(10); // a是10 b是undefined
當形參比實參少: 沒有形參接收多餘的實參
function sum(a, b) {
console.log(a);
console.log(b);
}
sum(10, 11, 12); // a是10 b是11 12沒有形參來接收
return關鍵字:該關鍵字是用於在函式內部 返回內容 並 中止函式執行 的。
返回值:函式呼叫整體代表的資料;函式執行完成後可以通過return語句將指定資料返回 。
// 中止函式執行
function demo() {
console.log(1);
console.log(2);
console.log(3);
return;
console.log(4);
console.log(5);
}
demo(); // 只會輸出1 2 3 而不會輸出4 5 因為遇見了return
// 返回內容
var a = 10;
var b = 11;
function sum(num1, num2) {
return num1 + num2;
}
// 進行計算
var result = sum(a, b);
// 使用結果
console.log(result); // 21
注: return關鍵字只能夠在函式內部出現。
break ,continue ,return 的區別
break :結束當前的迴圈體(如 for、while)
continue :跳出本次迴圈,繼續執行下次迴圈(如 for、while)
return :不僅可以退出迴圈,還能夠返回 return 語句中的值,同時還可以結束當前的函式體內的程式碼
arguments
(1)arguments是函式內部的一個成員,只有在函式執行的時候才會存在。可以使用它來獲取本次函式在執行時,所有傳遞的實參。
arguments.length可以獲取實參的個數。
function sum() {
console.log(arguments);
}
sum(1); // => [1]
sum(2); // => [2]
sum(1, 2, 3, 4, 5); // => [1, 2, 3, 4, 5]
(2)arguments是一個類陣列物件,也叫做偽陣列物件。可以使用 arguments[idx] 來獲取對應的值。 idx是 從0開始的。 這個idx,也可以叫做 下標 或者 索引
function sum() {
console.log(arguments[2]);
}
sum(1, 2, 3, 4); // => 3
sum(1, 2); // => undefined
函式的定義過程
(1)在記憶體中開闢一個空間;
(2)把程式碼放進去;
(3)把空間的地址交給函式名或者變數名來儲存。
函式的呼叫過程
(1)根據函式名稱找到記憶體空間
(2)將實參的值傳遞給形參
(3)開始解析記憶體空間中的程式碼
(4)執行程式碼
作用域:指的是變數起作用的範圍
作用域的劃分規則: 只有全域性作用域和函式的私有作用域
作用域是根據書寫的單詞和語法來確定的,所以又叫做詞法作用域
所以,通常我們把程式碼寫完,就可以確定每一個作用域的範圍
注: script標籤之間是全域性作用域 多個script標籤共享同一個作用域 但是每一個script標籤中的變數 函式,它們的提升,只能夠在本script標籤的範圍之內
注: 瀏覽器在載入script標籤的程式碼的時候,是一個script標籤載入並執行完畢之後,再去載入執行後面的一個
局作用域:作用於所有程式碼執行的環境(整個 script 標籤內部)或者一個獨立的 js 檔案。
區域性作用域:作用於函式內的程式碼環境,就是區域性作用域。 因為跟函式有關係,所以也稱為函式作用域。
<script>
var a = 10;
</script>
<script>
console.log(a); // 輸出10
</script>
<script>
console.log(a); // ReferenceError: a is not defined
</script>
<script>
var a = 10;
</script>
函式的屬性
name 它是函式的名稱 函式的名稱不論是表示式還是宣告 都是變數的名字
length 它是函式的形參的個數
function demo(a, b, c) {
}
console.log(demo.name); // "demo"
console.log(demo.length); // 3
var fun = function() {
}
console.log(fun.name); // "fun"
console.log(fun.length); // 0
遞迴函式(重要、瞭解)
遞迴函式指的是函式自己在函式體內部呼叫自己
注: 無停止條件的遞迴 就是死迴圈 寫遞迴函式,先寫停止條件return
// a變數一定要定義在函式外部
var a = 0;
function demo() {
a++;
if (a >= 10) {
return;
}
demo();
}
demo(); // 迴圈讓a自加10次 之後就停止
淺複製和深複製:
1.淺拷貝只是拷貝一層,更深層次物件級別的只拷貝引用.
2.深拷貝拷貝多層,每一級別的資料都會拷貝.
3. Object.assign( target, .. sources) es6 新增方法可以淺拷貝
作用域的特點
作用域的機制
作用域是針對變數的生效的範圍。 變數操作又分為使用變數 與 賦值變數
使用變數
出現在表示式中,賦值語句右側。
訪問變數規則
當訪問變數的時候,會先檢視當前作用域中是否存在該變數
如果有,就使用,並終止查詢。
如果沒有,就將會向上一層級作用域中尋找。
依次向上,直到找到,或者到了全域性作用域中還沒有找到,就會報錯。
賦值變數
只出現在賦值語句左側
賦值變數規則
當對一個變數進行賦值的時候,會先檢視當前作用域中是否存在該變數
如果有,就賦值。
如果沒有,就向上一層級查詢。
依次向上,直到找到,或者到了全域性作用域中還沒有找到
就會在全域性作用域中悄悄的宣告這個變數並賦值。
// 當前是全域性作用域
var num = 100; // 在全域性作用域中定義的變數
// 定義一個函式
function demo() {
var num = 101; // 這是在函式作用域中定義的變數
console.log(num);
}
// 執行函式
demo(); // 101
解釋: 當在函式內部訪問變數num的時候,先看當前作用域中是否有num 找到了! 於是就用101 輸出101
var num = 100; // 在全域性作用域中定義的變數
// 定義一個函式
function demo() {
// var num; // 因為宣告提升的原因 程式碼其實是這樣子的
console.log(num);
var num = 101; // 這是在函式作用域中定義的變數 雖然定義是在這裡 但是提升了
}
// 執行函式
demo(); // undefined
解釋: 依舊是作用域的問題,但是因為輸出程式碼在前面,所以有些同學可能會誤以為會輸出100 但是不要忘記變數的查詢規則與預解析!!!
var num = 100; // 在全域性作用域中定義的變數
// 定義一個函式
function demo() {
var num = 101;
}
// 執行函式
demo();
// num是多少
console.log(num); // 100
解釋: 作用域的問題,因為輸出的程式碼是在全域性作用域中的,所以會直接訪問全域性中的變數 而與函式內部的變數無關
var num = 100; // 在全域性作用域中定義的變數
// 定義一個函式
function demo() {
num = 101;
}
console.log(num); // 100
// 執行函式
demo();
// num是多少
console.log(num); // 101
JS的糟粕之一
// 如果一個變數沒有通過var宣告 就直接賦值
num = 101; // 這種不經過宣告就直接賦值而且還不會報錯的特點屬於JS的糟粕之一。一定不要使用。
console.log(num); // 101
console.log(num);
num = 101;
這種情況下會報錯 因為num沒有經過定義就被使用
JavaScript 程式碼是由瀏覽器中的 JavaScript 解析器來執行的。JavaScript 解析器在執行 JavaScript 程式碼的時候分為兩步:預解析和程式碼執行。
預解析: 在當前作用域下, JS 程式碼執行之前,瀏覽器會預設把帶有 var 和 function 宣告的變數在記憶體中進行提前宣告或者定義。
JS引擎執行程式碼的階段:
通讀程式碼
在通讀程式碼階段,引擎會先檢視是否有語法錯誤,如果有,就報錯。如果沒有,則在這個過程中,就會將程式碼中所有的通過var宣告的變數和通過function宣告的函式提升到程式碼的最前面。這個提升行為,就是發生在預解析階段。
執行程式碼: 從上到下執行JS語句。
變數提升(變數預解析): 變數的宣告會被提升到當前作用域的最上面,變數的賦值不會提升。
函式提升(函式預解析): 函式的宣告會被提升到當前作用域的最上面,但是不會呼叫函式。