成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

bind函數(shù)作用、應(yīng)用場(chǎng)景以及模擬實(shí)現(xiàn)

Eminjannn / 2921人閱讀

摘要:返回的新函數(shù)調(diào)用時(shí)也可以向原函數(shù)傳遞實(shí)參,這里就涉及順序問題。返回的新函數(shù)被當(dāng)成構(gòu)造函數(shù)調(diào)用函數(shù)后返回的新函數(shù),也可以被當(dāng)做構(gòu)造函數(shù)。使用,以原函數(shù)作為新對(duì)象的原型創(chuàng)建對(duì)象測(cè)試打印打印函數(shù)源碼已實(shí)現(xiàn)完成,希望對(duì)你有幫助。

bind函數(shù)

bind 函數(shù)掛在 Function 的原型上

Function.prototype.bind

創(chuàng)建的函數(shù)都可以直接調(diào)用 bind,使用:

    function func(){
        console.log(this)
    }
    func.bind(); // 用函數(shù)來調(diào)用

bind 的作用:

bind() 方法調(diào)用后會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)將作為新函數(shù)運(yùn)行時(shí)的 this的值,之后的序列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為新函數(shù)的參數(shù)。

bind 接收的參數(shù)

func.bind(thisArg[,arg1,arg2...argN])

第一個(gè)參數(shù)thisArg,當(dāng) func 函數(shù)被調(diào)用時(shí),該參數(shù)會(huì)作為 func 函數(shù)運(yùn)行時(shí)的 this 指向。當(dāng)使用 new 操作符調(diào)用綁定函數(shù)時(shí),該參數(shù)無效。

[,arg1,arg2...argN] 作為實(shí)參傳遞給 func 函數(shù)。

bind 返回值

返回一個(gè)新函數(shù)

注意:這和函數(shù)調(diào)用 call/apply 改變this指向有所不同。調(diào)用call/apply 會(huì)把原函數(shù)直接執(zhí)行了。

舉個(gè)例子說明:

function func(){
    console.log(this)
}

// 用call
func.call({a:1});  // func函數(shù)被執(zhí)行了,打?。簕a:1}

// 用bind
let newFunc = func.bind({});   // 返回新函數(shù)

newFunc(); // 只有當(dāng)返回的新函數(shù)執(zhí)行,func函數(shù)才會(huì)被執(zhí)行

從以上得到如下信息:

bind被函數(shù)調(diào)用

返回一個(gè)新函數(shù)

能改變函數(shù)this指向

可以傳入?yún)?shù)

深入bind 使用

以上知道了 bind 函數(shù)的作用以及使用方式,接下深入到 bind 函數(shù)的使用中,具體介紹三個(gè)方面的使用,這也是之后模擬實(shí)現(xiàn) bind 函數(shù)的要點(diǎn)。

改變函數(shù)運(yùn)行時(shí)this指向

傳遞參數(shù)

返回的新函數(shù)被當(dāng)成構(gòu)造函數(shù)

改變函數(shù)運(yùn)行時(shí)this指向

當(dāng)調(diào)用 bind 函數(shù)后,bind 函數(shù)的第一個(gè)參數(shù)就是原函數(shù)作用域中 this 指向的值。

function func(){
    console.log(this); 
}

let newFunc = func.bind({a:1});
newFunc(); // 打?。簕a:1}

let newFunc2 = func.bind([1,2,3]);
newFunc2(); // 打?。篬1,2,3]

let newFunc3 = func.bind(1);
newFunc3(); // 打印:Number:{1}

let newFunc4 = func.bind(undefined/null);
newFunc4(); // 打?。簑indow

以上要注意,當(dāng)傳入為 null 或者 undefined 時(shí),在非嚴(yán)格模式下,this 指向?yàn)?window。

當(dāng)傳入為簡(jiǎn)單值時(shí),內(nèi)部會(huì)將簡(jiǎn)單的值包裝成對(duì)應(yīng)類型的對(duì)象,數(shù)字就調(diào)用 Number 方法包裝;字符串就調(diào)用 String 方法包裝;true/false 就調(diào)用 Boolean 方法包裝。要想取到原始值,可以調(diào)用 valueOf 方法。

Number(1).valueOf(); // 1
String("hello").valueOf(); // hello
Boolean(true).valueOf(); // true

當(dāng)多次調(diào)用 bind 函數(shù)時(shí),以第一次調(diào)用 bind 函數(shù)的改變 this 指向的值為準(zhǔn)。

function func(){
    console.log(this);
}

let newFunc = func.bind({a:1}).bind(1).bind(["a","b","c"]);
newFunc(); // 打?。簕a: 1}
傳遞的參數(shù)

bind 的第二個(gè)參數(shù)開始,是向原函數(shù)傳遞的實(shí)參。bind 返回的新函數(shù)調(diào)用時(shí)也可以向原函數(shù)傳遞實(shí)參,這里就涉及順序問題。

function func(a,b,c){
    console.log(a,b,c); // 打印傳入的實(shí)參
}

let newFunc = func.bind({},1,2);

newFunc(3)

打印結(jié)果為1,2,3。
可以看到,在 bind 中傳遞的參數(shù)要先傳入到原函數(shù)中。

返回的新函數(shù)被當(dāng)成構(gòu)造函數(shù)

調(diào)用 bind 函數(shù)后返回的新函數(shù),也可以被當(dāng)做構(gòu)造函數(shù)。通過新函數(shù)創(chuàng)建的實(shí)例,可以找到原函數(shù)的原型上。

// 原函數(shù)
function func(name){
console.log(this); // 打?。和ㄟ^{name:"wy"}
this.name = name;
}
func.prototype.hello = function(){
    console.log(this.name)
}
let obj = {a:1}
// 調(diào)用bind,返回新函數(shù)
let newFunc = func.bind(obj);

// 把新函數(shù)作為構(gòu)造函數(shù),創(chuàng)建實(shí)例

let o = new newFunc("seven");

console.log(o.hello()); // 打印:"seven"
console.log(obj); // 打?。簕a:1}

新函數(shù)被當(dāng)成了構(gòu)造函數(shù),原函數(shù)func 中的 this 不再指向傳入給 bind 的第一個(gè)參數(shù),而是指向用 new 創(chuàng)建的實(shí)例。在通過實(shí)例 o 找原型上的方法 hello 時(shí),能夠找到原函數(shù) func 原型上的方法。

在模擬實(shí)現(xiàn) bind 特別要注意這一塊的實(shí)現(xiàn),這也是面試的重點(diǎn),會(huì)涉及到繼承。

bind函數(shù)應(yīng)用場(chǎng)景

以上只是說了 bind 函數(shù)時(shí)如何使用的,學(xué)會(huì)了使用,要把它放在業(yè)務(wù)場(chǎng)景中來解決一些現(xiàn)實(shí)問題。

場(chǎng)景一

先來一個(gè)布局:

  • 1
  • 1
  • 1

需求:點(diǎn)擊每一個(gè) li 元素,延遲1000ms后,改變 li 元素的顏色,

let lis = document.querySelectorAll("#list li");
for(var i = 0; i < lis.length; i++){
    lis[i].onclick = function(){
        setTimeout(function(){
            this.style.color = "red"
        },1000)
    }
}

以上代碼點(diǎn)擊每一個(gè) li,并不會(huì)改變顏色,因?yàn)槎〞r(shí)器回調(diào)函數(shù)的 this 指向的不是點(diǎn)擊的 li,而是window,(當(dāng)然你也可以使用箭頭函數(shù),let之類來解決,這里討論的主要是用bind來解決)。此時(shí)就需要改變回調(diào)函數(shù)的 this 指向。能改變函數(shù) this 指向的有:call、apply、bind。那么選擇哪一個(gè)呢?根據(jù)場(chǎng)景來定,這里的場(chǎng)景是在1000ms之后才執(zhí)行回調(diào)函數(shù),所以不能選擇使用call、apply,因?yàn)樗鼈儠?huì)立即執(zhí)行函數(shù),所以這個(gè)場(chǎng)景應(yīng)該選擇使用 bind解決。

setTimeout(function(){
    this.style.color = "red"
}.bind(this),1000)
場(chǎng)景二

有時(shí)會(huì)使用面向?qū)ο蟮姆绞絹斫M織代碼,涉及到把事件處理函數(shù)拆分在原型上,然后把這些掛在原型上的方法賦值給事件,此時(shí)的函數(shù)在事件觸發(fā)時(shí)this都指向了元素,進(jìn)而需要在函數(shù)中訪問實(shí)例上的屬性時(shí),便不能找到成。

function Modal(options){
    this.options = options;
}

Modal.prototype.init = function(){
    this.el.onclick = this.clickHandler; // 此方法掛載原型上
}
Modal.prototype.clickHandler = function(){
    console.log(this.left);  // 此時(shí)點(diǎn)擊元素執(zhí)行該函數(shù),this指向元素,不能找到left
}

let m = new Modal({
    el: document.querySelector("#list"),
    left: 300
})

m.init(); // 啟動(dòng)應(yīng)用

以上代碼,在 init 函數(shù)中,給元素綁定事件,事件處理函數(shù)掛在原型上,使用 this 來訪問。當(dāng)點(diǎn)擊元素時(shí),在 clickHandler 函數(shù)中需要拿到實(shí)例的 left 屬性,但此時(shí) clickHandler 函數(shù)中的 this 指向的是元素,而不是實(shí)例,所以拿不到。要改變 clickHandler 函數(shù) this 的指向,此時(shí)就需要用到 bind。

Modal.prototype.init = function(){
    this.el.onclick = this.clickHandler.bind(this)
}

以上場(chǎng)景只是 bind 使用的冰山一角,它本質(zhì)要做的事情是改變 this 的指向,達(dá)到預(yù)期目的。掌握了 bind 的作用以及應(yīng)用的場(chǎng)景,在腦海中就會(huì)樹立一個(gè)印象:當(dāng)需要改變this指向,并不立即執(zhí)行函數(shù)時(shí),就能想到 bind。

模擬實(shí)現(xiàn)

為什么要自己去實(shí)現(xiàn)一個(gè)bind函數(shù)呢?

bind()函數(shù)在 ECMA-262 第五版才被加入;它可能無法在所有瀏覽器上運(yùn)行(ie8以下)。
面試用,讓面試官找不到拒絕你的理由

抓住 bind 使用的幾個(gè)特征,把這些點(diǎn)一一實(shí)現(xiàn)就OK,具體的點(diǎn):

被函數(shù)調(diào)用

返回新函數(shù)

傳遞參數(shù)

改變函數(shù)運(yùn)行時(shí)this指向

新函數(shù)被當(dāng)做構(gòu)造函數(shù)時(shí)處理

被函數(shù)調(diào)用,可以直接掛在Function的原型上,為了補(bǔ)缺那些不支持的瀏覽器,不用再為支持的瀏覽器添加,可以做如下判斷:

if(!Function.prototype.bind) {
    Function.prototype.bind = function(){
        
    }
}

這種行為也叫作 polyfill,為不支持的瀏覽器添加某項(xiàng)功能,以達(dá)到抹平瀏覽器之間的差距。

注意:如果瀏覽器支持,方便自己測(cè)試,可以把 if 條件去掉,或者把 bind 改一個(gè)名字。在下文準(zhǔn)備改名字為 bind2,方便測(cè)試。

調(diào)用 bind 后會(huì)返回一個(gè)新的函數(shù),當(dāng)新函數(shù)被調(diào)用,原函數(shù)隨之也被調(diào)用。

Function.prototype.bind2 = function(thisArg,...args){
    let funcThis = this; // 函數(shù)調(diào)用bind,this指向原函數(shù)
    // 返回新函數(shù)
    return function (...rest) {
        return funcThis.apply(thisArg,[...args,...rest]/*bind2傳遞的實(shí)參優(yōu)先于新函數(shù)的實(shí)參*/)
    }
}

// 測(cè)試
function func(a,b,c){
    console.log(this)
    console.log(a,b,c)
}

let newFunc = func.bind2({a:1},1,2);

newFunc(3);
 // 打?。簕a: 1}
 // 打印:1 2 3

以上這個(gè)函數(shù)已經(jīng)能夠改變?cè)瘮?shù) this 的指向,并傳遞正確順序的參數(shù)。接下來就是比較難理解的地方,當(dāng)新函數(shù)被當(dāng)做構(gòu)造函數(shù)的情況。

需要作出兩個(gè)地方的改變:

新返回的函數(shù)要繼承原函數(shù)原型上的屬性

原函數(shù)改變this問題。如果用new調(diào)用,則原函數(shù)this指向應(yīng)該是新函數(shù)中this的值;否則為傳遞的thisArg的值。

先做繼承,讓新函數(shù)繼承原函數(shù)的原型,維持原來的原型關(guān)系。匿名函數(shù)沒辦法引用,所以給新函數(shù)起一個(gè)名字。

Function.prototype.bind2 = function(thisArg,...args){
    let funcThis = this; // 函數(shù)調(diào)用bind,this指向原函數(shù)
    
    // 要返回的新函數(shù)
    let fBound = function (...rest) {
        return funcThis.apply(thisArg,[...args,...rest]/*bind2傳遞的實(shí)參優(yōu)先于新函數(shù)的實(shí)參*/)
    }
    
    // 不是所有函數(shù)都有prototype屬性,比如 Function.prototype就沒有。
    if(funcThis.prototype){
        // 使用Object.create,以原函數(shù)prototype作為新對(duì)象的原型創(chuàng)建對(duì)象
        fBound.prototype = Object.create(funcThis.prototype);
    }
    return fBound;
}

// 測(cè)試
function func(name){
    console.log(this); // {a: 1}
    this.name = name;
}

func.prototype.hello = function(){
    console.log(this.name); // undefined
}

let newFunc = func.bind2({a:1});
let o = new newFunc("seven")

o.hello();
// 打?。簕a: 1}
// 打印:undefined

以上代碼,新建的實(shí)例 o 能夠調(diào)用到 hello 這個(gè)方法,說明繼承已經(jīng)實(shí)現(xiàn),能夠訪問新函數(shù)上原型方法。

接下來是關(guān)于 this 指向問題,上面例子中,使用了 new 運(yùn)算符調(diào)用函數(shù),那么原函數(shù)中,this 應(yīng)該指向?qū)嵗艑?duì)。所以需要在改變 this 指向的 apply 那里對(duì)是否是使用 new 操作符調(diào)用的做判斷。

用到的操作符是 instanceof,作用是判斷一個(gè)函數(shù)的原型是否在一個(gè)對(duì)象的原型鏈上,是的話返回true,否則返回false。測(cè)試如下:

function Person(){}
let p = new Person();
console.log(p instanceof Person); // true
console.log(p instanceof Object); // true
console.log(p instanceof Array); // fasle

也可以用 instanceof 在構(gòu)造函數(shù)中判斷是否是通過 new 來調(diào)用的。如果是用 new 來調(diào)用,說明函數(shù)中 this 對(duì)象的原型鏈上存在函數(shù)的原型,會(huì)返回true。

function Person(){
    console.log(this instanceof Person); // true
}

new Person();

回到我們的 bind2 函數(shù)上,當(dāng)調(diào)用 bind2 后返回了新函數(shù) fBound,當(dāng)使用 new 調(diào)用構(gòu)造函數(shù)時(shí),實(shí)際上調(diào)用的就是 fBound 這個(gè)函數(shù),所以只需要在 fBound 函數(shù)中利用 instanceof 來判斷是否是用 new 來調(diào)用即可。

Function.prototype.bind2 = function(thisArg,...args){
    let funcThis = this; // 函數(shù)調(diào)用bind,this指向原函數(shù)
    
    // 要返回的新函數(shù)
    let fBound = function (...rest) {
        // 如果是new調(diào)用的,原函數(shù)this指向新函數(shù)中創(chuàng)建的實(shí)例對(duì)象
        // 不是new調(diào)用,依然是調(diào)用bind2傳遞的第一個(gè)參數(shù)
        thisArg = this instanceof fBound ? this : thisArg;
        return funcThis.apply(thisArg,[...args,...rest]/*bind2傳遞的實(shí)參優(yōu)先于新函數(shù)的實(shí)參*/)
    }
    
    // 不是所有函數(shù)都有prototype屬性,比如 Function.prototype就沒有。
    if(funcThis.prototype){
        // 使用Object.create,以原函數(shù)prototype作為新對(duì)象的原型創(chuàng)建對(duì)象
        fBound.prototype = Object.create(funcThis.prototype);
    }
    return fBound;
}
// 測(cè)試
function func(name){
    console.log(this); // {a: 1}
    this.name = name;
}

func.prototype.hello = function(){
    console.log(this.name); // undefined
}


let newFunc = func.bind2({a:1});
let o = new newFunc("seven")

o.hello();
// 打?。簕name:"seven"}
// 打?。?seven"

bind 函數(shù)源碼已實(shí)現(xiàn)完成,希望對(duì)你有幫助。

如有偏差歡迎指正學(xué)習(xí),謝謝。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100481.html

相關(guān)文章

  • 【進(jìn)階3-4期】深度解析bind原理、使用場(chǎng)景模擬實(shí)現(xiàn)

    摘要:返回的綁定函數(shù)也能使用操作符創(chuàng)建對(duì)象這種行為就像把原函數(shù)當(dāng)成構(gòu)造器,提供的值被忽略,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)。 bind() bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù),當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),它的 this 值是傳遞給 bind() 的第一個(gè)參數(shù),傳入bind方法的第二個(gè)以及以后的參數(shù)加上綁定函數(shù)運(yùn)行時(shí)本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來調(diào)用原函數(shù)。bind返回的綁定函數(shù)也能使用 n...

    guyan0319 評(píng)論0 收藏0
  • js 閉包的使用技巧

    摘要:閉包的學(xué)術(shù)定義先來參考下各大權(quán)威對(duì)閉包的學(xué)術(shù)定義百科閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體。 前言 上一章講解了閉包的底層實(shí)現(xiàn)細(xì)節(jié),我想大家對(duì)閉包的概念應(yīng)該也有了個(gè)大概印象,但是真要用簡(jiǎn)短的幾句話來說清楚,這還真不是件容易的事。這里我們就來總結(jié)提煉下閉包的概念,以應(yīng)付那些非專人士的心血來潮。 閉包的學(xué)術(shù)...

    dendoink 評(píng)論0 收藏0
  • 22道JavaScript高頻手寫面試題

    JavaScript筆試部分 點(diǎn)擊關(guān)注本公眾號(hào)獲取文檔最新更新,并可以領(lǐng)取配套于本指南的 《前端面試手冊(cè)》 以及最標(biāo)準(zhǔn)的簡(jiǎn)歷模板. 實(shí)現(xiàn)防抖函數(shù)(debounce) 防抖函數(shù)原理:在事件被觸發(fā)n秒后再執(zhí)行回調(diào),如果在這n秒內(nèi)又被觸發(fā),則重新計(jì)時(shí)。 那么與節(jié)流函數(shù)的區(qū)別直接看這個(gè)動(dòng)畫實(shí)現(xiàn)即可。 showImg(https://segmentfault.com/img/remote/146000002...

    Alan 評(píng)論0 收藏0
  • 面試官問:JS的this指向

    摘要:之前寫過一篇文章面試官問能否模擬實(shí)現(xiàn)的和方法就是利用對(duì)象上的函數(shù)指向這個(gè)對(duì)象,來模擬實(shí)現(xiàn)和的。雖然實(shí)際使用時(shí)不會(huì)顯示返回,但面試官會(huì)問到。非嚴(yán)格模式下,和,指向全局對(duì)象 前言 面試官出很多考題,基本都會(huì)變著方式來考察this指向,看候選人對(duì)JS基礎(chǔ)知識(shí)是否扎實(shí)。讀者可以先拉到底部看總結(jié),再谷歌(或各技術(shù)平臺(tái))搜索幾篇類似文章,看筆者寫的文章和別人有什么不同(歡迎在評(píng)論區(qū)評(píng)論不同之處),...

    warnerwu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<