JavaScript函數
1.函數格式
-
function 函數名稱(形參列表){
? return x;
}
形參需要和C語言區別,js里邊只要==寫個形參名字就行==
2.函數的注意點
一個函數可以有形參也可以沒有形參(零個或多個)
一個函數可以有返回值也可以沒有返回值,沒有返回值默認是undefined
-
JavaScript中的函數和數組一樣, 都是引用數據類型(對象類型)
-
既然是一種數據類型, 所以也可以保存到一個變量中,將來可以==通過變量名稱找到函數并執行函數==
let say = function () { console.log("hello world"); } say();
-
-
調用函數時實參的個數和形參的個數可以不相同 ==我只能說一giao我里giao==
function getSum(a, b) { console.log(a, b); return a + b; } let res = getSum(10); //b就是undefined let res = getSum(10, 20, 30); //多余的收不到
3.函數arguments
因為console.log();也是通過()來調用的, 所以log也是一個函數,為什么log函數可以接收1個或多個參數,內部的實現原理就用到了arguments
-
arguments的作用:保存所有傳遞給函數的實參,我們寫一個求和函數
function getSum() { // 注意點: 每個函數中都有一個叫做arguments的東西 // arguments其實是一個偽數組,可以通過下標索引取值 // console.log(arguments); let sum = 0; for (let i = 0; i < arguments.length; i++){ let num = arguments[i]; // console.log(num); // 10 20 30 sum += num; } return sum; } //這樣我們就實現了不管有多少參數都能求和 let res = getSum(10, 20, 30, 40); console.log(res);
4.函數擴展運算符
-
擴展運算符在函數的形參列表中的作用
-
將傳遞給函數的所有實參打包到一個數組中,注意和在等號左邊一樣, ==也只能寫在形參列表的最后==
function getSum(...values) { //也可以實現不管多少個實參都能相加 // console.log(values); let sum = 0; for (let i = 0; i < values.length; i++){ let num = values[i]; sum += num; } return sum; } let res = getSum(10, 20, 30, 40); console.log(res); //只能寫在最后面哦 function getSum(a, ...values)
-
5.函數形參默認值
在ES6之前可以通過邏輯運算符來給形參指定默認值
-
格式: 條件A || 條件B,如果條件A成立, 那么就返回條件A,如果條件A不成立, 無論條件B是否成立, 都會返回條件B
function getSum(a,b){ a=a||"tanwenchao"; b=b||"lalala"; console.log(a,b); } getSum(); //就算沒傳也有值
從ES6開始, 可以直接在形參后面通過=指定默認值
-
注意點: ES6開始的默認值還可以從其它的函數中獲取
//function getSum(a = "tan", b = "xu") { //下面試通過函數獲取 function getSum(a = "指趣學院", b = getDefault()) { console.log(a, b); } getSum(); // getSum(123, "abc"); function getDefault() { return "tan" }
6.函數作為參數和返回值
注意點:在進行參數傳遞操作時,要想一想帶括號和不帶括號,比如下面
let say = function () { console.log("hello world"); } let fn = say; fn(); //我也可以這樣寫 let fn = say(); fn;
-
作為返回值
在其它編程語言中函數是不可以嵌套定義的
function test() { let say = function () { console.log("hello world"); } return say; } let fn = test(); // 相當于let fn = say; fn();
-
作為參數
let say = function () { console.log("hello world"); } function test(fn) { // let fn = say; fn(); } test(say);
7.匿名函數
匿名函數就是沒有名稱的函數
-
作為其他函數的返回值
function test() { return function () { console.log("hello lnj"); }; } let fn = test(); // let fn = say; fn();
-
作為一個立即執行的函數
注意點: 如果想讓匿名函數立即執行, 那么必須使用()將函數的定義包裹起來才可以
(function () { console.log("hello it666"); })();
-
作為其他函數的參數
function test(fn) { // let fn = say; fn(); } test(function () { console.log("hello world"); });
8.箭頭函數
箭頭函數是ES6中新增的一種定義函數的格式,目的就是為了簡化定義函數的代碼,相比之前有什么區別呢.如下
let 函數名稱 = (形參列表) =>{}
let say = (name) => { console.log("hello"+name); } say(); //上面看起來并不簡化 //在箭頭函數中如果只有一個形參, 那么()可以省略 let say = name => { console.log("hello"+name); } //在箭頭函數中如果{}中只有一句代碼, 那么{}也可以省略 let say = name => console.log("hello"+name); say("譚文超");
9.遞歸函數
和以前學的一樣
10.函數中變量作用域
我們知道,js中有兩種定義變量的方法,var 和 let ,區別之前已經學過,他們在變量作用域中也有區別
- 首先了解一下塊級作用域和局部作用域
- 在ES6中只要{}沒有和函數結合在一起, 那么應該"塊級作用域",例如寫在while循環,for循環等
- 函數后面{}中的的作用域, 我們稱之為"局部作用域
- 在了解一下var和let的作用域的區別
- 在塊級作用域中通過var定義的變量是全局變量,在局部作用域中通過var定義的變量是局部變量==感覺很沙雕的樣子==
- 通過let定義的變量,只要是在{}里就是局部變量,==var和C語言的變量差不多==
- 無論是在塊級作用域還是在局部作用域, 省略變量前面的let或者var就會變成一個全局變量 如 {num = 1;}
11.作用域鏈基本了解
正是因為有了函數的嵌套,所以才有了作用域鏈的概念,同時由與ES6之前和之后的語法差異,研究"作用域鏈"的時候最好將ES6之前和ES6分開研究.
-
ES6之前沒有塊級作用域, 只有全局作用域和局部作用域
- 全局作用域我們又稱之為0級作用域
- 定義函數開啟的作用域就是1級/2級/3級/...作用域,JavaScript會將這些作用域鏈接在一起形成一個鏈條, 這個鏈條就是作用域鏈,0 ---> 1 ----> 2 ----> 3 ----> 4
-
變量在作用域鏈查找規則
1先在當前找, 找到就使用當前作用域找到的,當前作用域中沒有找到, 就去上一級作用域中查找,以此類推直到0級為止, 如果0級作用域還沒找到, 就報錯
var num = 123; function demo() { // 1級作用域 var num = 456; function test() { // 2級作用域 // var num = 789; console.log(num); } test(); } demo(); //當前代碼會輸出456
ES6就是多加了一個塊級作用域,但是通過let 定義變量不管是新開了一個函數還是一個代碼塊,都會開啟作用域的.
12.函數預解析
什么是預解析:瀏覽器在執行JS代碼的時候會分成兩部分操作:預解析以及逐行執行代碼,也就是說瀏覽器不會直接執行代碼, 而是加工處理之后再執行, 這個加工處理的過程, 我們就稱之為預解析.
預解析規則:將變量聲明和函數聲明提升到當前作用域最前面,將剩余代碼按照書寫順序依次放到后面
-
三種預解析
//下為第一種,會預解析,可以先調用在創建, say(); function say(){ console.log("譚文超"); }
//下為第二種 say(); // 如果將函數賦值給一個var定義的變量, 那么函數不會被預解析, 只有變量會被預解析 var say = function() { console.log("hello itzb"); } //預解析完是這樣的 /* var say; //undefined say();// say is not a function say = function() { console.log("hello itzb"); } */
//下為第三種 // ES6定義函數的格式不會預解析 say(); // say is not defined let say = () => { console.log("hello itzb"); }
我們來看幾個練習
var num = 123; fun(); function fun() { console.log(num); var num = 666; } //輸出什么東西 /* var num; function fun() { var num; console.log(num); // undefined num = 666; } num = 123; fun(); */
var a = 666; test(); function test() { var b = 777; console.log(a); console.log(b); console.log(c); var a = 888; let c = 999; } /* var a; function test() { var b; var a; b = 777; console.log(a); // undefined console.log(b); // 777 console.log(c); // 報錯 a = 888; let c = 999; } a = 666; test(); */
-
下面這個了解一下(面試或筆試)
// 在ES6之前沒有塊級作用域, 并且沒有將這兩個函數定義到其它的函數中, // 所以這兩個函數應該屬于全局作用域 // 注意點: // 1.在高級瀏覽器中, 不會對{}中定義的函數進行提升 // 2.只有在低級瀏覽器中, 才會按照正常的方式解析 if(true){ function demo() { console.log("1"); } }else{ function demo() { console.log("2"); } } demo();//高級瀏覽器輸出1 /* 低級瀏覽器如下解析,輸出2 function demo() { console.log("1"); } function demo() { console.log("2"); } if(true){}else{} demo(); */
// 注意點: 如果變量名稱和函數名稱同名, 那么函數的優先級高于變量 // 一定要記住, 在企業開發中千萬不要讓變量名稱和函數名稱重名 console.log(value); // 會輸出函數的定義 var value = 123; function value() { console.log("fn value"); } console.log(value); // 會輸出123 /* function value() { console.log("fn value"); } console.log(value); var value; value = 123; console.log(value); */