談?wù)凧S中的高級(jí)函數(shù)

博客原文地址:Claiyre的個(gè)人博客
如需轉(zhuǎn)載,請(qǐng)?jiān)谖恼麻_頭注明原文地址

在JavaScript中,函數(shù)的功能十分強(qiáng)大蜓堕。它們是第一類對(duì)象,也可以作為另一個(gè)對(duì)象的方法,還可以作為參數(shù)傳入另一個(gè)函數(shù)尝盼,不僅如此吞滞,還能被一個(gè)函數(shù)返回!可以說(shuō)盾沫,在JS中裁赠,函數(shù)無(wú)處不在,無(wú)所不能赴精,堪比孫猴子呀佩捞!當(dāng)你運(yùn)用好函數(shù)時(shí),它能助你取西經(jīng)蕾哟,讓代碼變得優(yōu)雅簡(jiǎn)潔一忱,運(yùn)用不好時(shí),那就遭殃了谭确,要大鬧天宮咯~
除了函數(shù)相關(guān)的基礎(chǔ)知識(shí)外帘营,掌握一些高級(jí)函數(shù)并應(yīng)用起來(lái),不僅能讓JS代碼看起來(lái)更為精簡(jiǎn)逐哈,還可以提升性能芬迄。以下是博主總結(jié)的一些常用的、重要的高級(jí)函數(shù)昂秃,加上了一些個(gè)人見(jiàn)解禀梳,特此記錄下來(lái)。如果您是JS初學(xué)者肠骆,也不要被“高級(jí)”兩個(gè)字嚇到算途,因?yàn)槲闹写┎逯v解了一些原型、this等基礎(chǔ)知識(shí)蚀腿,相信并不難理解郊艘。如果您是JS大牛,也可以把本文用來(lái)查漏補(bǔ)缺。

正文

作用域安全的構(gòu)造函數(shù)

function Person(name,age){
    this.name = name;
    this.age = age;
}
var p1 = new Person("Claiyre",80);

相信您對(duì)上面的構(gòu)造函數(shù)一定不陌生纱注,但是畏浆,,如果某個(gè)粗心的程序猿調(diào)用這個(gè)構(gòu)造函數(shù)時(shí)忘記加new了會(huì)發(fā)生什么狞贱?

var p3 = Person("Tom",30);
console.log(p3);              //undefined
console.log(window.name);     //Tom

由于使用了不安全的構(gòu)造函數(shù)刻获,上面的代碼意外的改變了window的name,因?yàn)?code>this對(duì)象是在運(yùn)行時(shí)綁定的瞎嬉,使用new調(diào)用構(gòu)造函數(shù)時(shí)this是指向新創(chuàng)建的對(duì)象的蝎毡,不使用new時(shí),this是指向window的氧枣。
由于window的name屬性是用來(lái)識(shí)別鏈接目標(biāo)和frame的沐兵,所在這里對(duì)該屬性的偶然覆蓋可能導(dǎo)致其他錯(cuò)誤。

作用域安全的構(gòu)造函數(shù)會(huì)首先確認(rèn)this對(duì)象是正確類型的實(shí)例便监,然后再進(jìn)行更改扎谎,如下:

function Person(name,age){
    if(this instanceof Person){
        this.name = name;
        this.age = age;
    } else {
        return new Person(name,age);
    }   
}

這樣就避免了在全局對(duì)象上意外更改或設(shè)置屬性。
實(shí)現(xiàn)這個(gè)安全模式烧董,相當(dāng)于鎖定了調(diào)用構(gòu)造函數(shù)的環(huán)境毁靶,因此借用構(gòu)造函數(shù)繼承模式可能會(huì)出現(xiàn)問(wèn)題,解決方法是組合使用原型鏈和構(gòu)造函數(shù)模式逊移,即組合繼承预吆。
如果您是一個(gè)JS庫(kù)或框架的開發(fā)者,相信作用域安全的構(gòu)造函數(shù)一定對(duì)您非常有用胳泉。在多人協(xié)作的項(xiàng)目中拐叉,為了避免他們誤改了全局對(duì)象,也應(yīng)使用作用域安全的構(gòu)造函數(shù)扇商。

惰性載入函數(shù)

由于瀏覽器間的行為差異巷嚣,代碼中可能會(huì)有許多檢測(cè)瀏覽器行為的if語(yǔ)句。但用戶的瀏覽器若支持某一特性钳吟,便會(huì)一直支持廷粒,所以這些if語(yǔ)句,只用被執(zhí)行一次红且,即便只有一個(gè)if語(yǔ)句的代碼坝茎,也比沒(méi)有要快。
惰性載入表示函數(shù)執(zhí)行的分支僅會(huì)執(zhí)行一次暇番,有兩種實(shí)現(xiàn)惰性載入的方式嗤放,第一種就是在函數(shù)第一次被調(diào)用時(shí)再處理函數(shù),用檢測(cè)到的結(jié)果重寫原函數(shù)壁酬。

function detection(){
    if(//支持某特性){
        detection = function(){
            //直接用支持的特性
        }
    } else if(//支持第二種特性){
        detection = function(){
            //用第二種特性
        }
    } else {
        detection = function(){
            //用其他解決方案
        }
    }
}

第二種實(shí)現(xiàn)惰性載入的方式是在聲明函數(shù)時(shí)就指定適當(dāng)?shù)暮瘮?shù)

var detection = (function(){
    if(//支持某特性){
        return function(){
            //直接用支持的特性
        }
    } else if(//支持第二種特性){
        return function(){
            //用第二種特性
        }
    } else {
        return function(){
            //用其他解決方案
        }
    } 
})();

惰性載入函數(shù)的有點(diǎn)是在只初次執(zhí)行時(shí)犧牲一點(diǎn)性能次酌,之后便不會(huì)再有多余的消耗性能恨课。

函數(shù)綁定作用域

在JS中,函數(shù)的作用域是在函數(shù)被調(diào)用時(shí)動(dòng)態(tài)綁定的岳服,也就是說(shuō)函數(shù)的this對(duì)象的指向是不定的剂公,但在一些情況下,我們需要讓某一函數(shù)的執(zhí)行作用域固定吊宋,總是指向某一對(duì)象纲辽。這時(shí)怎么辦呢?
當(dāng)當(dāng)當(dāng)~~可以用函數(shù)綁定作用域函數(shù)呀

function bind(fn,context){
    return function(){
        return fn.apply(context,arguments);
    }
}

用法:

var person1 = {
    name: "claiyre",
    sayName: function(){
        alert(this.name);
    }
}
var sayPerson1Name = bind(person1.sayName,person1);
sayPerson1Name();  //claiyre

call函數(shù)和apply函數(shù)可以臨時(shí)改變函數(shù)的作用域璃搜,使用bind函數(shù)可以得到一個(gè)綁定了作用域的函數(shù)

函數(shù)柯里化(curry)

curry的概念很簡(jiǎn)單:只傳遞部分參數(shù)來(lái)調(diào)用函數(shù)拖吼,然后讓函數(shù)返回另一個(gè)函數(shù)去處理剩下的參數(shù)≌馕牵可以理解為賦予了函數(shù)“加載”的能力吊档。
許多js庫(kù)中都封裝了curry函數(shù),具體使用可以這樣唾糯。

var match = curry(function(what,str){
    return str.match(what)
}); 

var hasNumber = match(/[0-9]+/g);
var hasSpace = match(/\s+/g)

hasNumber("123asd");       //['123']
hasNumber("hello world!");  //null

hasSpace("hello world!");  //[' '];
hasSpace("hello");         //null

console.log(match(/\s+/g,'i am  Claiyre'));  //直接全部傳參也可: [' ','  ']

一旦函數(shù)經(jīng)過(guò)柯里化怠硼,我們就可以先傳遞部分參數(shù)調(diào)用它,然后得到一個(gè)更具體的函數(shù)趾断。這個(gè)更具體的函數(shù)通過(guò)閉包幫我們記住了第一次傳遞的參數(shù),最后我們就可以用這個(gè)更具體的函數(shù)為所欲為啦~

一個(gè)較為簡(jiǎn)單的實(shí)現(xiàn)curry的方式:

function curry(fn){
    var i = 0;
    var outer = Array.prototype.slice.call(arguments,1);
    var len = fn.length;
    return function(){
        var inner = outer.concat(Array.prototype.slice.call(arguments));
        return inner.length === len?fn.apply(null,inner):function (){
                var finalArgs = inner.concat(Array.prototype.slice.call(arguments));
                return fn.apply(null,finalArgs);
            }
    }
}

debounce函數(shù)

debounce函數(shù)吩愧,又稱“去抖函數(shù)”芋酌。它的功能也很簡(jiǎn)單直接,就是防止某一函數(shù)被連續(xù)調(diào)用雁佳,從而導(dǎo)致瀏覽器卡死或崩潰脐帝。用法如下:

var myFunc = debounce(function(){
    //繁重、耗性能的操作
}糖权,250);
window.addEventListener('resize',myFunc);

像窗口的resize堵腹,這類可以以較高的速率觸發(fā)的事件,非常適合用去抖函數(shù)星澳,這時(shí)也可稱作“函數(shù)節(jié)流”疚顷,避免給瀏覽器帶來(lái)過(guò)大的性能負(fù)擔(dān)。
具體的實(shí)現(xiàn)時(shí)禁偎,當(dāng)函數(shù)被調(diào)用時(shí)腿堤,不立即執(zhí)行相應(yīng)的語(yǔ)句,而是等待固定的時(shí)間w,若在w時(shí)間內(nèi)如暖,即等待還未結(jié)束時(shí)笆檀,函數(shù)又被調(diào)用了一次,則再等待w時(shí)間盒至,重復(fù)上述過(guò)程酗洒,直到最后一次被調(diào)用后的w時(shí)間內(nèi)該函數(shù)都沒(méi)有被再調(diào)用士修,則執(zhí)行相應(yīng)的代碼。
實(shí)現(xiàn)代碼如下:

function debounce(fn,wait){
    var td;
    return function(){
        clearTimeout(td);
        td= setTimeout(fn,wait);
    }
}

once函數(shù)

顧名思義樱衷,once函數(shù)是僅僅會(huì)被執(zhí)行一次的函數(shù)棋嘲。具體實(shí)現(xiàn)如下:

function once(fn){
    var result;
    return function(){
        if(fn){
            result = fn(arguments);
            fn = null;
        }
        return result;
    }
}

var init = once(function(){
    //初始化操作
})

在被執(zhí)行過(guò)一次后,參數(shù)fn就被賦值null了,那么在接下來(lái)被調(diào)用時(shí)箫老,便再也不會(huì)進(jìn)入到if語(yǔ)句中了封字,也就是第一次被調(diào)用后,該函數(shù)永遠(yuǎn)不會(huì)被執(zhí)行了耍鬓。

還可以對(duì)上述once函數(shù)進(jìn)行改進(jìn)阔籽,不僅可以傳入函數(shù),同時(shí)還可以給傳入的函數(shù)綁定作用域u牲蜀,同時(shí)實(shí)現(xiàn)了bind和once笆制。

function once(fn,context){
    var result;
    return function(){
        if(fn){
            result = fn.apply(context,arguments);
            fn = null;
        }
        return result;
    }
}

結(jié)語(yǔ)

通過(guò)以上的閱讀,不難發(fā)現(xiàn)很多“高級(jí)函數(shù)”的實(shí)現(xiàn)其實(shí)并不復(fù)雜涣达,數(shù)十行代碼便可搞定在辆,但重要的是能真正理解它們的原理,在實(shí)際中適時(shí)地應(yīng)用度苔,以此性能提升匆篓,讓代碼簡(jiǎn)潔,邏輯清晰

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末寇窑,一起剝皮案震驚了整個(gè)濱河市鸦概,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌甩骏,老刑警劉巖窗市,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異饮笛,居然都是意外死亡咨察,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門福青,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摄狱,“玉大人,你說(shuō)我怎么就攤上這事无午《叮” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵指厌,是天一觀的道長(zhǎng)刊愚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)踩验,這世上最難降的妖魔是什么鸥诽? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任商玫,我火速辦了婚禮,結(jié)果婚禮上牡借,老公的妹妹穿的比我還像新娘拳昌。我一直安慰自己,他們只是感情好钠龙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布炬藤。 她就那樣靜靜地躺著,像睡著了一般碴里。 火紅的嫁衣襯著肌膚如雪沈矿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天咬腋,我揣著相機(jī)與錄音羹膳,去河邊找鬼。 笑死根竿,一個(gè)胖子當(dāng)著我的面吹牛陵像,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播寇壳,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼醒颖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了壳炎?” 一聲冷哼從身側(cè)響起泞歉,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冕广,沒(méi)想到半個(gè)月后疏日,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偿洁,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡撒汉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了涕滋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片睬辐。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宾肺,靈堂內(nèi)的尸體忽然破棺而出溯饵,到底是詐尸還是另有隱情,我是刑警寧澤锨用,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布丰刊,位于F島的核電站,受9級(jí)特大地震影響增拥,放射性物質(zhì)發(fā)生泄漏啄巧。R本人自食惡果不足惜寻歧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望秩仆。 院中可真熱鬧码泛,春花似錦、人聲如沸澄耍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)齐莲。三九已至痢站,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铅搓,已是汗流浹背瑟押。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留星掰,地道東北人多望。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像氢烘,于是被迫代替她去往敵國(guó)和親怀偷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 函數(shù)和對(duì)象 1播玖、函數(shù) 1.1 函數(shù)概述 函數(shù)對(duì)于任何一門語(yǔ)言來(lái)說(shuō)都是核心的概念椎工。通過(guò)函數(shù)可以封裝任意多條語(yǔ)句,而且...
    道無(wú)虛閱讀 4,556評(píng)論 0 5
  • 繼承 一蜀踏、混入式繼承 二维蒙、原型繼承 利用原型中的成員可以被和其相關(guān)的對(duì)象共享這一特性,可以實(shí)現(xiàn)繼承果覆,這種實(shí)現(xiàn)繼承的...
    magic_pill閱讀 1,062評(píng)論 0 3
  • 1,javascript 基礎(chǔ)知識(shí) Array對(duì)象 Array對(duì)象屬性 Arrray對(duì)象方法 Date對(duì)象 Dat...
    Yuann閱讀 903評(píng)論 0 1
  • 今天有點(diǎn)熱颅痊,我來(lái)到留和路288號(hào)浙江工業(yè)大學(xué)的廣知樓考優(yōu)爾杯。一下車局待,一片金燦燦的向日葵撲入眼簾斑响。 叮鈴鈴,終于挨...
    抽風(fēng)一刻閱讀 824評(píng)論 0 2
  • 時(shí)間如流水,他走了好久了钳榨。 很多人離我而去,如滾滾黃河去而不返舰罚。直到今天,才明白。 ...
    冰雨_c4b0閱讀 148評(píng)論 0 0