啊,函數吶!!!

一份須要你補充完整的函數導圖!我仍是一個初學者,這篇文章是我所知道的全部關於函數的知識,若有不完善或者錯誤,但願可以在評論下方指出,哈哈哈,大神勿噴。
java


查看原圖react

在 JavaScript 中函數是第一類型的對象(函數是對象),咱們能夠將函數視爲任何類型的 JavaScript 對象;git

  1. 函數能夠擁有有屬性

Function.prototype 或者 Function.arguments...github

  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
複製代碼

因此說函數是第一類型對象,函數是代碼執行的主要模塊單元化segmentfault

函數包含一組語句,用來指定對象的某一種行爲,是JavaScript的基礎模塊單元,用於代碼複用,信息隱藏和組合調用;

所謂的編程,就是將一組需求分解成一組函數與數據結構的技能數組

函數名是指向函數對象的指針。若是沒有函數名,咱們就稱之爲匿名函數,匿名函數沒有指向函數對象的指針,通常狀況下咱們會當即執行函數,或者將匿名函數賦值給變量;瀏覽器

函數建立的兩種方式:函數聲明和函數表達式(匿名函數,拉姆達函數) bash

其中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.apply():在一個對象的上下文中應用另外一個對象的方法;參數可以以數組形式傳入。
  • 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,有興趣能夠關注哈哈哈哈

相關文章
相關標籤/搜索