this指向,閉包,嚴格模式,函數定義和調用,遞歸

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() {}
      })();
      //或者
  • 情況二: 爲函數開啓嚴格模式
    • 要給某個函數開啓嚴格模式,需要把“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);
    }

  2. 閉包應用-3秒鐘之後,打印所有li元素的內容

    for (var i = 0; i < lis.length; i++) {
    (function(i) {
    setTimeout(function() {
    console.log(lis[i].innerHTML);
    }, 3000)
    })(i);
    }

  3. 閉包應用-計算打車價格

    /需求分析
    打車起步價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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章