1. 程式人生 > 實用技巧 >JavaScript 複習09

JavaScript 複習09

JavaScript高階第03天筆記

1.函式的定義和呼叫

1.1函式的定義方式

  1. 方式1 函式宣告方式 function 關鍵字 (命名函式)

    function fn(){}
    
  2. 方式2 函式表示式(匿名函式)

    var fn = function(){}
    
  3. 方式3 new Function()

    var f = new Function('a', 'b', 'console.log(a + b)');
    f(1, 2);
    
    var fn = new Function('引數1','引數2'..., '函式體')
    注意
    /*Function 裡面引數都必須是字串格式
    第三種方式執行效率低,也不方便書寫,因此較少使用
    所有函式都是 Function 的例項(物件)  
    函式也屬於物件
    */
    

1.2函式的呼叫

/* 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('人生的巔峰');
})();

2.this

2.1函式內部的this指向

這些 this 的指向,是當我們呼叫函式的時候確定的。呼叫方式的不同決定了this 的指向不同

一般指向我們的呼叫者.

2.2改變函式內部 this 指向

2.2.1 call方法

call()方法呼叫一個物件。簡單理解為呼叫函式的方式,但是它可以改變函式的 this 指向

應用場景: 經常做繼承.

var o = {
	name: 'andy'
}
 function fn(a, b) {
      console.log(this);
      console.log(a+b)
};
fn(1,2)// 此時的this指向的是window 執行結果為3
fn.call(o,1,2)//此時的this指向的是物件o,引數使用逗號隔開,執行結果為3

以上程式碼執行結果為:

2.2.2 apply方法

apply() 方法呼叫一個函式。簡單理解為呼叫函式的方式,但是它可以改變函式的 this 指向。

應用場景: 經常跟陣列有關係

var o = {
	name: 'andy'
}
 function fn(a, b) {
      console.log(this);
      console.log(a+b)
};
fn()// 此時的this指向的是window 執行結果為3
fn.apply(o,[1,2])//此時的this指向的是物件o,引數使用陣列傳遞 執行結果為3

2.2.3 bind方法

bind() 方法不會呼叫函式,但是能改變函式內部this 指向,返回的是原函式改變this之後產生的新函式

如果只是想改變 this 指向,並且不想呼叫這個函式的時候,可以使用bind

應用場景:不呼叫函式,但是還想改變this指向

 var o = {
 name: 'andy'
 };

function fn(a, b) {
	console.log(this);
	console.log(a + b);
};
var f = fn.bind(o, 1, 2); //此處的f是bind返回的新函式
f();//呼叫新函式  this指向的是物件o 引數使用逗號隔開

2.2.4 call、apply、bind三者的異同

  • 共同點 : 都可以改變this指向

  • 不同點:

    • call 和 apply 會呼叫函式, 並且改變函式內部this指向.
    • call 和 apply傳遞的引數不一樣,call傳遞引數使用逗號隔開,apply使用陣列傳遞
    • bind 不會呼叫函式, 可以改變函式內部this指向.
  • 應用場景

    1. call 經常做繼承.
    2. apply經常跟陣列有關係. 比如藉助於數學物件實現陣列最大值最小值
    3. bind 不呼叫函式,但是還想改變this指向. 比如改變定時器內部的this指向.

3.嚴格模式

3.1什麼是嚴格模式

JavaScript 除了提供正常模式外,還提供了嚴格模式(strict mode)。ES5 的嚴格模式是採用具有限制性 JavaScript變體的一種方式,即在嚴格的條件下執行 JS 程式碼。

嚴格模式在 IE10 以上版本的瀏覽器中才會被支援,舊版本瀏覽器中會被忽略。

嚴格模式對正常的 JavaScript 語義做了一些更改:

1.消除了 Javascript 語法的一些不合理、不嚴謹之處,減少了一些怪異行為。

2.消除程式碼執行的一些不安全之處,保證程式碼執行的安全。

3.提高編譯器效率,增加執行速度。

4.禁用了在 ECMAScript 的未來版本中可能會定義的一些語法,為未來新版本的 Javascript 做好鋪墊。比如一些保留字如:class,enum,export, extends, import, super 不能做變數名

3.2開啟嚴格模式

嚴格模式可以應用到整個指令碼或個別函式中。因此在使用時,我們可以將嚴格模式分為為指令碼開啟嚴格模式和為函式開啟嚴格模式兩種情況。

  • 情況一 :為指令碼開啟嚴格模式

    • 有的 script 指令碼是嚴格模式,有的 script 指令碼是正常模式,這樣不利於檔案合併,所以可以將整個指令碼檔案放在一個立即執行的匿名函式之中。這樣獨立建立一個作用域而不影響其他
      script 指令碼檔案。

      (function (){
        //在當前的這個自呼叫函式中有開啟嚴格模式,當前函式之外還是普通模式
          "use strict";
             var num = 10;
          function fn() {}
      })();
      //或者 
      <script>
         "use strict"; //當前script標籤開啟了嚴格模式
      </script>
      <script>
        			//當前script標籤未開啟嚴格模式
      </script>
      
  • 情況二: 為函式開啟嚴格模式

    • 要給某個函式開啟嚴格模式,需要把“use strict”; (或 'use strict'; ) 宣告放在函式體所有語句之前。

      function fn(){
        "use strict";
        return "123";
      } 
      //當前fn函式開啟了嚴格模式
      
      

3.3嚴格模式中的變化

嚴格模式對 Javascript 的語法和行為,都做了一些改變。

'use strict'
num = 10 
console.log(num)//嚴格模式後使用未宣告的變數
--------------------------------------------------------------------------------
var num2 = 1;
delete num2;//嚴格模式不允許刪除變數
--------------------------------------------------------------------------------
function fn() {
 console.log(this); // 嚴格模式下全域性作用域中函式中的 this 是 undefined
}
fn();  
---------------------------------------------------------------------------------
function Star() {
	 this.sex = '男';
}
// Star();嚴格模式下,如果 建構函式不加new呼叫, this 指向的是undefined 如果給他賦值則 會報錯.
var ldh = new Star();
console.log(ldh.sex);
----------------------------------------------------------------------------------
setTimeout(function() {
  console.log(this); //嚴格模式下,定時器 this 還是指向 window
}, 2000);  

更多嚴格模式要求參考

4.高階函式

高階函式是對其他函式進行操作的函式,它接收函式作為引數或將函式作為返回值輸出。

此時fn 就是一個高階函式

函式也是一種資料型別,同樣可以作為引數,傳遞給另外一個引數使用。最典型的就是作為回撥函式。

同理函式也可以作為返回值傳遞回來

5.閉包

5.1變數的作用域複習

變數根據作用域的不同分為兩種:全域性變數和區域性變數。

  1. 函式內部可以使用全域性變數。
  2. 函式外部不可以使用區域性變數。
  3. 當函式執行完畢,本作用域內的區域性變數會銷燬。

5.2什麼是閉包

閉包(closure)指有權訪問另一個函式作用域中變數的函式。簡單理解就是 ,一個作用域可以訪問另外一個函式內部的區域性變數。

5.3閉包的作用

作用:延伸變數的作用範圍。

 function fn() {
   var num = 10;
   function fun() {
       console.log(num);
 	}
    return fun;
 }
var f = fn();
f();

5.4閉包的案例

  1. 利用閉包的方式得到當前li 的索引號
for (var i = 0; i < lis.length; i++) {
// 利用for迴圈建立了4個立即執行函式
// 立即執行函式也成為小閉包因為立即執行函式裡面的任何一個函式都可以使用它的i這變數
(function(i) {
    lis[i].onclick = function() {
      console.log(i);
    }
 })(i);
}

  1. 閉包應用-3秒鐘之後,列印所有li元素的內容
 for (var i = 0; i < lis.length; i++) {
   (function(i) {
     setTimeout(function() {
     console.log(lis[i].innerHTML);
     }, 3000)
   })(i);
}

  1. 閉包應用-計算打車價格
/*需求分析
打車起步價13(3公里內),  之後每多一公里增加 5塊錢.  使用者輸入公里數就可以計算打車價格
如果有擁堵情況,總價格多收取10塊錢擁堵費*/

 var car = (function() {
     var start = 13; // 起步價  區域性變數
     var total = 0; // 總價  區域性變數
     return {
       // 正常的總價
       price: function(n) {
         if (n <= 3) {
           total = start;
         } else {
           total = start + (n - 3) * 5
         }
         return total;
       },
       // 擁堵之後的費用
       yd: function(flag) {
         return flag ? total + 10 : total;
       }
	}
 })();
console.log(car.price(5)); // 23
console.log(car.yd(true)); // 33

5.5案例

 var name = "The Window";
   var object = {
     name: "My Object",
     getNameFunc: function() {
     return function() {
     return this.name;
     };
   }
 };
console.log(object.getNameFunc()())
-----------------------------------------------------------------------------------
var name = "The Window";  
  var object = {    
    name: "My Object",
    getNameFunc: function() {
    var that = this;
    return function() {
    return that.name;
    };
  }
};
console.log(object.getNameFunc()())
       

6.遞迴

6.1什麼是遞迴

遞迴:如果一個函式在內部可以呼叫其本身,那麼這個函式就是遞迴函式。簡單理解:函式內部自己呼叫自己, 這個函式就是遞迴函式

注意:遞迴函式的作用和迴圈效果一樣,由於遞迴很容易發生“棧溢位”錯誤(stack overflow),所以必須要加退出條件return。

6.2利用遞迴求1~n的階乘

//利用遞迴函式求1~n的階乘 1 * 2 * 3 * 4 * ..n
 function fn(n) {
     if (n == 1) { //結束條件
       return 1;
     }
     return n * fn(n - 1);
 }
 console.log(fn(3));

6.3利用遞迴求斐波那契數列

// 利用遞迴函式求斐波那契數列(兔子序列)  1、1、2、3、5、8、13、21...
// 使用者輸入一個數字 n 就可以求出 這個數字對應的兔子序列值
// 我們只需要知道使用者輸入的n 的前面兩項(n-1 n-2)就可以計算出n 對應的序列值
function fb(n) {
  if (n === 1 || n === 2) {
        return 1;
  }
  return fb(n - 1) + fb(n - 2);
}
console.log(fb(3));

6.4利用遞迴遍歷資料

// 我們想要做輸入id號,就可以返回的資料物件
 var data = [{
   id: 1,
   name: '家電',
   goods: [{
     id: 11,
     gname: '冰箱',
     goods: [{
       id: 111,
       gname: '海爾'
     }, {
       id: 112,
       gname: '美的'
     },

            ]

   }, {
     id: 12,
     gname: '洗衣機'
   }]
 }, {
   id: 2,
   name: '服飾'
}];
//1.利用 forEach 去遍歷裡面的每一個物件
 function getID(json, id) {
   var o = {};
   json.forEach(function(item) {
     // console.log(item); // 2個數組元素
     if (item.id == id) {
       // console.log(item);
       o = item;
       return o;
       // 2. 我們想要得裡層的資料 11 12 可以利用遞迴函式
       // 裡面應該有goods這個陣列並且陣列的長度不為 0 
     } else if (item.goods && item.goods.length > 0) {
       o = getID(item.goods, id);
     }
   });
   return o;
}