這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)?lái)有關(guān)JavaScript函數(shù)的特性有哪些,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
創(chuàng)新互聯(lián)是一家從事企業(yè)網(wǎng)站建設(shè)、成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、外貿(mào)網(wǎng)站建設(shè)、行業(yè)門戶網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)制作的專業(yè)的建站公司,擁有經(jīng)驗(yàn)豐富的網(wǎng)站建設(shè)工程師和網(wǎng)頁(yè)設(shè)計(jì)人員,具備各種規(guī)模與類型網(wǎng)站建設(shè)的實(shí)力,在網(wǎng)站建設(shè)領(lǐng)域樹立了自己獨(dú)特的設(shè)計(jì)風(fēng)格。自公司成立以來(lái)曾獨(dú)立設(shè)計(jì)制作的站點(diǎn)近1000家。
1 函數(shù)對(duì)象
JavaScript 函數(shù)就是對(duì)象。對(duì)象是名值對(duì)的集合,它還擁有一個(gè)連接到原型對(duì)象的鏈接。對(duì)象字面量產(chǎn)生的對(duì)象連接到 Object.prototype
,而函數(shù)對(duì)象連接到 Function.prototype(這個(gè)對(duì)象本身連接到 Object.prototype
)。每個(gè)函數(shù)在創(chuàng)建時(shí)會(huì)附加兩個(gè)隱藏屬性:函數(shù)的上下文以及實(shí)現(xiàn)函數(shù)的代碼。
函數(shù)對(duì)象在創(chuàng)建后會(huì)有一個(gè) prototype 屬性,它的值是一個(gè)擁有 constructor 屬性、且值既是該函數(shù)的對(duì)象。
因?yàn)楹瘮?shù)是對(duì)象,所以可以被當(dāng)做參數(shù)傳遞給其他函數(shù)。它也可以再返回函數(shù)。
函數(shù)可以通過(guò)字面量進(jìn)行創(chuàng)建:
var add = function (a, b) { return a + b; }
這里沒有給函數(shù)命名,所以稱它為匿名函數(shù)。
一個(gè)內(nèi)部函數(shù)除了可以訪問(wèn)自己的參數(shù)和變量之外,還可以訪問(wèn)它的父函數(shù)的參數(shù)和變量。通過(guò)函數(shù)字面量創(chuàng)建的函數(shù)對(duì)象包含一個(gè)連接到外部上下文的連接,這被稱為閉包。它是 JavaScript 強(qiáng)大表現(xiàn)力的來(lái)源。
調(diào)用一個(gè)函數(shù)會(huì)暫停當(dāng)前函數(shù)的執(zhí)行,它會(huì)傳遞控制權(quán)和參數(shù)給這個(gè)被調(diào)用的函數(shù)。
當(dāng)函數(shù)的實(shí)際參數(shù)的個(gè)數(shù)與形式參數(shù)的個(gè)數(shù)不匹配時(shí),不會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。如果實(shí)際參數(shù)的個(gè)數(shù)過(guò)多,那么超出的參數(shù)會(huì)被忽略;如果實(shí)際參數(shù)的個(gè)數(shù)過(guò)少,那么缺失的值會(huì)是 undefined。不會(huì)對(duì)參數(shù)類型進(jìn)行檢查,所以任何類型的值都可以被傳遞給任何參數(shù)。
當(dāng)一個(gè)函數(shù)被保存為對(duì)象的一個(gè)屬性時(shí),就稱它為方法。當(dāng)方法被調(diào)用時(shí),this 被綁定到這個(gè)對(duì)象。如果調(diào)用表達(dá)式包含一個(gè)提取屬性的動(dòng)作(即包含一個(gè)”.” 點(diǎn)表達(dá)式或 “[]” 下標(biāo)表達(dá)式),那么它就是被當(dāng)做一個(gè)方法被調(diào)用。
var myObject = { value: 0,//屬性 increment: function (inc) {//方法 this.value += typeof inc === 'number' ? inc : 1; } }; myObject.increment(); console.log(myObject.value);//1 myObject.increment(2); console.log(myObject.value);//3
這里可以使用 this 來(lái)訪問(wèn)自己所屬的對(duì)象。通過(guò) this 可以取得它們所屬對(duì)象的上下文的方法被稱為公共方法。
var add = function (a, b) { return a + b; } var sum = add(3, 4);//7;this 被綁定到全局對(duì)象
這里的 this 被綁定到全局對(duì)象,這其實(shí)是語(yǔ)言設(shè)計(jì)上的失誤!如果設(shè)計(jì)正確,那么當(dāng)內(nèi)部函數(shù)被調(diào)用時(shí),this 應(yīng)該被綁定到外部函數(shù)的 this 變量才是??梢赃@樣解決:為這個(gè)方法定義一個(gè)變量并給它賦值為 this,這樣內(nèi)部函數(shù)就可以通過(guò)這個(gè)變量訪問(wèn)到 this 啦,一般把這個(gè)變量命名為 that:
myObject.double = function () { var that = this;//讓內(nèi)部函數(shù)可以通過(guò)這個(gè)變量訪問(wèn)到 this (myObject) var helper = function () { that.value = add(that.value, that.value); }; helper();//以函數(shù)形式調(diào)用 helper }; myObject.double();//以方法形式調(diào)用 helper console.log(myObject.value);//6
JavaScript 是基于原型繼承的語(yǔ)言,所以對(duì)象可以從其他對(duì)象繼承它們的屬性。
如果在函數(shù)之前加上 new ,那么 JavaScript 就會(huì)創(chuàng)建一個(gè)連接到該函數(shù)的 prototype 屬性的新對(duì)象,而 this 會(huì)綁定到這個(gè)新對(duì)象。
/** * 構(gòu)造器調(diào)用模式(不推薦) */ var Quo = function (string) {//定義構(gòu)造器函數(shù);按照約定,變量名首字母必須大寫 this.status = string;//屬性 }; /** * 為 Quo 的所有實(shí)例提供一個(gè)名為 get_status 的公共方法 * @returns {*} */ Quo.prototype.get_status = function () { return this.status; }; var myQuo = new Quo("confused");//定義一個(gè) Quo 實(shí)例 console.log(myQuo.get_status());//"confused"
按照約定,構(gòu)造器函數(shù)被保存在以大寫字母命名的變量中。因?yàn)槿绻{(diào)用構(gòu)造器函數(shù)時(shí)沒有加上 new,問(wèn)題很大,所以才以大寫字母的命名方式讓大家記住調(diào)用時(shí)要加上 new。
因?yàn)?JavaScript 是函數(shù)式的面向?qū)ο笳Z(yǔ)言,所以函數(shù)可以擁有方法。
apply 方法可以構(gòu)建一個(gè)參數(shù)數(shù)組,然后再傳遞給被調(diào)用的函數(shù)。這個(gè)方法接收兩個(gè)參數(shù):要綁定給 this 的值以及參數(shù)數(shù)組。
//相加 var array = [3, 4]; var sum = add.apply(null, array);//7 console.log(sum); //調(diào)用 Quo 的 get_status 方法,給 this 綁定 statusObject 上下文 var statusObject = { status: 'A-OK' }; var status = Quo.prototype.get_status.apply(statusObject); console.log(status);//'A-OK'
當(dāng)函數(shù)被調(diào)用時(shí),會(huì)有一個(gè) arguments 數(shù)組。它是函數(shù)被調(diào)用時(shí),傳遞給這個(gè)函數(shù)的參數(shù)列表,包含那些傳入的、多出來(lái)的參數(shù)??梢岳眠@一點(diǎn),編寫一個(gè)無(wú)須指定參數(shù)個(gè)數(shù)的函數(shù):
//構(gòu)造一個(gè)能夠接收大量參數(shù),并相加的函數(shù) var sum = function () { var i, sum = 0; for (i = 0; i < arguments.length; i += 1) { sum += arguments[i]; } return sum; }; console.log(sum(4, 5, 6, 7, 8, 9));//39
arguments 不是一個(gè)真正的數(shù)組,它只是一個(gè)類數(shù)組的對(duì)象,它擁有 length 屬性,但沒有數(shù)組的相關(guān)方法。
return 語(yǔ)句可以讓函數(shù)提前返回。return 被執(zhí)行時(shí),函數(shù)會(huì)立即返回。
一個(gè)函數(shù)總會(huì)返回一個(gè)值,如果沒有指定這個(gè)值,它就會(huì)返回 undefined。
如果使用 new 前綴來(lái)調(diào)用一個(gè)函數(shù),那么它的返回值是:創(chuàng)建的一個(gè)連接到該函數(shù)的 prototype 屬性的新對(duì)象。
異常是干擾程序正常流程的事故。發(fā)生事故時(shí),我們要拋出一個(gè)異常:
var add = function (a, b) { if (typeof a !== 'number' || typeof b !== 'number') { throw{ name: 'TypeError', message: 'add needs numbers' }; } return a + b; }
throw 語(yǔ)句會(huì)中斷函數(shù)的執(zhí)行,它要拋出一個(gè) exception 對(duì)象,這個(gè)對(duì)象包含一個(gè)用來(lái)識(shí)別異常類型的 name 屬性和一個(gè)描述性的 message 屬性。也可以根據(jù)需要,擴(kuò)展這個(gè)對(duì)象。
這個(gè) exception 對(duì)象會(huì)被傳遞到 try 語(yǔ)句的 catch 從句:
var try_it = function () { try { add("seven"); } catch (e) { console.log(e.name + ": " + e.message); } }; try_it();
一個(gè) try 語(yǔ)句只會(huì)有一個(gè)捕獲所有異常的 catch 從句。所以如果處理方式取決于異常的類型,那么我們就必須檢查異常對(duì)象的 name 屬性,來(lái)確定異常的類型。
可以給 Function.prototype 增加方法來(lái)使得這個(gè)方法對(duì)所有的函數(shù)都可用:
/** * 為 Function.prototype 新增 method 方法 * @param name 方法名稱 * @param func 函數(shù) * @returns {Function} */ Function.prototype.method = function (name, func) { if (!this.prototype[name])//沒有該方法時(shí),才添加 this.prototype[name] = func; return this; };
通過(guò)這個(gè)方法,我們給對(duì)象新增方法時(shí),就可以省去 prototype 字符啦O(∩_∩)O~
有時(shí)候需要提取數(shù)字中的整數(shù)部分,我們可以為 Number.prototype
新增一個(gè) integer 方法:
Number.method('integer', function () { return Math[this < 0 ? 'ceil' : 'floor'](this); });
它會(huì)根據(jù)數(shù)字的正負(fù)來(lái)決定是使用 Math.ceiling
還是 Math.floor
。
然后再為 String 添加一個(gè)移除字符串首尾空白的方法:
String.method('trim', function () { return this.replace(/^\s+|\s+$/g, ''); });
這里使用了正則表達(dá)式。
通過(guò)為基本類型增加方法,可以極大地提高 JavaScript 的表現(xiàn)力。因?yàn)樵屠^承的動(dòng)態(tài)本質(zhì),新的方法立刻被賦予所有的對(duì)象實(shí)例上(甚至包括那些在方法被增加之前的那些對(duì)象實(shí)例)
基本類型的原型是公用的,所以在使用其他類庫(kù)時(shí)要小心。一個(gè)保險(xiǎn)的做法是:只在確定沒有該方法時(shí)才添加它。
Function.prototype.method = function (name, func) { if (!this.prototype[name])//沒有該方法時(shí),才添加 this.prototype[name] = func; return this; };
遞歸函數(shù)是會(huì)直接或間接地調(diào)用自身的函數(shù)。它會(huì)把一個(gè)問(wèn)題分解為一組相似的子問(wèn)題,而每一個(gè)子問(wèn)題都會(huì)用一個(gè)尋常的解來(lái)解決。
漢諾塔的游戲規(guī)則是:塔上有 3 根柱子和一套直徑不相同的空心圓盤。開始時(shí),源柱子上的所有圓盤都是按照從小到大的順序堆疊的。每次可以移動(dòng)一個(gè)圓盤到另一個(gè)柱子,但不允許把較大的圓盤放置在嬌小的圓盤之上。最終的目標(biāo)是把一堆圓盤移動(dòng)到目標(biāo)柱子上。我們可以用遞歸解決這個(gè)問(wèn)題:
/** * 漢若塔 * @param disc 圓盤編號(hào) * @param src 源柱子 * @param aux 輔助用的柱子 * @param dst 目的柱子 */ var hanoi = function (disc, src, aux, dst) { if (disc > 0) { hanoi(disc - 1, src, dst, aux); console.log('Move disc ' + disc + ' from ' + src + ' to ' + dst); hanoi(disc - 1, aux, src, dst); } }; hanoi(3, 'Src', 'Aux', 'Dst');
這里演示了如果圓盤數(shù)為 3 的解法:
這個(gè)問(wèn)題可以分解為 3 個(gè)子問(wèn)題。首先移動(dòng)一對(duì)圓盤中較小的圓盤到輔助的柱子上,從而露出下面較大的圓盤;然后再移動(dòng)下面的圓盤到目標(biāo)柱子。最后再將較小的圓盤從輔助柱子再移動(dòng)到目標(biāo)柱子上。通過(guò)遞歸調(diào)用自身來(lái)處理圓盤的移動(dòng),就可以解決這些子問(wèn)題。
上面這個(gè)函數(shù)最終會(huì)以一個(gè)不存在的圓盤編號(hào)被調(diào)用,但它不執(zhí)行任何操作,所以不會(huì)導(dǎo)致死循環(huán)。
遞歸函數(shù)可以非常高效地操作樹型結(jié)構(gòu)。比如文檔對(duì)象模型(DOM),我們可以在每次遞歸調(diào)用時(shí)處理指定樹的一小段:
/** * 從某個(gè)節(jié)點(diǎn)開始,按照 HTML 源碼中的順序,訪問(wèn)該樹的每一個(gè)節(jié)點(diǎn) * @param node 開始的節(jié)點(diǎn) * @param func 被訪問(wèn)到的每一個(gè)節(jié)點(diǎn),會(huì)作為參數(shù)傳入這個(gè)函數(shù),然后這個(gè)函數(shù)被調(diào)用 */ var walk_the_DOM = function walk(node, func) { func(node); node = node.firstChild; while (node) { walk(node, func); node = node.nextSibling; } };
/** * 查找擁有某個(gè)屬性的元素 * @param att 屬性名稱字符串 * @param value 匹配值(可選) * @return 匹配的元素?cái)?shù)組 */ var getElementsByAttributes = function (att, value) { var results = []; walk_the_DOM(document.body, function (node) { var actual = node.nodeType === 1 && node.getAttribute(attr); if (typeof actual === 'string' && (actual === value) || typeof value != 'string') { results.push(node); } }); return results; };
注意: 深度遞歸的函數(shù)會(huì)因?yàn)槎褩R绯龆\(yùn)行失敗。比如一個(gè)會(huì)返回自身調(diào)用函數(shù)結(jié)果的函數(shù),它被稱為尾遞歸函數(shù)。
/** * 求階乘(帶尾遞歸的函數(shù)) * * JavaScript 當(dāng)前沒有對(duì)尾遞歸進(jìn)行優(yōu)化,所以如果遞歸過(guò)深會(huì)導(dǎo)致堆棧溢出 * @param i * @param a * @returns {*} 返回自身調(diào)用的結(jié)果 */ var factorial = function factorial(i, a) { a = a || 1; if (i < 2) { return a; } return factorial(i - 1, a * i); }; console.log(factorial(4));
作用域控制著變量和參數(shù)的可見性以及生命周期,它減少了名稱沖突,而且提供自動(dòng)內(nèi)存管理機(jī)制。
var foo = function () { var a = 3, b = 5; var bar = function () { var b = 7, c = 11; console.log("a:" + a + ";b:" + b + ";c:" + c);//a:3;b:7;c:11 a += b + c; console.log("a:" + a + ";b:" + b + ";c:" + c);//a:21;b:7;c:11 }; console.log("a:" + a + ";b:" + b);//a:3;b:5 bar(); console.log("a:" + a + ";b:" + b);//a:21;b:5 }; foo();
JavaScript 支持函數(shù)作用域,但要注意一點(diǎn),就是在一個(gè)函數(shù)內(nèi)部的任何位置定義的變量,都這個(gè)函數(shù)的任何地方都是可見的!
**注意:**JavaScript 不支持塊級(jí)作用域。所以最好的做法是在函數(shù)體的頂部,聲明函數(shù)中可能會(huì)用到的所有變量。
作用域的好處是:內(nèi)部函數(shù)可以訪問(wèn)定義它們外部函數(shù)的參數(shù)和變量(除了 this 和 arguments)。
注意:內(nèi)部函數(shù)擁有比它的外部函數(shù)更長(zhǎng)的生命周期。
var myObject = (function () { var value = 0;//只對(duì) increment 與 getValue 可見 return { increment: function (inc) { value += typeof inc === 'number' ? inc : 1; }, getValue: function () { return value; } } }());
通過(guò)一個(gè)函數(shù)的形式來(lái)初始化 myObject 對(duì)象,它會(huì)返回一個(gè)對(duì)象字面量。函數(shù)定義了一個(gè) value 變量,這個(gè)變量對(duì) increment 和 getValue 方法總是可見的,但它的函數(shù)作用域卻使得這個(gè)變量對(duì)其他的代碼來(lái)說(shuō)是不可見的!
/** * quo 構(gòu)造函數(shù) * @param status 私有屬性 * @returns {{get_status: Function}} */ var quo = function (status) { return { get_status: function () {//方法 return status; } }; }; var myQuo = quo("amazed"); console.log(myQuo.get_status());//amazed
當(dāng)我們調(diào)用 quo 時(shí),它會(huì)返回一個(gè)包含 get_status 方法的新對(duì)象,它的引用保存在 myQuo 中。所以即使 quo 函數(shù)已經(jīng)返回了,但 get_status 方法仍然享有訪問(wèn) quo 對(duì)象的 status 屬性的特權(quán)。get_status 方法訪問(wèn)的可是 status 屬性本身,這就是閉包哦O(∩_∩)O~
再看一個(gè)例子:
/** * 設(shè)置一個(gè) DOM 節(jié)點(diǎn)為黃色,然后漸變?yōu)榘咨? * @param node */ var fade = function (node) { var level = 1; var step = function () { var hex = level.toString(16);//轉(zhuǎn)換為 16 位字符 node.style.backgroundColor = '#FFFF' + hex + hex; if (level < 15) { level += 1; setTimeout(step, 100); } }; setTimeout(step, 100); }; fade(document.body);
fade 函數(shù)在最后一行被調(diào)用后已經(jīng)返回,但只要 fade 的內(nèi)部函數(shù)又需要,它的變量就會(huì)持續(xù)保留。
注意:內(nèi)部函數(shù)能夠訪問(wèn)外部函數(shù)的實(shí)際變量:
var add_the_handlers_error = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i);//綁定的是變量 i 本身,而不是函數(shù)在構(gòu)造時(shí)的變量 i 的值?。?! } } };
add_the_handlers_error 函數(shù)的本意是:想傳遞給每個(gè)事件處理器一個(gè)唯一的 i 值,但因?yàn)槭录幚砥骱瘮?shù)綁定了變量 i 本身,而不是它的值!
/** * 給數(shù)組中的節(jié)點(diǎn)設(shè)置事件處理程序(點(diǎn)擊節(jié)點(diǎn),會(huì)彈出一個(gè)顯示節(jié)點(diǎn)序號(hào)的對(duì)話框) * @param nodes */ var add_the_handlers = function (nodes) { var helper = function (i) {//輔助函數(shù),綁定了當(dāng)前的 i 值 return function (e) { alert(i); }; }; var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = helper(i); } };
我們?cè)谘h(huán)之外向構(gòu)造一個(gè)輔助函數(shù),讓這個(gè)函數(shù)返回一個(gè)綁定了當(dāng)前 i 值的函數(shù),這樣就可以解決問(wèn)題啦O(∩_∩)O~
假設(shè)用戶觸發(fā)了一個(gè)請(qǐng)求,瀏覽器向服務(wù)器發(fā)送這個(gè)請(qǐng)求,然后最終顯示服務(wù)器的響應(yīng)結(jié)果:
request = prepare_the_request(); response = send_request_synchronously(request); display(response);
這種方式的問(wèn)題在于,網(wǎng)絡(luò)上的同步請(qǐng)求可能會(huì)導(dǎo)致客戶端進(jìn)入假死狀態(tài)。
所以建議使用異步請(qǐng)求,并為服務(wù)端的響應(yīng)創(chuàng)建一個(gè)回調(diào)函數(shù)。這個(gè)異步請(qǐng)求函數(shù)會(huì)立即返回,這樣我們的客戶端就不會(huì)被阻塞啦:
request = prepare_the_request(); send_request_asynchronously(request, function(response){ display(response); )};
一旦接收到服務(wù)端的響應(yīng),傳給 send_request_asynchronously 的匿名函數(shù)就會(huì)被調(diào)用啦O(∩_∩)O~
模塊是一個(gè)提供接口但卻隱藏狀態(tài)與實(shí)現(xiàn)的函數(shù)??梢允褂煤瘮?shù)和閉包來(lái)構(gòu)建模塊。通過(guò)函數(shù)來(lái)生成模塊,就可以不用全局變量啦。
假設(shè)我們想給 String 增加一個(gè) deentityify 方法。它會(huì)尋找字符串中的 HTML 字符,并把它們替換為對(duì)應(yīng)的字符。這就需要在對(duì)象中保存字符實(shí)體的名字和它對(duì)應(yīng)的字符。不能用全局變量,因?yàn)樗悄Ч?!如果定義在函數(shù)內(nèi)部,那么就會(huì)帶來(lái)運(yùn)行時(shí)的損耗,因?yàn)槊看螆?zhí)行函數(shù)時(shí),這個(gè)字面量就會(huì)被求值一次。所以理想的方式是把它放入閉包:
String.method('deentityify', function () { //字符實(shí)體表:映射字符實(shí)體的名字到對(duì)應(yīng)的字符 var entity = { quot: '"', lt: '<', gt: '>' }; //返回 deentityify 方法 return function () { /** * 返回以'&'開頭 和 以';'結(jié)尾的子字符串 */ return this.replace(/&([^$;]+);/g, function (a, b) { var r = entity[b];//b:映射字符實(shí)體名字 return typeof r === 'string' ? r : a;//a:原始字符串 }); }; }());
注意:最后一行是 (),所以我們是立即調(diào)用剛剛創(chuàng)建的函數(shù)!
模塊模式利用函數(shù)的作用域和閉包來(lái)創(chuàng)建被綁定對(duì)象和私有成員的關(guān)聯(lián)。這個(gè)例子中只有 deentityify 方法才有權(quán)訪問(wèn) entity (字符實(shí)體表)。
模塊模式是一個(gè)定義了私有變量和函數(shù)的函數(shù)。先利用閉包創(chuàng)建一個(gè)可以訪問(wèn)私有變量和函數(shù)的特權(quán)函數(shù),最后返回這個(gè)函數(shù),或者把它保存到一個(gè)可以被訪問(wèn)到的地方。
模塊模式做到了信息隱藏,是一種優(yōu)秀的設(shè)計(jì)實(shí)踐,特別適合于封裝應(yīng)用程序或者構(gòu)造單例哦O(∩_∩)O~
模塊模式還可以產(chǎn)生安全對(duì)象。比如我們想構(gòu)造一個(gè)能夠產(chǎn)生序列號(hào)的對(duì)象:
/** * 產(chǎn)生唯一字符串的對(duì)象(安全的對(duì)象) * 唯一字符串由 (前綴 + 序列號(hào)) 組成 * @returns {{set_prefix: Function, set_seq: Function, gensym: Function}} */ var serial_number = function () { var prefix = ''; var seq = 0; return { /** * 設(shè)置前綴 * @param p */ set_prefix: function (p) { prefix = String(p); }, /** * 設(shè)置序列號(hào) * @param s */ set_seq: function (s) { seq = s; }, /** * 產(chǎn)生唯一的字符串 * @returns {string} */ gensym: function () { var result = prefix + seq; seq += 1; return result; } }; }; var seqer = serial_number(); seqer.set_prefix('Q'); seqer.set_seq(1000); var unique = seqer.gensym(); console.log(unique);//Q1000
sequer 包含的方法沒有用到 this 或 that,所以很安全。sequer 是一組函數(shù)的集合,只有那些特權(quán)函數(shù)才能夠獲取或修改私有屬性哦O(∩_∩)O~
如果把 sequer.gensym
作為值傳遞給第三方函數(shù),那么那個(gè)函數(shù)可以使用它產(chǎn)生唯一的字符串,但卻不能通過(guò)這個(gè)函數(shù)改變 prefix 或 seq 的值。因?yàn)檫@個(gè)函數(shù)的功能只是“產(chǎn)生唯一的字符串”呀!
如果我們讓某些方法返回 this,那么就會(huì)啟動(dòng)級(jí)聯(lián)。在級(jí)聯(lián)中,我們可以在單條語(yǔ)句中依次調(diào)用同一個(gè)對(duì)象的多個(gè)方法,最著名的例子就是 jQuery 哦O(∩_∩)O~
形如:
getElement('myDiv') .move(350,150) .width(100);
級(jí)聯(lián)可以產(chǎn)生出極富表現(xiàn)力的接口。
柯里化指的是:把函數(shù)與傳遞給它的參數(shù)結(jié)合,產(chǎn)生出新的函數(shù):
Function.method('curry', function () { var slice = Array.prototype.slice, args = slice.apply(arguments),//創(chuàng)建一個(gè)真正的數(shù)組 that = this; return function () { return that.apply(null, args.concat(slice.apply(arguments))); }; }); var add1 = add.curry(1); console.log(add1(6));//7
因?yàn)?arguments 不是真正的數(shù)組,所以沒有 concat 方法,因此我們使用 slice 創(chuàng)建出了真正的數(shù)組。
有時(shí)候可以把先前計(jì)算過(guò)的結(jié)果記錄在某個(gè)對(duì)象中,避免重復(fù)計(jì)算。
假設(shè)我們想通過(guò)遞歸來(lái)計(jì)算 Fibonacci 數(shù)列。一個(gè) Fibonacci 數(shù)字是之前的兩個(gè) Fibonacci 數(shù)字之和,最前面的兩個(gè)數(shù)字是 0 和 1:
var fiboncci = function () { var memo = [0, 1];//存儲(chǔ)結(jié)果(已經(jīng)計(jì)算過(guò)的值) var fib = function (n) { var result = memo[n]; if (typeof result !== 'number') { result = fib(n - 1) + fib(n - 2); memo[n] = result; } return result; }; return fib; }(); for (var i = 0; i <= 10; i += 1) { console.log(i + ": " + fiboncci(i)); }
我們把計(jì)算結(jié)果存儲(chǔ)在 memo 數(shù)組中,它被隱藏在閉包里。函數(shù)被調(diào)用時(shí),它會(huì)先檢查計(jì)算結(jié)果是否已存在,如果存在就立即返回。
我們對(duì)這個(gè)例子進(jìn)行擴(kuò)展,構(gòu)造一個(gè)帶記憶功能的函數(shù):
/** * 帶記憶功能的函數(shù) * @param memo 初始的 memo 數(shù)組 * @param formula 公式函數(shù) * @returns {Function} */ var memoizer = function (memo, formula) { var recur = function (n) { var result = memo[n]; if (typeof result != 'number') { result = formula(recur, n); memo[n] = result; } return result; }; return recur; };
這個(gè)函數(shù)會(huì)返回一個(gè)可以管理 meno 存儲(chǔ)參數(shù)和在需要時(shí)調(diào)用 formula 函數(shù)的 recur 函數(shù)。recur 函數(shù)和它的參數(shù)會(huì)被傳遞給 formula 函數(shù)(就是公式)。是不是感覺有點(diǎn)繞,我們來(lái)看看實(shí)例就會(huì)清楚啦。
現(xiàn)在我們使用 memoizer 函數(shù)來(lái)重新定義 fibonacci 函數(shù):
/** * 斐波那契 * @type {Function} */ var fibonacci = memoizer([0, 1], function (recur, n) { return recur(n - 1) + recur(n - 2); }); console.log(fibonacci(10));//55
這樣清楚了吧,使用這個(gè)函數(shù)可以極大地減少我們的工作量哦,比如下面的這個(gè)階乘函數(shù):
/** * 階乘 * @type {Function} */ var factorial = memoizer([1, 1], function (recur, n) { return n * recur(n - 1); }); console.log(factorial(10));//3628800
上述就是小編為大家分享的JavaScript函數(shù)的特性有哪些了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。