摘要:關(guān)于構(gòu)造函數(shù)有幾點需要特別注意構(gòu)造函數(shù)允許在運行時動態(tài)的創(chuàng)建并編譯函數(shù)。而函數(shù)本身的表示該函數(shù)的形參。每一個函數(shù)都包含不同的原型對象,當(dāng)將函數(shù)用作構(gòu)造函數(shù)的時候,新創(chuàng)建的對象會從原型對象上繼承屬性。
該文章以收錄: 《JavaScript深入探索之路》 前言
函數(shù)是這樣的一段JavaScript代碼,它只定義一次,但是可能被執(zhí)行或調(diào)用任意次。你可能已經(jīng)從諸如子例程或者過程這些名字里對函數(shù)的概念有所了解。 JavaScript函數(shù)時參數(shù)化的:函數(shù)的定義會包括一個稱為形參的表示符列表,這些參數(shù)在函數(shù)體中像局部變量一樣工作。函數(shù)調(diào)用會為形參提供實參的值,函數(shù)使用它們實參的值來計算返回值,成為該函數(shù)調(diào)用表達式的值。除了實參外,每次調(diào)用還會擁有另一個值 —— 本次調(diào)用的上下文 —— 這就是this關(guān)鍵字的值。
參數(shù)有形參(parameter)和實參(argument)的區(qū)別,形參相當(dāng)于函數(shù)中定義的變量,實參是運行時的函數(shù)調(diào)用時傳入的參數(shù)。
創(chuàng)建函數(shù)一般我們有三種方法去創(chuàng)建函數(shù):
函數(shù)聲明:function foo(){}
函數(shù)表達式:var foo = function(){}
函數(shù)構(gòu)造法:var foo = new Function("a","b","return a+b")
函數(shù)聲明和函數(shù)表達式1.什么是函數(shù)聲明,什么是函數(shù)表達式
ECMA規(guī)范說:函數(shù)聲明必須帶有標(biāo)示符(Identifier)(就是大家常說的函數(shù)名稱),而函數(shù)表達式則可以省略這個標(biāo)示符:
// 函數(shù)聲明 function 函數(shù)名(可選參數(shù)){} // 函數(shù)表達式 function 可選函數(shù)名(可選參數(shù)){}
我們可以很清楚的看出,如果沒有標(biāo)識符(函數(shù)名),該函數(shù)就一定是函數(shù)表達式,如果有標(biāo)識符我們該怎么區(qū)分函數(shù)聲明和函數(shù)表達式呢。
函數(shù)聲明我們不用太多的考慮,很容易看出來,我們來看看函數(shù)表達式都有哪些自然就知道哪些是函數(shù)聲明了。
第一種函數(shù)表達式:var聲明的函數(shù)我們很常見
// 沒有標(biāo)識符一定是表達式 var fun = function(){} //我們還可以這樣寫 var fun = function foo(){}
第二種我們帶有標(biāo)識符,但將函數(shù)賦值給變量后,該函數(shù)就成了函數(shù)表達式。在這里foo標(biāo)識符在該函數(shù)體外訪問時并不起作用
var fun = function foo(a){ if(a){ console.log("外部調(diào)用了"); foo(false); //這會執(zhí)行函數(shù)fun。 }else{ console.log("內(nèi)部調(diào)用了"); } } fun(true); //外部調(diào)用了 ,內(nèi)部調(diào)用了 foo(); // 報錯 ReferenceError: foo is not defined
我們可以看出,函數(shù)表達式中foo標(biāo)識符只能在函數(shù)內(nèi)部使用,去訪問該函數(shù),在函數(shù)外部訪問不到任何東西。上面我們加判斷是為了防止函數(shù)一直遞歸。
第二種函數(shù)表達式:()括號包住的函數(shù)
// 這是函數(shù)表達式 (function foo(){})
括號包住的函數(shù)是函數(shù)表達式是因為,()是一個分組操作符,分組操作符內(nèi)只能是表達式。
例如:
//Unexpected token var (var a = 0)
上面報錯,這是因為var是一個語句,而分組操作符內(nèi)只能是表達式。還有我們在使用evel()時這樣寫 var ev = eval("(" + json + ")") ,這里是強制將{}解析為表達式而不是代碼塊。
2.函數(shù)聲明和函數(shù)表達式的特點
函數(shù)聲明在我們的代碼執(zhí)行前,會被整個提升,也就是說我們在函數(shù)聲明之前就可以調(diào)用該函數(shù),而函數(shù)表達式不能夠被提升。
a() // 結(jié)果 我是函數(shù)a" function a(){ console.log("我是函數(shù)a"); } fun() //結(jié)果 fun is not a function var fun = function b(){ console.log("我是函數(shù)b"); }
為什么會出現(xiàn)這種情況我們在以后的文章中會詳細的討論。同時,我們應(yīng)該養(yǎng)成一種習(xí)慣,盡量不要在函數(shù)聲明之前使用函數(shù)。這只是一種良好的代碼書寫習(xí)慣。
其次我們需要注意的是在if判斷語句中我們不應(yīng)該出現(xiàn)函數(shù)聲明,即便是沒有報錯,也是不推薦這樣去寫的。
立即執(zhí)行函數(shù)(表達式)和自執(zhí)行函數(shù)1. 立即執(zhí)行函數(shù)(表達式)
我們來看一下:
(function (){ console.log("one"); })(); (function (){ console.log("two"); }());
上邊兩個函數(shù)都可以自己去執(zhí)行,而值有個共同的特點就是括號左邊的函數(shù)必須是一個函數(shù)表達式,而不是函數(shù)聲明。
// 如果括號左邊是函數(shù)聲明,報錯 function fun(){ console.log() }() // 括號左邊是函數(shù)時表達式,(函數(shù)表達式都可以自執(zhí)行) var F = function(){ return 10; }(); true && function (){}(); 0 , function (){}(); //你甚至看還可以這樣 !function () { /* code */ } (); ~function () { /* code */ } (); -function () { /* code */ } (); +function () { /* code */ } (); new function(){}();
其實上邊的些自執(zhí)行函數(shù)表達式我們?yōu)榱朔奖阄覀兊拈喿x我們一般會用到第一個和第二個,當(dāng)然如果你非要搞個性化,使用其他的也是可以。只不過你的代碼并不美觀,后期也不容易維護。
2. 什么是自執(zhí)行函數(shù),像這樣
function fun1(){ //code } fun1(); function fun2(){ fun2() // 遞歸 } // 這是一個自執(zhí)行匿名函數(shù) var foo = function () { arguments.callee(); };
看了這些例子,我們應(yīng)該清楚了什么是自執(zhí)行,什么是立即調(diào)用。
函數(shù)構(gòu)造器Function()構(gòu)造函數(shù)并不需要通過傳入實參以指定函數(shù)名,就像函數(shù)直接量一樣,Function()構(gòu)造函數(shù)創(chuàng)建一個匿名函數(shù)。
關(guān)于Function()構(gòu)造函數(shù)有幾點需要特別注意
Function()構(gòu)造函數(shù)允許JavaScript在運行時動態(tài)的創(chuàng)建并編譯函數(shù)。
Function()構(gòu)造函數(shù)每次執(zhí)行時都會解析函數(shù)主體,并創(chuàng)建一個新的函數(shù)對象,所以當(dāng)在一個循環(huán)或頻繁執(zhí)行的函數(shù)中調(diào)用Function()構(gòu)造函數(shù)效率是非常低的。而函數(shù)字面量(函數(shù)表達式)卻不是每次遇到都會重新編譯的,用Function()構(gòu)造函數(shù)創(chuàng)建一個函數(shù)時并不遵循典型的作用域,它一直把它當(dāng)作是頂級函數(shù)來執(zhí)行
它所創(chuàng)建的函數(shù)并不是使用詞法作用域,相反,函數(shù)體代碼的編譯總是會在頂層函數(shù)執(zhí)行,
用函數(shù)構(gòu)造器創(chuàng)建的函數(shù)不會在上下文中創(chuàng)建閉包,它們總是被創(chuàng)建在全局作用域中,當(dāng)執(zhí)行被創(chuàng)建的函數(shù)時,它們只能使用自己的局部變量或者全局變量。例如下面:
var scop = "global"; function fun(){ var scop = "fun"; return new Function("return scope") // global }函數(shù)的屬性、方法和構(gòu)造函數(shù)
1. length屬性
函數(shù)體中,arguments.length表示傳入函數(shù)的實參個數(shù)。而函數(shù)本身的length表示該函數(shù)的形參。
function fun(x,y,z){ console.log(arguments.length);// 2 console.log(arguments.callee.length); //3 } fun(1,2);
我們這里提一下,在函數(shù)中的arguments,函數(shù)中arguments包含所用實參,它并不是一個真正的數(shù)組,而是一個實參的對象。另外他還有兩個特別的屬性callee和caller屬性。
callee屬性只帶黨慶正在執(zhí)行的函數(shù)。
caller屬性時非標(biāo)準(zhǔn)的,但大多數(shù)瀏覽器都實現(xiàn)了這個屬性它指帶調(diào)用當(dāng)前正在執(zhí)行的函數(shù)的函數(shù)。
通過caller屬性可以訪問調(diào)用棧。callee屬性在某些時候會非常的有用,比如在匿名函數(shù)中通過callee來遞歸調(diào)用自身,上面我們已經(jīng)提到過。
2. prototype屬性
每一個函數(shù)都包含一個prototype屬性,這個屬性是指向一個對象的引用,這個對象稱作"原型對象"。每一個函數(shù)都包含不同的原型對象,當(dāng)將函數(shù)用作構(gòu)造函數(shù)的時候,新創(chuàng)建的對象會從原型對象上繼承屬性。
3. call()方法和apply()方法
call 和 apply 是為了改變某個函數(shù)運行時的 context 即上下文而存在的,換句話說,就是為了改變函數(shù)體內(nèi)部 this 的指向。因為 JavaScript 的函數(shù)存在「定義時上下文」和「運行時上下文」以及「上下文是可以改變的」這樣的概念。
它們的用法也是很簡單的。例如:
var obj = { msg:"我是obj對象", fun:function(){ console.log(this.msg); }, } var objTwo = { msg:"我是objTwo對象" }; // 這里我們想讓通過objTwo 對象調(diào)用 obj對象的fun方法, // 此時obj的fun方法中的this已經(jīng)改變了指向。 obj.fun.call(objTwo) // 我是objTwo對象
call 和 apply 的區(qū)別在于他們傳參的形式不同:
var obj = { msg:"我是obj對象", fun:function(one,two){ console.log(this.msg); console.log("我是參數(shù):"+ one + "," +two); }, } var objTwo = { msg:"我是objTwo對象" }; // 這里我們想讓通過objTwo 對象調(diào)用 obj對象的fun方法, // 此時obj的fun方法中的this已經(jīng)改變了指向。 obj.fun.call(objTwo,1,2) // 我是objTwo對象 ,我是參數(shù):1,2 obj.fun.apply(objTwo,[1,2]) // 我是objTwo對象 ,我是參數(shù):1,2高階函數(shù)
所謂高階函數(shù)就是操作函數(shù)的函數(shù),它接收一個或多個函數(shù)作為參數(shù),并返回一個新函數(shù)。我們來看一個例子:
這個例子的主要作用是,fun函數(shù)接收f()和g()兩個函數(shù),并返回一個新的函數(shù)用以計算f(g())
function fun(f,g){ return function(){ return f.call(this,g.apply(this,arguments)); } } var square = function(x){ return x * x } var sum = function(x,y){ return x + y; } var suqareOfSum = compose(square,sum) squareOfSum(2,3) // 25
在這里函數(shù)fun() 就是高階函數(shù)。
不完全函數(shù)不完全函數(shù)是一種函數(shù)變換技巧,即把一次完整的函數(shù)調(diào)用折成多次函數(shù)調(diào)用,每次傳入的實參都是完整實參的一部分,每個拆分開的函數(shù)叫做不完全函數(shù),每次函數(shù)調(diào)用叫做不完全調(diào)用,這種函數(shù)變換的特點是每次調(diào)用都反一個函數(shù),知道得到最終運行結(jié)果為止,例如:將函數(shù)f(1,2,3,4,5)的調(diào)用修改為等價的f(1,2)(3,4)(5)后者包含三次調(diào)用,和每次調(diào)用相關(guān)的函數(shù)就是 不完全函數(shù)”。
結(jié)束參考文獻: 《JavaScript權(quán)威指南》
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/89706.html
摘要:看著別人寫的功能對,就直接拿過來用,寫出來的代碼臃腫到爆炸,滿屏幕的的初級使用方式,運氣好了能湊合跑起來,出了問題全臉懵逼,心中安慰自己一萬遍我可是干設(shè)計的,但是既然打算好好整下就得從頭開始看了。 溫馨提示:作者的爬坑記錄,對你等大神完全沒有價值,別在我這浪費生命 首先,說實話,我對不起javascript,作為一個接觸前端快10年的老前端(偽),一直發(fā)揚的是不求甚解的拿來就用主義。看...
摘要:原理探索模塊系統(tǒng)是什么拋出組件化的概念后,對于開發(fā)者而言,為了提高代碼的可讀性與結(jié)構(gòu)性,通過文件目錄結(jié)構(gòu)去闡述組件嵌套關(guān)系無疑是一個很好的辦法,但是目錄級別的加深,同時讓的文件路徑讓人頭疼。 React原理探索- @providesModule 模塊系統(tǒng) @providesModule是什么 react拋出組件化的概念后,對于開發(fā)者而言,為了提高代碼的可讀性與結(jié)構(gòu)性,通過文件目錄結(jié)構(gòu)去...
摘要:溫馨提示作者的爬坑記錄,對你等大神完全沒有價值,別在我這浪費生命溫馨提示續(xù)本文將會成為一篇筆記類型的文章,記錄閉包具體的應(yīng)用方式溫馨提示再續(xù)本文存在錯誤,會慢慢改進的,請不要把我說的當(dāng)真在上一篇博文閉包不完全探索記錄閉包啥餡的中,對中 溫馨提示:作者的爬坑記錄,對你等大神完全沒有價值,別在我這浪費生命溫馨提示-續(xù):本文(maybe)將會成為一篇筆記類型的文章,記錄閉包具體的應(yīng)用方式溫馨...
摘要:模塊化編程,已經(jīng)成為一個迫切的需求。隨著網(wǎng)站功能逐漸豐富,網(wǎng)頁中的也變得越來越復(fù)雜和臃腫,原有通過標(biāo)簽來導(dǎo)入一個個的文件這種方式已經(jīng)不能滿足現(xiàn)在互聯(lián)網(wǎng)開發(fā)模式,我們需要團隊協(xié)作模塊復(fù)用單元測試等等一系列復(fù)雜的需求。 隨著網(wǎng)站逐漸變成互聯(lián)網(wǎng)應(yīng)用程序,嵌入網(wǎng)頁的Javascript代碼越來越龐大,越來越復(fù)雜。網(wǎng)頁越來越像桌面程序,需要一個團隊分工協(xié)作、進度管理、單元測試等等......開發(fā)...
摘要:因為瀏覽器環(huán)境里是單線程的,所以異步編程在前端領(lǐng)域尤為重要。除此之外,它還有兩個特性,使它可以作為異步編程的完整解決方案函數(shù)體內(nèi)外的數(shù)據(jù)交換和錯誤處理機制。 showImg(https://segmentfault.com/img/bVz9Cy); 在我們?nèi)粘>幋a中,需要異步的場景很多,比如讀取文件內(nèi)容、獲取遠程數(shù)據(jù)、發(fā)送數(shù)據(jù)到服務(wù)端等。因為瀏覽器環(huán)境里Javascript是單線程的,...
閱讀 2069·2021-09-22 15:43
閱讀 8734·2021-09-22 15:07
閱讀 1086·2021-09-03 10:28
閱讀 2059·2021-08-19 10:57
閱讀 1071·2020-01-08 12:18
閱讀 2978·2019-08-29 15:09
閱讀 1530·2019-08-29 14:05
閱讀 1645·2019-08-29 13:57