啊,函式吶!!!

sunseekers發表於2018-03-18

一份需要你補充完整的函式導圖!我還是一個初學者,這篇文章是我所知道的所有關於函式的知識,如有不完善或者錯誤,希望能夠在評論下方指出,哈哈哈,大神勿噴。


啊,函式吶!!!

檢視原圖

在 JavaScript 中函式是第一型別的物件(函式是物件),我們可以將函式視為任何型別的 JavaScript 物件;

  1. 函式可以擁有有屬性

Function.prototype 或者 Function.arguments...

  1. 函式可以擁有方法

Function.prototype.apply() , Function.prototype.call()Function.prototype.bind()...

  1. 函式可以賦值給變數,陣列或者其他物件屬性給變數
var large=function (){}
var obj={
  large:function(){}
}
複製程式碼
  1. 函式還能被呼叫
function large(){},large();
複製程式碼
  1. 當然函式還享有普通物件所擁有的特性,因為 Function 繼承 Object
  1. 函式可以作為引數傳遞給函式,(函式名本身是變數,所以函式也可以作為值來使用;即可以把函式作為引數傳遞給另一個函式,也可以把函式作為另一函式的結果返回;)
function add(a,b){
    return a+b
}
function sum(fn,c){
    return fn+c
}
sum(add(2,3),4);//9
複製程式碼
  1. 函式可以作為返回值進行返回
function add(a,b){
    return a+b;
}
add(2,3)//5
複製程式碼

所以說函式是第一型別物件,函式是程式碼執行的主要模組單元化

函式包含一組語句,用來指定物件的某一種行為,是JavaScript的基礎模組單元,用於程式碼複用,資訊隱藏和組合呼叫;

所謂的程式設計,就是將一組需求分解成一組函式與資料結構的技能

函式名是指向函式物件的指標。如果沒有函式名,我們就稱之為匿名函式,匿名函式沒有指向函式物件的指標,一般情況下我們會立即執行函式,或者將匿名函式賦值給變數;

函式建立的兩種方式:函式宣告和函式表示式(匿名函式,拉姆達函式) 啊,函式吶!!!

其中num1num2 是函式的形參,(形參,形式上的引數)當 num1num2作為具體的資料傳遞給函式時,就是實參,(實參,實際的引數) 形參和實參

如果形參個數大於實參個數,剩下沒有對應的形參將賦值為 undefined

如果形參個數小於實參個數,多餘的實參將會自動忽略

函式宣告和函式表示式的區別:

我們可以將表示式視為一個匿名函式,然後將其賦值給變數

  1. 解析器會率先讀取函式宣告,並在執行任何程式碼之前可以訪問;函式表示式必須等到解析器執行到他所造的程式碼才會真正被解析(函式宣告會提前;函式表示式不會);
  2. 函式宣告後面不能跟圓括號;表示式可以(表示式後加圓括號表示函式呼叫);
  3. 函式宣告只能建立區域性函式;函式表示式可建立全域性函式

在函式體內,變數宣告的作用域開始於宣告的地方,結束於所在函式的結尾,與程式碼巢狀無關;(即函式的作用域以及所有的變數都會在函式執行完以後立即被銷燬)

命名函式的作用域是指宣告該函式的整個函式範圍,與程式碼巢狀無關 啊,函式吶!!!

inner 函式能夠訪問到 outer 裡面的變數,此時就形成了閉包,稍後會對閉包進行進一步瞭解

函式呼叫都會傳遞兩個隱式的引數: thisarguments;

  1. arguments 傳遞給函式的所有引數集合,一個類陣列結構

  2. this 呼叫上下文,在哪呼叫,this 就指向誰,而不是取決於宣告的時候。(有兩個特殊的匿名函式和定時器的 this 指向 window

匿名函式

沒有名字的函式都稱匿名函式,所有的函式表示式都屬於匿名函式,立即呼叫函式也是匿名函式

(function(c){
    return console.log(c);
})(10)
複製程式碼

JavaScript沒有塊級作用域,我們常用匿名函式模仿塊級作用域;

for (var i=0;i<10;i++){
    (function(j){
        console.log(j)
    })(i)
}
複製程式碼

匿名函式在實際專案中用的也算比較多

遞迴函式

函式自己呼叫自己(引用自身),並且有終止條件

  1. 普通命名函式遞迴
function num(n){
    return num>1?(num-1)*num:1;
}
複製程式碼
  1. 方法中的遞迴
var ninja={
    chirp:function(n){
        return n>1?ninja.chirp(n-1)*n:1
    }
}
複製程式碼

當我們在方法中遞迴採用了匿名函式的時候,會引來另外一個問題,引用丟失;

  var ninja={
    chirp:function(n){
      return n>1?ninja.chirp(n-1)*n:1;
    }
  }
  var sarural={chirp:ninja.chirp};
  var ninja={};
  console.log(sarural.chirp(4));//報錯
複製程式碼

為什麼會報錯,原因如下: 啊,函式吶!!! 那麼如何解決呢?

  var ninja1={
    chirp:function signal(n){
      return n>1?signal(n-1)*n:1;
    }
  }
  var sarural1={chirp:ninja1.chirp};
  console.log(sarural1.chirp(4));
  var ninja1={};
  console.log(sarural1.chirp(4));//24
複製程式碼

我們在函式內部不適用匿名函式就能解決問題啦! 啊,函式吶!!! 每個函式物件在建立時也隨配有一個prototype屬性,它的值擁有一個constructor屬性且值即為該函式的物件

回撥函式

回撥函式:回撥函式就是先定義一個函式稍後執行,不管是在瀏覽器還是其他地方執行,我們都稱之為回撥函式;也有種說法:回撥函式是一個函式在另一個函式中呼叫

有沒有發現回撥函式在我們寫程式碼的時候處處可見,回撥已經成為 JavaScript 中必不可少的一部分了,我們廣泛使用回撥函式作為事件處理程式

function add(a,b){
    return a+b
}
function sum(fn,c){
    return fn+c
}
sum(add(2,3),4);//9
複製程式碼

我們首先定義了一個 add 函式,然後在 sum 中呼叫了他,雖然這個例子不實用,但是很好的解釋了回撥函式的概念

遞迴函式

一個直接或者間接的呼叫自身的一種函式;他把一個問題分解為一組相似的子問題,每個都用一個尋常解去解決;(呼叫自身去解決她的子問題);

啊,函式吶!!!

遞迴函式可以非常高效的操作樹形結構;

閉包

一句話概括就是:一個函式能夠訪問該函式以外的變數就形成了閉包;

閉包記住的是變數的引用,而不是閉包建立時刻該變數的值

  1. 簡單點的閉包,看完之後有沒有發現我們經常用到
<script>
    var num=1;
    function outerFunction(){
        return num;
    }
    outerFunction()
</script>
複製程式碼
  1. 複雜點的閉包,一個函式內建立另一個函式
<script>
    var outerValue='ninja';
    var later;
    function outerFunction(){
        var innerValue='samurai';
        function innerFunction(){
            console.log(innerValue);
            console.log(outerValue);
        }
        later=innerFunction;
    }
    outerFunction()
    later();
</script>
複製程式碼

在外部函式 outerFunction 執行以後 ,內部函式 innerFunction的引用複製到全域性引用later,因為內部函式 innerFunction引用複製到全域性變數later,所以內部函式一直存在,形成了閉包;

如果直接去呼叫 later 則會報錯,因為內部函式 innerFunction的還沒有引用複製到全域性變數 later

只要內部函式 innerFunction一直存在,就形成了閉包,該函式引用的變數(innerValue,outerValue)就一直存在,沒有被 javaScript 的回收機制給回收,閉包就想保護罩一樣把她們保護起來,不允許外部訪問,也不能被回收機制回收

問題:閉包儲存的是整個變數物件,而不是某個特殊的變數;因為閉包必須維護額外的作用域,因此會比其他函式佔用更多的記憶體,對效能有一定的影響,因此慎重使用閉包;

啊,函式吶!!!

私有變數:任何在函式中定義的變數,都可以認為是私有變數;因為函式的外部不能訪問這些變數,私有變數包括函式的引數,區域性變數,函式內部定義的其他函式

function Private(){
  var num=1;
  this.getnum=function(){
    return num;
  };
  this.setnum=function(){
    num++;
    console.log(num);
  }
}
var private=new Private();
console.log(private.num);//報錯,閉包形成私有變數,訪問不到
console.log(private.getnum());//能夠存取方法來獲取私有變數,但是不能直接訪問私有變數
console.log(private.setnum());
複製程式碼

特權方法:有權訪問私有變數和私有函式的公共方法;利用私有和特權成員,可以隱藏那些不應該被直接修改的資料

Function的方法

原生函式:String(),Number(),Boolean(),Array(),Object(),Function(),RegExp(),Date(),Error(),Symbol(); 原生函式可以直接當做建構函式來使用;建構函式建立出來的是封裝了基本型別的值的封裝物件

  • Function.prototype.bind():bind()方法會建立一個新函式,稱為繫結函式.當呼叫這個繫結函式時,繫結函式會以建立它時傳入 bind()方法的第一個引數作為 this,傳入 bind()方法的第二個以及以後的引數加上繫結函式執行時本身的引數按照順序作為原函式的引數來呼叫原函式.
  • Function.prototype.call() :在一個物件的上下文中應用另一個物件的方法;引數能夠以列表形式傳入。

函式呼叫時 this 的指向

JavaScript 的四種呼叫形式:普通函式呼叫,方法呼叫,構造器呼叫模式,bind呼叫模式

  1. 普通函式呼叫
function add(num1,num2){
    console.log(this)
   return num1+num2;
}
add(2,4);
複製程式碼

如果使用非嚴格模式,this預設指向全域性物件(window);嚴格模式(strict mode),則不能將全域性物件用於預設繫結,因此this會繫結到undefined;

方法呼叫 當一個函式被儲存為物件的一個屬性時,我們稱它為一個方法,this被繫結到該物件(也有意外的情況;有時this會丟掉的物件,回撥函式會修改this)

  var ninja={
   chirp:function(n){
     return n>1?this.chirp(n-1)*n:1;
   }
 }
 var sarural={chirp:ninja.chirp};
 console.log(sarural.chirp(4));
複製程式碼

構造器呼叫

function Ninja(a){
    this.a=a;
}
var ninja=new Ninja('a');
ninja.a;
複製程式碼
  1. 建立(構造)一個全新的物件

  2. 這個新物件被執行[[Prototype]]連結

  3. 這個新物件繫結到函式呼叫的this

  4. 如果函式沒有返回其他物件,那麼new表示式中的函式會自動返回這個新物件

apply(),call(),bind()呼叫模式 apply(),call(),bind() 直接將this,繫結成一個固定的值

var tim = { 
   name: 'tim', 
   age: 20, 
   getName: function() {
       console.log(this.name);
       return this.name; 
   }
}

var jake = { name: 'jake', age: 20 }

tim.getName(); // tim

// jake物件沒有getName方法,但是可以通過call/apply去使用tim物件的getName方法
tim.getName.call(jake);  // jake 
tim.getName.apply(jake); // jake
複製程式碼

call/apply/bind 的理解與例項分享

return語句可用來使函式提前返回,當return被執行時,函式立即返回而不再執行餘下的語句;

原文連結 ,最近弄了個人微信公眾號:sunseekers,有興趣可以關注哈哈哈哈


相關文章