私人定制javascript中函數小知識點
函數的定義
首先在javascript中,函數就是對象,程序可以隨意操控它們。比如,可以給它們設置屬性,甚至調用它們的方法。函數使用function關鍵字來定義。它既可以用在函數定義表達式,也可以用在函數聲明語句中。函數聲明function后面必須要更上函數名稱也就是所謂的函數名稱標識符。如果是函數表達式函數名稱標識符可有可無。這段重點是函數是對象,所以函數表現出來的種種行為你想想成對象,那么很多疑惑可能就恍如昨日初見。
函數調用
4種方式來調用javascript函數:
1.作為函數
就是函數名稱后加上圓括號加上參數列表 例如 func(2,3); 這里需要說明的是如果函數里有沒有return obj;那么改函數返回值都是undefined除非返回就是undefined值。每個函數都有默認的返回值undefined;除非給予返回值。
2.作為方法
如果一個對象的屬性被賦值為一個函數表達式,那么該函數就被當作一個方法,而不是作為一個普通函數來調用了。
例如:o.m=f; o.m();
3.作為構造函數
如果函數或方法調用之前帶有關鍵字new,它就是構造函數調用,構造函數調用和普通的函數調用以及方法調用在實參處理,調用上下文和返回值方面都有不同。 構造函數通常不會使用return關鍵字,它們通常初始化新對象。
4.通過它們的call()和apply()方法間接調用
再次強調javascript中的函數也是對象,和其他javascript對象一樣,函數對象也可以包含方法,其中每個函數都都有兩個很熟悉不過的函數call()和apply()可以用來間接地調用函數。指定上下文this值,指定參數列表。后者傳入參數時需要按照數組的形式傳入參數。
函數的實參對象
如果在調用方法時少傳參數,那么其余參數都將置為undefined。那么如何知道到底傳了多少參數呢,實際的參數到底是多少個呢?在函數體內,標識符arguments就是指向實參對象的引用,實參對象視一個類數組對象。
例如
function f(x,y,z) { if(arguments.length!=3){ throw new Error(“arguments length is should be 3”); } }
arguments.length是指實參個數,而f.length是指形參的個數
callee和caller屬性
callee屬性指代當前正在執行的函數,caller可以訪問調用棧
淺說閉包
現在不得不扯到閉包,javascript采用詞法作用域,就是說函數的執行依賴于變量作用域,這個作用域是在函數定義時決定的,而不是函數執行時決定的。為了實現這種詞法作用域,javascript函數對象的內部狀態不僅包含函數邏輯代碼,還必須引用當前的作用域鏈。函數對象可以通過作用域鏈相互關聯起來,函數體內部的變量都可以保存在函數作用域內,這種特性就叫做閉包。函數定義時的作用域鏈在調用函數時依然有效,但是這并不影響閉包。
首先上一段簡單的代碼:
var scope=”global scope”; function f(){ var scope=“local scope”; function func(){return scope;} return func(); } console.log(f()); //”local scope“
簡單解釋一下,關鍵是變量scope在函數func定義的時候就在同一個作用域鏈中,而var scope=”global scope”;是在最外層的作用域中,一般都在作用域中都會存在一個變量對象來專門存放作用域鏈,全局變量肯定是最先被存放就進去,即壓入棧底,而此時在執行調用func就會找最近的變量scope找到了,立馬返回。所以就會是local scope不知道我的語言組織你能明白么?
var scope=”global scope”; function f(){ var scope=“local scope”; function func(){return scope;} return func; } console.log(f()());
請問結果是多少?
這個時候將javascript詞法作用域的基本規則體現的很淋漓盡致,javascript函數執行用到了作用域鏈,這個作用域鏈式函數定義的時候創建的。嵌套的函數func()定義在這個作用域鏈里,其中變量scope一定是選取最近的局部變量,不管任何時候執行func(),這種綁定在執行func()時依然有效,因此返回為”local scope”. 多個函數共享一個作用域
function counterfun(){ var n=0; return{ count:function(){return n++;}, reset:function(){return n=0;} }; } var c=counterfun(),d=counterfun(); console.log(c.count()); //0 console.log(c.reset()); //0 console.log(d.count()); //0 console.log(c.reset()); //0 console.log(d.count());//1
分析:因為函數count和reset共享一個屬性,所以如果c.count()和c.reset()都會改變n的值并且相互影響,但是影響不了d.count()和d.reset()因為它們創建了各自獨立的作用域鏈。 但是為什么n的值會被保存下來呢?此時你再回過頭看看scope變量的那個例子道理一樣,用return返回函數時都已經將私有變量保存起來了,下次再執行時肯定還是取這段作用域中的變量來操作。
易出錯且經典的例子
如下:
function constfuncs(){ var funcs=[]; for(var i=0;i<10;i++) funcs[i]=function(){return i;}; return funcs; } var funcs=constfuncs(); console.log(funcs[5]());//輸出是什么呢?
這個應該不會出錯吧。必須是10,因為這里所有的函數funcs[i]都共享一個作用域鏈其中變量i同樣是共享。所以最終是循環結束的最大值10 那接下來的這個呢?
function constfunc(v){return function(){return v;};} var funcs=[]; for(var i=0;i<10;i++) funcs[i]=constfunc(i); console.log(funcs[5]());//輸出是什么呢?
輸出是5,結果是我們預期的。 這里如果按照所有的函數作用域是定義的時候就確定了的,理解起來比較費勁么,不管你信不信,反正我是中槍了。 從函數嵌套來理解,因為函數中返回函數嵌套時必須會將函數及其作用域鏈保存起來,這樣就新創建了一個作用域鏈,其中的變量i也就被保存起來賦值給v了,每每return一次就會新創建一個作用域鏈。所以得到的v就是當時保存的i值。
prototype屬性
每個函數都用一個prototype屬性,請記住它是函的一個屬性,盡管這個prototype我們總是稱之為對象,但是它確實是函數的一個對象。它就稱之為原型對象,不管你是否打算將這個函數作為構造函數來用。那問題來了,為啥我們要關注prototype屬性呢?
1.原生構造函數(如 Object()/Array()/Function()等)使用prototype屬性,以便讓構造函數實力繼承屬性和方法。
2.當用戶自定義構造函數時,可以像javascript原生對象那樣使用繼承。
3.可以借用別人編寫的優秀代碼的時候繼承賦值過來自己用
4.通過使用原型繼承,我們可以創建有效的對象實例,并且使用相同的方法,這樣就節省了很多代碼。
結束語
本節重點是函數是對象,所表現出來的一些特性,同時函數作用域鏈相關的閉包這4個例子應該是堪稱經典了,以后可以經常看看,還有比較重要的作用域鏈等等,以后再深入了解,再繼續分享。同時如果有錯誤,還請指出。今天看了樂嘉書中,覺得有一句話說的挺好的,我全力回顧了自己曾有的夢想,對比已實現的和沒實現的,終于發現,夢想都與行動有關,不動就是幻想,動了就是夢想,動到底夢想就實現了。也許我做的夢,剛好也是你的夢,果真如此,此刻開始,你我就算是同夢中人了。如果你覺得對你有所幫助,推薦一下,給我下次寫作的動力。
ps: 給大家介紹一個新詞(不要問我是誰,請叫我好人)。
路見不平
舊意:指走在路上遇見不公平的事情
新意:指走在路上在一大波平胸妹子中發現一個大波妹子
例句:路見不平,拔屌想護

浙公網安備 33010602011771號