javascript 函數 -聲明+實際應用

本篇介紹javascript函數的相關概念javascript

函數聲明

函數定義

函數表達式

var fn = function(){
    console.log(1)
  }
複製代碼

function + 函數名字

function fnf(){
  console.log(1)
}
複製代碼

new Function()

傳遞參數 參數名稱,參數字符串java

var fnc = new Function('name','age','console.log(name,age)');
fnc('33',33)
複製代碼

函數的變量提高

  • 函數表達式進行定義的函數,會提高var + 函數名字,而=後的做爲參數進行賦值操做
  • 使用function 定義的函數,在變量提高的時候會總體進行提高;
//函數表達式
  console.log('--函數表達式開始-',fn)
  var fn = function(){
    console.log(1)
  }
  console.log('--函數表達式結束-',fn)

  /** 函數fun */
console.log('--function開始-',fnf)
  function fnf(){
    console.log(1)
  }
console.log('--function結束-',fnf)
複製代碼
圖1 函數的變量提高

函數類型

具名函數

具名函數是有函數名字的函數,面試

var fnNameFn = function(){}
function fnNameFn2(){}
console.log(fnNameFn.prototype)

複製代碼
圖2 具名函數的名字

匿名函數

無函數名稱、如當即執行函數,ajax

(function(){ }())
複製代碼

箭頭函數

箭頭函數涉及的內容比較多,最多見的就是this指向的問題。箭頭函數最多見的使用場景就是回調函數了編程

let arrowFun = ()=>{}
console.log(arrowFun.prototype) // undefined
複製代碼

箭頭函數的定義格式瀏覽器

// 直接返回值
let arrfn = ()=> 2;
console.log(arrfn())
//傳遞參數進行判斷
let arrfn2 = (name)=> name !='mfy';
console.log(arrfn2())
複製代碼

函數參數

函數的參數分爲實參形參markdown

  • 實參 實際傳入的參數
  • 形參 只能使用在函數體內部的變量
function add(num1,num2){} // num1 、num2是形參
var a=1,b=3;
add(a,b) // a,b 爲實參
複製代碼

arguments

  • 普通函數是存在arguments
  • 箭頭函數不存在arguments
let funMfy =function(){
 console.log(arguments)
}
funMfy('mfy','23')

//箭頭函數
let fnnn = ()=>{
 console.log(arguments)
}
fnnn('mfy',33) //報錯
複製代碼

收集參數

let fyy = function(...args){
 console.log(args)
}
fyy('mfy',32,[])
複製代碼

函數應用

函數的使用在js中很是很是多了,其實如下的相關介紹,無非就是在基礎函數中進行變換和使用纔有了下面的名此閉包

閉包

一個函數和對其周圍狀態(lexical environment,詞法環境)的引用捆綁在一塊兒(或者說函數被引用包圍),這樣的組合就是閉包(closure)。也就是說,閉包讓你能夠在一個內層函數中訪問到其外層函數的做用域。在 JavaScript 中,每當建立一個函數,閉包就會在函數建立的同時被建立出來。app

function clouser(){
  let a = '666'
  return function(){
    console.log(a)
  }
 }
 let bfn = clouser();
 bfn();//666
複製代碼

擴展知識點:變量提高 做用域
閉包應用:工廠函數 當即執行函數框架

閉包常見問題

  • 循環問題
  • 變量打印問題

高階函數

高階函數(higher-order-function)指操做函數的函數,通常地,有如下兩種狀況:

  • 一、函數能夠做爲參數被傳遞
  • 二、函數能夠做爲返回值輸出

javascript 中的函數顯然知足高階函數的條件,在實際開發中,不管是將函數看成參數傳遞,仍是讓函數的執行結果返回另一個函數,這兩種情形都有不少應用場景。下面將對這兩種狀況進行詳細介紹;

函數作爲參數傳遞

把函數看成參數傳遞,表明能夠抽離出一部分容易變化的業務邏輯,把這部分業務邏輯放在函數參數中,這樣一來能夠分離業務代碼中變化與不變的部分。其中一個常見的應用場景就是回調函數。

回調函數是最多見的參數傳遞了在ajax異步請求的應用中,回調函數的使用很是頻繁。想在ajax請求返回以後作一些事情,但又並不知道請求返回的確切時間時,最多見的方案就是把callback函數看成參數傳入發起ajax請求的方法中,待請求完成以後執行callback函數

function ajax(callback){
    $.ajax( 'http://xx.com/getUserInfo?' + userId, function( data ){
        //不肯定何時返回,進行回調執行
        if ( typeof callback === 'function' ){
          callback( data );
        }
      });
}
複製代碼

不少框架已經異步處理的時候都使用了函數做爲參數傳入 Vue的this.nextTickReact 的setState

函數做爲返回值

閉包工廠函數

function People(){}
function Factort(){
 var instance = null
 return function(){
 if(!instance)
   instance = new People();
 }
 return instance;
}
複製代碼

函數柯里化

函數的柯里化(currying)又稱部分求值。一個currying的函數首先會接受一些參數,接受了這些參數以後,該函數並不會當即求值,而是繼續返回另一個函數,剛纔傳入的參數在函數造成的閉包中被保存起來。待到函數被真正須要求值的時候,以前傳入的全部參數都會被一次性用於求值.簡稱閉包;

demo 花費計算問題

//通用的柯里化函數
var curring = function(fn){
    var args = [];
    return function(){
        if(arguments.length ==0){
            return fn.apply(this,args);

        }else{
            [].push.apply(args,arguments);
        }
    }
}
var cost1 = (function(){
    var money = 0;
    return  function(){
        for(var i=0;i<arguments.length;i++){
            money+=arguments[i]
        }
        return money;
    }

})()
var cost3 = curring(cost1);
cost3(100);
cost3(200);
cost3(100);
console.log(cost3())
複製代碼

求值柯里化

傳遞多個參數時候

/** * 求值柯里化 */

var curring3= function(fn){
   //獲取fn外的其餘參數
   var args = [].slice.call(arguments,1);
   return function(){
       // 獲取fn的全部參數
       var innerArgs =[].slice.call(arguments);
       // 最終參數列表重合展開
       var finnalArgs = args.concat(innerArgs);
       //將參數列表展開,並傳入fn中
       return fn.apply(null,finnalArgs)
   }
}
var cost6 = (function(){
   var money = 0;
   return function () {
     for (var i = 0, l = arguments.length; i < l; i++) {
       money += arguments[i];
     } 
     return money;
   }
})()
var costC = curring3(cost6,300,233);
console.log(costC(44))

console.log(costC(2003,444))
複製代碼

反柯里化

Array.prototype上的方法本來只能用來操做array對象。但用call和apply能夠把任意對象看成this傳入某個方法,這樣一來,方法中用到this的地方就再也不侷限於原來規定的對象,而是加以泛化並獲得更廣的適用性

有沒有辦法把泛化this的過程提取出來呢?反柯里化(uncurrying)就是用來解決這個問題的。反柯里化主要用於擴大適用範圍,建立一個應用範圍更廣的函數。使原本只有特定對象才適用的方法,擴展到更多的對象。

Function.prototype.unCurring=function(){
    var _this = this; 
    return function(){
        var obj = Array.prototype.shift.call(arguments);
        return _this.apply(obj,arguments); //更改當前的this指向
    }
}

//另外一種方法實現

Function.prototype.currying = function() {
    var _this = this;
    return function() {
        return Function.prototype.call.apply(_this, arguments);
    }
} 
複製代碼

遞歸函數

遞歸函數則是本身調用本身自己,最多見的一個就是對象的深拷貝以及斐波那契

惰性函數

惰性函數表示函數執行的分支只會在函數第一次調用的時候執行,在第一次調用過程當中,該函數會被覆蓋爲另外一個按照合適方式執行的函數,這樣任何對原函數的調用就不用再通過執行的分支了。
javascript的瀏覽器事件兼容

function addEvent(type, element, fun) {
    if (element.addEventListener) {
        element.addEventListener(type, fun, false);
    }
    else if(element.attachEvent){
        element.attachEvent('on' + type, fun);
    }
    else{
        element['on' + type] = fun;
    }
}

//添加事件

//第一次綁定
var ele = document.getElementById("bind-event");
addEvent('bind-event',ele,()=>{
    console.log(111)
});
//第二次綁定
var ele1= document.getElementById("bind-event2");
addEvent('click',ele1,()=>{
    console.log(222)
})
複製代碼

咱們每次綁定的時候都回去判斷當前是否支持,而在一個瀏覽器中,咱們只須要進行判斷一次便可;
惰性函數的本質就是函數重寫,所謂惰性載入,指函數執行的分支只會發生一次,有兩種實現惰性載入的方式;

  • 第一種 函數的重寫 進行變量綁定賦值
function addEvent(type, element, fun){ 
    if (element.addEventListener) {
        addEvent = function(type, element, fun){
            element.addEventListener(type, fun, false);
        }
    }else if(element.attachEvent){
        addEvent = function(type, element, fun){
            element.attachEvent('on' + type, fun);
        }
    }else{
        addEvent =function(type,element,fun){
            element['on' + type] = fun;
        }
    }
    return addEvent(type, element, fun)
}

複製代碼

在這個惰性載入的addEvent()中,if語句的每一個分支都會爲addEvent變量賦值,有效覆蓋了原函數。最後一步即是調用了新賦函數。下一次調用addEvent()時,便會直接調用新賦值的函數,這樣就不用再執行if語句了;

  • 第二種是聲明函數時就指定適當的函數。把嗅探瀏覽器的操做提早到代碼加載的時候,在代碼加載的時候就馬上進行一次判斷,以便讓addEvent返回一個包裹了正確邏輯的函數;
var addEvent = (function () {
    if (document.addEventListener) {
        return function (type, element, fun) {
            element.addEventListener(type, fun, false);
        }
    }
    else if (document.attachEvent) {
        return function (type, element, fun) {
            element.attachEvent('on' + type, fun);
        }
    }
    else {
        return function (type, element, fun) {
            element['on' + type] = fun;
        }
    }
})();
複製代碼

純函數

簡單來講,一個函數的返回結果只依賴於它的參數,而且在執行過程裏面沒有反作用,咱們就把這個函數叫作純函數。這麼說確定比較抽象,咱們把它掰開來看:

  • 返回結果只依賴於它的參數
  • 函數執行過程當中是沒有反作用的
var a =1;
function ac(b){
  return a+b
}
複製代碼

此時不是純函數,由於此時函數的結果依賴的函數外部的變量a;

const a = 1
const foo = (x, b) => x + b
foo(1, 2) // => 3
複製代碼

此時函數依賴函數傳遞過來的參數,當傳遞的參數爲1,2的時候,不管如何都不會改變函數傳出的結果 一個函數的返回結果只依賴於它的參數。

👉🏻 引用類型數據 看成參數傳入的時候,若是內部更改了引用類型的數據,那麼他就不是純函數,由於出現了反作用;

防抖和節流

這個高頻點,另分出來進行解釋

js函數+實際應用面試高頻點

  • 函數參數arguments的使用
  • 箭頭函數使用點
  • 函數內部this指向問題
  • 閉包問題
    • 閉包概念
    • 常見閉包問題
    • 閉包產生的缺點
    • 涉及做用域
  • 函數柯里化(參數收集過程)
  • 寫一個純函數(多是編程中,要求寫純函數)
  • 防抖和節流 (面試被問頻率很是高)
    • 防抖和節流使用場景
    • 手寫防抖和節流

參考文檔

-《javascript 高級程序設計第四版》

  • 很早以前參考的一篇文章
相關文章
相關標籤/搜索