1.函數進階
- 函數的定義和使用
- 函數聲明方式function關鍵字(命名函數)
- 函數表達式(匿名函數)
- new Function()
- Function里面的參數必須都是字符串格式
- 第三種方式執行效率低,也不方便書寫,因此較少使用
- 所有函數都是Function的實例(對象)
- 函數也屬於對象
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //函數的定義方式 10 //1.自定義函數 11 function fn(){}; 12 //2.函數表達式 13 var fun = function(){}; 14 //3.利用new Function('參數1','參數2','函數體'); 15 var f = new Function('a','b','console.log(a+b)'); 16 f(1,2); 17 //4.所有的函數都是Function的實例對象 18 </script> 19 </body> 20 </html>
- 函數的調用方式
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //函數的調用方式 10 11 //1.普通函數 12 function fn(){ 13 console.log("普通函數"); 14 } 15 // fn(); 16 // fn.call(); 17 //2.對象的方法 18 var obj = { 19 sayHi: function(){ 20 console.log("Hi"); 21 } 22 } 23 obj.sayHi(); 24 //3.構造函數 25 function Star(){}; 26 new Star(); 27 //4.綁定時間的函數 28 // btn.onclick = function(){}; 29 //點擊按鈕就可以調用這個函數 30 //5.定時器函數 31 setInterval(function(){},1000); 32 //這個函數是定時器自動每秒鍾調用一次 33 //6.立即執行函數 34 (function(){console.log("怎么也飛不出,花花的世界~~~")})() 35 //立即執行函數是自動調用 36 </script> 37 </body> 38 </html>
- this
| 調用方式 | this指向 |
| 普通函數調用 | window |
| 構造函數調用 | 實例對象,原型對象里面的方法也指向實例對象 |
| 對象方法調用 | 該方法所屬對象 |
| 事件綁定調用 | 綁定事件對象 |
| 定時器函數 | window |
| 立即執行函數 | window |
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //函數的調用方式 10 //函數的不同決定了this的指向不同 11 //1.普通函數 this指向window 12 function fn(){ 13 console.log("普通函數"+this); 14 } 15 // fn(); 16 // fn.call(); 17 //2.對象的方法 this指向對象 18 var obj = { 19 sayHi: function(){ 20 console.log("Hi"+this); 21 } 22 } 23 // obj.sayHi(); 24 //3.構造函數 this指向的是實例對象,原型對象里面的this指向的也是實例對象 25 function Star(){}; 26 new Star(); 27 //4.綁定時間的函數 this指向函數的調用者btn 28 // btn.onclick = function(){}; 29 //點擊按鈕就可以調用這個函數 30 //5.定時器函數 this指向window 31 setInterval(function(){},1000); 32 //這個函數是定時器自動每秒鍾調用一次 33 //6.立即執行函數this指向window 34 (function(){console.log("怎么也飛不出,花花的世界~~~")})() 35 //立即執行函數是自動調用 36 </script> 37 </body> 38 </html>
- 改變函數內部this指向
JavaScript為我們提供了一些函數方法幫助我們改變函數內部this指向問題,常用的有bind()、call()、apply()三種方法
-
- call方法
call方法調用一個對象,簡單理解為調用函數的方式,但是它可以改變函數的this指向
-
- apply()方法
- fun.apply(thisArg,[argsArray])
- thisArg:在fun函數運行時指定的this值
- argsArray:傳遞的值,必須包含在數組里面
- 返回值值就是函數的返回值,因為它必須是調用函數
bind()方法:不會調用函數
- fun.bind(thisArg,arg1,arg2,…)
- thisArg:在fun函數運行時指定this的值
- arg1、arg2:傳遞其他參數
- 返回由指定的this值和初始化參數改造的原函數拷貝
- apply()方法
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script type="text/javascript"> //改變函數內this指向,js提供了三種方式call()、apply()、bind() //1.call() var o = { name: 'bbh' }; function fn(arr,num){ console.log(this); console.log(arr+num) } // fn.call(o); //call第一個可以調用函數,第二個可以改變函數內部的this指向 //call的主要作用可以實現繼承 //2.apply()應用運用的意思 // fn.apply(o,['bbh','56']); //(1)也是調用函數,第二個可以改變函數內部的this指向 //(2)但是他的參數必須是數組、偽數組 //(3)apply的主要應用比如我們可以利用apply借助於數學內置對象找最大值 //3.bind()綁定捆綁的意思 var res = fn.bind(o); //(1)不會調用原來的函數 可以改變原來函數內部this指向 //(2)返回的是原函數改變this之后產生的新函數 console.log(res); </script> </body> </html>
- bind方法應用
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <button>點擊</button> 9 <script type="text/javascript"> 10 //1.如果有的函數我們不需要立即調用,但是又想改變函數內部的this指向 11 //2.我們有一個按鈕當我們點擊了之后就禁用這個按鈕,3秒之后啟動 12 var btn = document.querySelector("button"); 13 btn.onclick = function(){ 14 this.disabled = true; 15 setTimeout(function(){ 16 this.disabled = false; 17 //這個this指向的btn 18 }.bind(this),3000) 19 } 20 </script> 21 </body> 22 </html>
- call、apply、bind總結
- 相同點
- 都可以改變函數內部的this指向
- 區別點
- call和apply會調用函數,並且改變函數內部this指向
- call和apply傳遞的參數不一樣,call傳遞參數arg1,arg2,…形式,apply必須數組形式[arg]
- bind不會調用函數,可以改變函數內部this指向
- 主要應用場景
- call經常做繼承
- apply經常跟數組有關系,比如借助數學對象實現數組最大值和最小值
- bind不調用函數,但是還想改變this指向,比如改變定時器里的this指向
2.嚴格模式
JavaScript除了提供正常模式外,還提供了嚴格模式(strict mode)。即在嚴格的條件下運行JS代碼
- 嚴格模式
- 消除了JavaScript語法的一些不合理,不嚴謹之處,減少了一些怪異行為
- 消除代碼運行的一些不安全之處,保證代碼運行的安全
- 提高編譯器效率,增加運行速度
- 禁用了在ECMAScript的未來版本中可能會使用的一些語法,例如一些保留字:class
- 開啟嚴格模式:嚴格模式可以應用到整個腳本或個別函數中。我們價格嚴格模式分為腳本開啟嚴格模式和函數開啟嚴格模式
- 腳本開啟嚴格模式
- 為整個腳本文件開啟嚴格模式,需要在所有語句之前放一個特定語句"use strict"
- 函數開啟嚴格模式
- 需要把"use strict"聲明放在函數題所有語句之前
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <!-- 為整個腳本(script標簽)開啟嚴格模式 --> 9 <script type="text/javascript"> 10 'use strict'; 11 //下面的js代碼會按照嚴格模式執行代碼 12 </script> 13 <script type="text/javascript"> 14 (function(){ 15 'use strict'; 16 })() 17 </script> 18 <!-- 為某個函數開啟嚴格模式 --> 19 <script type="text/javascript"> 20 function fn(){ 21 //此時只是給函數fn開啟嚴格模式 22 'use strict'; 23 } 24 function fun(){ 25 //里面還是按照普通模式執行 26 } 27 </script> 28 </body> 29 </html>
- 嚴格模式中的變化
- 變量規定
- 嚴格模式中,變量都必須先用var聲明,然后再使用
- 嚴禁刪除已經聲明的變量
- 變量規定
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <!-- 為整個腳本(script標簽)開啟嚴格模式 --> 9 <script type="text/javascript"> 10 'use strict'; 11 //下面的js代碼會按照嚴格模式執行代碼 12 //1.變量名必須先聲明再使用 13 // num = 1; 14 // console.log(num);// num is not defined 15 //2.我們不能隨意刪除已經聲明好的變量 16 // var num = 10; 17 // delete num;//Delete of an unqualified identifier in strict mode 18 19 </script> 20 21 </body> 22 </html
-
- 嚴格模式下this指向問題
- 嚴格模式下全局作用域中的函數this指向的是undefined
- 嚴格模式下,如果構造函數不加new調用,this就會報錯
- 函數變化
- 函數不能有重名的參數
- 函數必須聲明在頂層,不允許在非函數代碼塊內聲明函數
- 嚴格模式下this指向問題
3.高階函數
高階函數是對其他函數進行操作的函數,它接收函數作為參數或將函數作為返回值輸出
- 函數作為參數傳遞
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //高階函數-函數可以作為參數傳遞 10 function fn(a,b,callback){ 11 console.log(a+b); 12 callback&&callback(); 13 } 14 fn(1,2,function(){ 15 console.log("success"); 16 }); 17 </script> 18 </body> 19 </html>
- 閉包
閉包指有權訪問另一個函數作用域中的變量的函數
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //閉包(closure)指有權訪問另一個函數作用域中變量的函數 10 //閉包:我們fun這個函數作用域 訪問了另外一個函數fn里面的局部變量 11 //我們fn 外面的作用域可以訪問fn,內部的額局部變量 12 //閉包的主要作用,延伸了變量的作用范圍 13 function fn(){ 14 var num = 10; 15 function fun(){ 16 console.log(num); 17 } 18 return fun; 19 } 20 var f = fn(); 21 /* 22 類似於 23 f = function fun(){ 24 console.log(num); 25 } 26 */ 27 f(); 28 </script> 29 </body> 30 </html>
- 循環注冊點擊事件
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <ul class="nav"> 9 <li>榴蓮</li> 10 <li>臭豆腐</li> 11 <li>鯡魚罐頭</li> 12 <li>大豬蹄子</li> 13 </ul> 14 <script type="text/javascript"> 15 //閉包應用-點擊li輸出當前li的索引號 16 var lis = document.querySelectorAll(".nav li"); 17 //1.利用動態添加屬性的方式 18 // for(var i = 0 ; i < lis.length ; i++){ 19 // lis[i].index = i; 20 // lis[i].onclick = function(){ 21 // console.log(this.index); 22 // } 23 // } 24 //2.利用閉包的方式得到當前小li的索引號 25 for(var i = 0 ; i < lis.length ; i++){ 26 //利用for循環創建了4個執行函數 27 (function(i){ 28 lis[i].onclick = function(){ 29 console.log(i); 30 } 31 })(i); 32 } 33 </script> 34 </body> 35 </html>
- 定時器中的閉包
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <ul class="nav"> 9 <li>榴蓮</li> 10 <li>臭豆腐</li> 11 <li>鯡魚罐頭</li> 12 <li>大豬蹄子</li> 13 </ul> 14 <script type="text/javascript"> 15 //閉包應用-3秒鍾之后,打印所有li元素的內容 16 var lis = document.querySelectorAll(".nav li"); 17 for(var i = 0 ; i < lis.length ; i++){ 18 (function(i){ 19 setTimeout(function(){ 20 console.log(lis[i].innerText); 21 },3000); 22 })(i) 23 } 24 </script> 25 </body> 26 </html>
- 打車價格
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //閉包應用-計算打車價格 10 //打車起步價13(3公里內),之后每多一公里增加5塊錢,用戶輸入公里數就可以計算打車價格 11 //如果有擁堵情況,總價格多收取10塊錢擁堵費 12 var res = (function(){ 13 var start = 13;//起步價 14 var total = 0;//總價 15 return { 16 price: function(n){//正常價格 17 if(n <= 3) 18 total = 13; 19 else{ 20 total = (n-3)*5 + 13; 21 } 22 return total; 23 }, 24 yongdu: function(flag){//擁堵價格 25 return flag ? total+10 : total; 26 } 27 } 28 })(); 29 console.log(res.price(5)); 30 console.log(res.yongdu(true)); 31 </script> 32 </body> 33 </html>
- 閉包總結
- 閉包是一個函數(一個作用域可以訪問另一個函數的作用域)
- 閉包的作用:延伸變量的作用范圍
4.遞歸
如果一個函數可以在內部調用其本身,這個函數就是遞歸函數
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //遞歸函數:函數內部自己調用自己,這個函數就是遞歸函數 10 //由於遞歸很容易發生“棧溢出”錯誤,所以必須要加退出條件(return) 11 var num = 0; 12 function fn(){ 13 console.log("bbh"); 14 if(num == 5){ 15 return;//遞歸函數里面必須添加退出條件 16 } 17 num++; 18 fn(); 19 } 20 fn(); 21 </script> 22 </body> 23 </html>
- 利用遞歸函數求解數學題
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //遞歸函數:函數內部自己調用自己,這個函數就是遞歸函數 10 //由於遞歸很容易發生“棧溢出”錯誤,所以必須要加退出條件(return) 11 function fn(n){ 12 if(n == 1){ 13 return 1; 14 } 15 return n*fn(n-1); 16 } 17 var res = fn(3); 18 console.log(res); 19 </script> 20 </body> 21 </html>
- 利用遞歸求斐波那契序列
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <script type="text/javascript"> //利用遞歸函數求斐波那契序列 //用戶輸入一個數字n,就可以求出這個數字對應的兔子序列值 function fn(n){ if(n == 1 || n == 2){ return 1; }else{ return fn(n-1)+fn(n-2); } } console.log(fn(8)) </script> </body> </html>
- 根據id返回數據對象
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 //根據id返回數據對象 10 var data = [{ 11 id: 1, 12 name: '家電', 13 goods: [{ 14 id: 11, 15 gname: '冰箱' 16 }, { 17 id: 22, 18 gname: '洗衣機', 19 goods: [{ 20 id: 2201, 21 gname: '滾筒洗衣機' 22 }, 23 { 24 id: 2202, 25 gname: '壁掛洗衣機' 26 } 27 ] 28 }] 29 }, 30 { 31 id: 2, 32 name: '服飾' 33 } 34 ] 35 //我們想要做輸入id號,就可以返回數據對象 36 //1.利用foreach遍歷里面的每一個對象 37 function getId(json, id) { 38 var res = {}; 39 json.some(function(value) { 40 if (value.id == id) { 41 res = value; 42 return value.id == id; 43 } else if(value.goods && value.goods.length > 0){ 44 res = getId(value.goods, id); 45 } 46 }); 47 return res; 48 } 49 console.log(getId(data, 2202)); 50 </script> 51 </body> 52 </html>
- 深拷貝和淺拷貝
- 淺拷貝知識拷貝一層,更深層次對象級別的只拷貝引用
- 深拷貝拷貝多層,每一級別的額數據都會拷貝
- 淺拷貝
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 var obj = { 10 id: 1, 11 name: 'bbh', 12 msg: { 13 age:18 14 } 15 }; 16 var o = {}; 17 // for(var k in obj){ 18 // o[k] = obj[k]; 19 // } 20 // o.msg.age = 30; 21 // console.log(o); 22 // console.log(obj); 23 console.log("---------------------"); 24 //ES6提供的淺拷貝語法糖 25 //注意:淺拷貝只拷貝一層,當拷貝的數據里還存在復雜類型數據時, 26 //只會拷貝其地址,對數據進行修改時會影響到原來的數據 27 Object.assign(o,obj); 28 o.msg.age = 30; 29 console.log(obj); 30 console.log(o); 31 </script> 32 </body> 33 </html>
-
- 深拷貝
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title></title> 6 </head> 7 <body> 8 <script type="text/javascript"> 9 var obj = { 10 id: 1, 11 name: 'bbh', 12 msg: { 13 age:18 14 }, 15 sing: function(){ 16 17 } 18 }; 19 var o = {}; 20 function fn(obj,o){ 21 for(k in obj){ 22 //判斷屬性值屬於哪種數據類型 23 //1.獲取屬性值 obj[k] 24 //2.判斷這個值是否為數組 25 //3.判斷是否為對象 26 if(obj[k] instanceof Array){ 27 o[k] = []; 28 fn(obj[k],o[k]); 29 }else if(obj[k] instanceof Object){ 30 o[k] = {}; 31 fn(obj[k],o[k]); 32 }else{ 33 o[k] = obj[k]; 34 } 35 } 36 } 37 //深拷貝封裝函數 38 fn(obj,o); 39 o.msg.age = 30; 40 console.log(o); 41 console.log(obj); 42 </script> 43 </body> 44 </html>
