手動(dòng)實(shí)現(xiàn)Javascript中 apply晓锻,call奈偏,bind,new郁油,節(jié)流防抖函數(shù)本股,讓你一次性全搞懂。

一桐腌、寫在前面

趁著做畢設(shè)的空閑時(shí)間拄显,打算梳理一下 Javascript 中幾個(gè)比較重要的函數(shù),并手動(dòng)來實(shí)現(xiàn)一下案站」螅可以加深自己對(duì)這些函數(shù)的理解,從而更方便在平時(shí)項(xiàng)目中使用蟆盐,所謂知其然承边,亦知其所以然也。梳理了一下主要有以下幾個(gè)函數(shù)舱禽,往后會(huì)不斷更新炒刁。

  • 節(jié)流,防抖函數(shù)
  • call誊稚,apply 和 bind 方法
  • new方法

二翔始、節(jié)流防抖(throttle & debounce)

1)、什么是節(jié)流里伯、防抖城瞎。

函數(shù)的節(jié)流和防抖是前端對(duì)于前端代碼性能優(yōu)化的一個(gè)重要的組成部分,節(jié)流疾瓮,顧名思義脖镀,就是當(dāng)我們執(zhí)行一個(gè)函數(shù)多次時(shí),我們只讓它在一定時(shí)間段執(zhí)行一次狼电,達(dá)到節(jié)制的目的蜒灰。防抖就是一段時(shí)間內(nèi)一個(gè)函數(shù)多次執(zhí)行時(shí)弦蹂,我們讓它只執(zhí)行一次。這里引出別人的概念强窖。

函數(shù)防抖(debounce):
在事件被觸發(fā)n秒后再執(zhí)行回調(diào)凸椿,如果在這n秒內(nèi)又被觸發(fā),則重新計(jì)時(shí)翅溺;典型的案例就是輸入搜索:輸入結(jié)束后n秒才進(jìn)行搜索請(qǐng)求脑漫,n秒內(nèi)又輸入的內(nèi)容,就重新計(jì)時(shí)咙崎。

函數(shù)節(jié)流(throttle):
規(guī)定在一個(gè)單位時(shí)間內(nèi)优幸,只能觸發(fā)一次函數(shù),如果這個(gè)單位時(shí)間內(nèi)觸發(fā)多次函數(shù)褪猛,只有一次生效网杆; 典型的案例就是鼠標(biāo)不斷點(diǎn)擊觸發(fā),規(guī)定在n秒內(nèi)多次點(diǎn)擊只有一次生效握爷。

上面的概念其實(shí)也給了我們實(shí)現(xiàn)函數(shù)的思路跛璧,下面就來實(shí)現(xiàn)一下。

// 節(jié)流函數(shù)
function throttle(fn) {
    // 先設(shè)置標(biāo)識(shí)
    let flag = true
    return function () {
        // 觸發(fā)時(shí)間段還沒過去
        if (!flag) return
        // 改變狀態(tài)
        flag = false
        // 這里我沒有寫在setTimeout里新啼,因?yàn)槲蚁朦c(diǎn)擊立即觸發(fā)追城。
        fn.apply(this, arguments)
        setTimeout(() => {
            // 一秒后變?yōu)閠rue標(biāo)識(shí)該函數(shù)可以再次觸發(fā)了
            flag = true
        }, 1000)
    }
}

// 防抖函數(shù)
function debounce(fn) {
    // 開始定時(shí)器為null
    let time = null
    return function () {
        // 清除之前的 time,永遠(yuǎn)保留這段時(shí)間內(nèi)的最后一個(gè)執(zhí)行
        clearTimeout(time)
        time = setTimeout(() => {
            fn.apply(this, arguments)
        }, 500)
    }
}

三燥撞、call, apply, bind 方法

首先座柱,這三者都是用來調(diào)用函數(shù)的,我們 Javascript 中的函數(shù)除了可以直接調(diào)用外物舒,還可以用這三個(gè)函數(shù)來手動(dòng)調(diào)用色洞。那么問題就來了,為什么要手動(dòng)去調(diào)用呢冠胯?

其實(shí)當(dāng)我們調(diào)用一個(gè)函數(shù)時(shí)候火诸,是在全局對(duì)象window 上調(diào)用的,這是時(shí)候 this 自然就指向 window 荠察,所以調(diào)用結(jié)果是一樣的置蜀。關(guān)于 this指向的問題 后面會(huì)出一篇文章詳細(xì)講講。

// 瀏覽器中(因?yàn)樵趎ode中this默認(rèn)指向一個(gè)空對(duì)象)
let name = 'rinvay'
function getName() {
    console.log(this.name);
}
window.getName() // rinvay
this.getName() // rinvay

那想想當(dāng)我們需要在另一個(gè)對(duì)象而不是window對(duì)象上調(diào)用這個(gè)函數(shù)悉盆,是不是就沒辦法了盯荤,這個(gè)時(shí)候就需要我們的這三個(gè)函數(shù)出場(chǎng)了。下面來介紹這三個(gè)函數(shù)的基本用法焕盟。首先介紹 apllycall 的區(qū)別秋秤,這兩個(gè)函數(shù)都是立即調(diào)用函數(shù) 第一個(gè)參數(shù)就是函數(shù)this的指向,其余參數(shù)在函數(shù)執(zhí)行的時(shí)候傳給函數(shù)。兩個(gè)函數(shù)唯一的不同就是其余參數(shù)的傳遞形式灼卢,call個(gè)是一個(gè)個(gè)傳绍哎,apply是放在一個(gè)數(shù)組中

let person = {
    name: '樹街貓'
}
let name = 'rinvay'

function getInfo(age, height) {
    console.log(this.name, age, height);
}
getInfo(21, 178) // rinvay 21 178

// call(this,arg1,arg2,...) 函數(shù)
getInfo.call(person, 21, 178) // 樹街貓 21 178

// apply(this,[arg1,arg2,...]) 函數(shù)
getInfo.apply(person, [21, 178]) // 樹街貓 21 178

bind這個(gè)函數(shù)比較特殊,它不同于上面兩個(gè)芥玉。她改變了this指向之后不會(huì)立即執(zhí)行該函數(shù)而是返回一個(gè)新的綁定函數(shù)

let person = {
    name: '樹街貓'
}
let getInfoBind = getInfo.bind(person, 21, 178)
// 得到后執(zhí)行
getInfoBind() // 樹街貓 21 178

這樣三個(gè)函數(shù)的基本使用搞清楚了蛇摸,還有就是也可以不傳參,不改變this指向灿巧,就和直接調(diào)用函數(shù)的效果一樣。

搞清楚了用法揽涮,我們趁熱打鐵來實(shí)現(xiàn)一下抠藕。先寫步驟,再寫代碼蒋困。

1)call 和 apply

  1. 將函數(shù)的 this指向 指向 obj
  2. 傳入?yún)?shù)并執(zhí)行函數(shù)

實(shí)現(xiàn)代碼

// call 函數(shù)實(shí)現(xiàn)
Function.prototype._call = function (obj,...rest) {
    if (typeof this !== 'function') return
    // 這里this指向調(diào)用 _call 的函數(shù),也就是函數(shù)本身盾似。所以將函數(shù)賦值給對(duì)象的fn屬性。對(duì)象調(diào)用函數(shù)時(shí)候函數(shù)的this自然指向?qū)ο蟆?    // 這里用了Symbol.for API 大概就是在沒有fn的時(shí)候運(yùn)行Symbol.for('fn')會(huì)創(chuàng)建一個(gè)fn放入注冊(cè)表,有了之后會(huì)返回這個(gè)值雪标。
    obj[Symbol.for('fn')] = this;
    // 取到fn這個(gè)值,對(duì)象調(diào)用函數(shù)零院。
    obj[Symbol.for('fn')](...rest);
    // 這里需要?jiǎng)h除這個(gè)屬性,否則越來越多村刨。
    delete obj[Symbol.for('fn')]
}

// apply 函數(shù) 其實(shí)只是參數(shù)的處理變了而已,
Function.prototype._apply = function (obj) {
    if (typeof this !== 'function') return
    obj[Symbol.for('fn')] = this;
    obj[Symbol.for('fn')](...arguments[1]);
    delete obj[Symbol.for('fn')]
}

2)bind函數(shù)

下面來說說bind函數(shù)告抄,這個(gè)和上面的區(qū)別就是它在綁定后不執(zhí)行,返回一個(gè)綁定函數(shù)

  1. 將函數(shù)的 this指向 指向 obj
  2. 傳入?yún)?shù)并返回函數(shù)

但是這里bind有點(diǎn)不一樣嵌牺,它支持函數(shù)柯里化打洼,就是如果有兩個(gè)參數(shù),可以先傳一個(gè)參數(shù)逆粹,執(zhí)行返回函數(shù)的時(shí)候再傳入這里需要除了剩余參數(shù) 募疮。

Function.prototype._bind = function (obj) {
    if (typeof this !== 'function') {
        return;
    }
    var _this = this;
    //從第二個(gè)參數(shù)截取
    var args = Array.prototype.slice.call(arguments, 1)
    return function () {
        // 剩余參數(shù)的處理合并再傳入
        return _this.apply(obj, args.concat(Array.prototype.slice.call(arguments)));
    }
};

這里三個(gè)函數(shù)都實(shí)現(xiàn)完畢了,當(dāng)然可能有點(diǎn)小瑕疵僻弹,希望發(fā)現(xiàn)問題的評(píng)論區(qū)留言阿浓。

四、new方法

平時(shí)我們都是怎么用的 new 呢蹋绽,無非就是new 一個(gè)類嘛芭毙,我們 Javascript 叫做構(gòu)造函數(shù),其實(shí)就是普通函數(shù)蟋字。我們ES中的calss關(guān)鍵字其實(shí)只是語法糖稿蹲,實(shí)質(zhì)還是一個(gè)函數(shù),可以打印類型看一看鹊奖。下面演示一下

function Person(name, age) {
    this.name = name
    this.age = age
}

let p1 = new Person('rinvay', 21)
console.log(p1.name); // rinvay

感覺它做的事情就是創(chuàng)建了一個(gè)新的對(duì)象把Person里面的屬性給了這個(gè)對(duì)象苛聘,再返回。哈哈哈,這只是感覺设哗,其實(shí)過程是這樣的唱捣。

  1. 創(chuàng)建一個(gè)空對(duì)象,將它的引用賦給 this网梢,繼承函數(shù)的原型震缭。
  2. 通過 this 將屬性和方法添加至這個(gè)對(duì)象
  3. 最后返回 this 指向的新對(duì)象,也就是實(shí)例(如果沒有手動(dòng)返回其他的對(duì)象)

這里只描述了如何將構(gòu)造器中的屬性方法給實(shí)例战虏,那么原型上的實(shí)例呢拣宰?這里我們就用以構(gòu)造器原型去完成創(chuàng)建第一步那個(gè)空對(duì)象,下面代碼實(shí)現(xiàn)一下烦感。這里給出實(shí)現(xiàn)步驟

  1. 以構(gòu)造器的prototype屬性為原型巡社,創(chuàng)建新對(duì)象;
  2. 將this(也就是上一句中的新對(duì)象)和調(diào)用參數(shù)傳給構(gòu)造器手趣,執(zhí)行晌该;
  3. 如果構(gòu)造器沒有手動(dòng)返回對(duì)象,則返回第一步創(chuàng)建的新對(duì)象绿渣,如果有朝群,則舍棄掉第一步創(chuàng)建的新對(duì)象,返回手動(dòng)return的對(duì)象中符。
function _new(constructor, ...rest) {
    let obj = Object.create(constructor.prototype)
    let res = constructor.apply(obj, rest)
    return typeof res === 'object' ? res : obj
}

好了這里常用的api函數(shù)就差不多實(shí)現(xiàn)完成了姜胖,凌晨了也該睡覺了。

喜歡的話點(diǎn)個(gè)關(guān)注吧舟茶,你的點(diǎn)贊關(guān)注是我最大的動(dòng)力谭期。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市吧凉,隨后出現(xiàn)的幾起案子隧出,更是在濱河造成了極大的恐慌,老刑警劉巖阀捅,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胀瞪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡饲鄙,警方通過查閱死者的電腦和手機(jī)凄诞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忍级,“玉大人帆谍,你說我怎么就攤上這事≈嵩郏” “怎么了汛蝙?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵烈涮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我窖剑,道長(zhǎng)坚洽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任西土,我火速辦了婚禮讶舰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘需了。我一直安慰自己跳昼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布肋乍。 她就那樣靜靜地躺著庐舟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪住拭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天历帚,我揣著相機(jī)與錄音滔岳,去河邊找鬼。 笑死挽牢,一個(gè)胖子當(dāng)著我的面吹牛谱煤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播禽拔,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼刘离,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了睹栖?” 一聲冷哼從身側(cè)響起硫惕,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎野来,沒想到半個(gè)月后恼除,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曼氛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年豁辉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舀患。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徽级,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聊浅,到底是詐尸還是另有隱情餐抢,我是刑警寧澤现使,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站弹澎,受9級(jí)特大地震影響朴下,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苦蒿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一殴胧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧佩迟,春花似錦团滥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秉溉,卻和暖如春力惯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背召嘶。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工父晶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人弄跌。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓甲喝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铛只。 傳聞我的和親對(duì)象是個(gè)殘疾皇子埠胖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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