函數丫!函數吶!

函數丫!函數吶!

作為一個初學者,站在一個初學者的角度,有很多地方寫的不全,或者沒寫好,多多見諒。這是我所知道的所有關於函數的知識,如有不完善或者錯誤,希望能夠在評論下方指出,哈哈哈,大神勿噴。

查看原圖

JavaScript中函數是第一類型的對象(函數是對象),我們可以將函數視為任何類型的JavaScript對象;

1 .函數可以擁有有屬性

Function.prototype 或者 Function.arguments...

2 .函數可以擁有方法

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

3 .函數可以賦值給變量,數組或者其他對象屬性給變量

var large=function (){} var obj={ large:function(){} }

4. 函數還能被調用

function large(){},large();

5.函數可以作為參數傳遞給函數,(函數名本身是變量,所以函數也可以作為值來使用;即可以把函數作為參數傳遞給另一個函數,也可以把函數作為另一函數的結果返回; )

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

6. 函數可以作為返回值進行返回

function add(a,b){ return a+b; } add(2,3)//5

當然函數還享有普通對象所擁有的特性,因為Function繼承Object

所以說函數是第一類型對象,函數是代碼執行的主要模塊單元化;在工作中模塊化開發必不可少,只是為了代碼的複用,提高辦公效率,還有模塊化開發能讓代碼清晰易懂,不給自己挖坑,不給他人留坑;我們可以在平時的練習中就養成這樣的好習慣

函數包含一組語句,用來指定對象的某一種行為,是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沒有塊級作用域,我們常用匿名函數模仿塊級作用域;ES6已經實現了塊級作用域;

for (var i=0;i<10;i++){ (function(j){ console.log(j) })(i) }

匿名函數在實際項目中用的也算比較多

遞歸函數

函數自己調用自己(引用自身),並且有終止條件

  1. 普通命名函數遞歸
function num(n){ return n>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調用模式(定時器和匿名函數的this永遠指向全局)

  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被執行時,函數立即返回而不再執行餘下的語句;

原文鏈接

PS(你總說畢業遙遙無期,轉眼大家就各奔東西;總有太多的不捨和留念。不能還能否和我的室友們來一場告別旅行。再來一次深夜臥談會,我們臥談會主題上到國家大事,下到家長里短。那些慷慨激昂日還剩多少。。。。我可以去寫一遍日誌啦哈哈哈)題外話

What do you think?

Written by marketer

外刊君譯者挖掘機計劃

gitlab-tree — gitlab代碼查看必備神器