自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

javascript原生一步步實(shí)現(xiàn)bind分析

開(kāi)發(fā) 前端
bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內(nèi)置的call屬性)。當(dāng)目標(biāo)函數(shù)被調(diào)用時(shí) this 值綁定到 bind() 的第一個(gè)參數(shù),該參數(shù)不能被重寫(xiě)。綁定函數(shù)被調(diào)用時(shí),bind() 也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。

[[175195]]

bind

官方描述

bind() 函數(shù)會(huì)創(chuàng)建一個(gè)新函數(shù)(稱為綁定函數(shù)),新函數(shù)與被調(diào)函數(shù)(綁定函數(shù)的目標(biāo)函數(shù))具有相同的函數(shù)體(在 ECMAScript 5 規(guī)范中內(nèi)置的call屬性)。當(dāng)目標(biāo)函數(shù)被調(diào)用時(shí) this 值綁定到 bind() 的***個(gè)參數(shù),該參數(shù)不能被重寫(xiě)。綁定函數(shù)被調(diào)用時(shí),bind() 也接受預(yù)設(shè)的參數(shù)提供給原函數(shù)。一個(gè)綁定函數(shù)也能使用new操作符創(chuàng)建對(duì)象:這種行為就像把原函數(shù)當(dāng)成構(gòu)造器。提供的 this 值被忽略,同時(shí)調(diào)用時(shí)的參數(shù)被提供給模擬函數(shù)。

使用介紹

由于javascript中作用域是由其運(yùn)行時(shí)候所處的環(huán)境決定的,所以往往函數(shù)定義和實(shí)際運(yùn)行的時(shí)候所處環(huán)境不一樣,那么作用域也會(huì)發(fā)生相應(yīng)的變化。

例如下面這個(gè)情況:

  1. var id = 'window'
  2. //定義一個(gè)函數(shù),但是不立即執(zhí)行 
  3. var test = function(){ 
  4.     console.log(this.id) 
  5. test() // window 
  6. //把test作為參數(shù)傳遞 
  7. var obj = { 
  8.     id:'obj'
  9.     hehe:test 
  10. //此時(shí)test函數(shù)運(yùn)行環(huán)境發(fā)生了改變 
  11. obj.hehe() // 'obj' 
  12. //為了避免這種情況,javascript里面有一個(gè)bind方法可以在函數(shù)運(yùn)行之前就綁定其作用域,修改如下 
  13.  
  14. var id = 'window'
  15. var test = function(){ 
  16.     console.log(this.id) 
  17. }.bind(window) 
  18. var obj = { 
  19.     id:'obj'
  20.     hehe:test 
  21. test() // window 
  22. obj.hehe() // window  

上面介紹了bind方法的一個(gè)重要作用就是為一個(gè)函數(shù)綁定作用域,但是bind方法在低版本瀏覽器不兼容,這里我們可以手動(dòng)實(shí)現(xiàn)一下。

拆分一下關(guān)鍵思路

  1. 因?yàn)閎ind方法不會(huì)立即執(zhí)行函數(shù),需要返回一個(gè)待執(zhí)行的函數(shù)(這里用到閉包,可以返回一個(gè)函數(shù))return function(){}
  2. 作用域綁定,這里可以使用apply或者call方法來(lái)實(shí)現(xiàn) xx.call(yy)/xx.apply(yy)
  3. 參數(shù)傳遞,由于參數(shù)的不確定性,需要用apply傳遞數(shù)組(實(shí)例更明了)xx.apply(yy,[...Array...]),如果用call就不太方便了,因?yàn)閏all后面的參數(shù)需要一個(gè)個(gè)列出來(lái)

實(shí)現(xiàn)

有了上述的思路,大致的雛形已經(jīng)明了了,代碼應(yīng)該也很容易實(shí)現(xiàn)

綁定作用域,綁定傳參

  1. Function.prototype.testBind = function(that){ 
  2.     var _this = this, 
  3.         /* 
  4.         *由于參數(shù)的不確定性,統(tǒng)一用arguments來(lái)處理,這里的arguments只是一個(gè)類(lèi)數(shù)組對(duì)象,有l(wèi)ength屬性 
  5.         *可以用數(shù)組的slice方法轉(zhuǎn)化成標(biāo)準(zhǔn)格式數(shù)組,除了作用域?qū)ο髏hat以外, 
  6.         *后面的所有參數(shù)都需要作為數(shù)組參數(shù)傳遞 
  7.         *Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1) 
  8.         */ 
  9.         slice = Array.prototype.slice, 
  10.         args = slice.apply(arguments,[1]); 
  11.     //返回函數(shù)     
  12.     return function(){ 
  13.         //apply綁定作用域,進(jìn)行參數(shù)傳遞 
  14.         return _this.apply(that,args) 
  15.     }     
  16.  

測(cè)試

  1. var test = function(a,b){ 
  2.     console.log('作用域綁定 '+ this.value) 
  3.     console.log('testBind參數(shù)傳遞 '+ a.value2) 
  4.     console.log('調(diào)用參數(shù)傳遞 ' + b) 
  5. var obj = { 
  6.     value:'ok' 
  7. var fun_new = test.testBind(obj,{value2:'also ok'}) 
  8.  
  9. fun_new ('hello bind'
  10. // 作用域綁定 ok 
  11. // testBind參數(shù)傳遞 also ok 
  12. // 調(diào)用參數(shù)傳遞  undefined  

動(dòng)態(tài)參數(shù)

上面已經(jīng)實(shí)現(xiàn)了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個(gè)函數(shù),調(diào)用的時(shí)候應(yīng)該支持傳遞參數(shù),很顯然,上面的 fun_new 調(diào)用的時(shí)候并不支持傳參,只能在 testBind 綁定的時(shí)候傳遞參數(shù),因?yàn)槲覀冏罱K調(diào)用的是這個(gè)返回函數(shù)

  1. function(){ 
  2.         return _this.apply(that,args) 
  3.     }     
  4.  
  5. 這里面的args在綁定的時(shí)候就已經(jīng)確定了,調(diào)用的時(shí)候值已經(jīng)固定, 
  6. 我們并沒(méi)有處理這個(gè)function傳遞的參數(shù)。  

我們對(duì)其進(jìn)行改造

  1. return function(){ 
  2.         return _this.apply(that, 
  3.             args.concat(Array.prototype.slice.apply(arguments,[0])) 
  4.         ) 
  5.     }      

這里的 Array.prototype.slice.apply(arguments,[0]) 指的是這個(gè)返回函數(shù)執(zhí)行的時(shí)候傳遞的一系列參數(shù),所以是從***個(gè)參數(shù)開(kāi)始 [0] ,之前的args = slice.apply(arguments,[1])指的是 testBind方法執(zhí)行時(shí)候傳遞的參數(shù),所以從第二個(gè)開(kāi)始 [1],兩則有本質(zhì)區(qū)別,不能搞混,只有兩者合并了之后才是返回函數(shù)的完整參數(shù)

所以有如下實(shí)現(xiàn)

  1. Function.prototype.testBind = function(that){ 
  2.     var _this = this, 
  3.         slice = Array.prototype.slice, 
  4.         args = slice.apply(arguments,[1]); 
  5.     return function(){ 
  6.         return _this.apply(that, 
  7.                     args.concat(Array.prototype.slice.apply(arguments,[0])) 
  8.                 ) 
  9.     }     
  10.  

測(cè)試

  1. var test = function(a,b){ 
  2.     console.log('作用域綁定 '+ this.value) 
  3.     console.log('testBind參數(shù)傳遞 '+ a.value2) 
  4.     console.log('調(diào)用參數(shù)傳遞 ' + b) 
  5. var obj = { 
  6.     value:'ok' 
  7. var fun_new = test.testBind(obj,{value2:'also ok'}) 
  8.  
  9. fun_new ('hello bind'
  10. // 作用域綁定 ok 
  11. // testBind參數(shù)傳遞 also ok 
  12. // 調(diào)用參數(shù)傳遞  hello bind  

在以上2種傳參方式中,bind的優(yōu)先級(jí)高,從 args.concat(Array.prototype.slice.apply(arguments,[0])) 也可以看出來(lái),bind的參數(shù)在數(shù)組前面。

原型鏈

官方文檔上有一句話:

A bound function may also be constructed using the new operator: doing

so acts as though the target function had instead been constructed.

The provided this value is ignored, while prepended arguments are

provided to the emulated function.

說(shuō)明綁定過(guò)后的函數(shù)被new實(shí)例化之后,需要繼承原函數(shù)的原型鏈方法,且綁定過(guò)程中提供的this被忽略(繼承原函數(shù)的this對(duì)象),但是參數(shù)還是會(huì)使用。

這里就需要一個(gè)中轉(zhuǎn)函數(shù)把原型鏈傳遞下去

  1. fNOP = function () {} //創(chuàng)建一個(gè)中轉(zhuǎn)函數(shù) 
  2. fNOP.prototype = this.prototype; 
  3. xx.prototype = new fNOP()  
  4. 修改如下 
  5. Function.prototype.testBind = function(that){ 
  6.     var _this = this, 
  7.         slice = Array.prototype.slice, 
  8.         args = slice.apply(arguments,[1]), 
  9.         fNOP = function () {}, 
  10.         //所以調(diào)用官方bind方法之后 有一個(gè)name屬性值為 'bound ' 
  11.         bound = function(){ 
  12.             return _this.apply(that, 
  13.                 args.concat(Array.prototype.slice.apply(arguments,[0])) 
  14.             ) 
  15.         }     
  16.  
  17.     fNOP.prototype = _this.prototype; 
  18.  
  19.     bound.prototype = new fNOP(); 
  20.  
  21.     return bound; 
  22.  

而且bind方法的***個(gè)參數(shù)this是可以不傳的,需要分2種情況

  • 直接調(diào)用bind之后的方法
  1. var f = function () { console.log('不傳默認(rèn)為'+this)  };f.bind()() 
  2. // 不傳默認(rèn)為 Window   

所以直接調(diào)用綁定方法時(shí)候 apply(that, 建議改為 apply(that||window,,其實(shí)不改也可以,因?yàn)椴粋髂J(rèn)指向window

  • 使用new實(shí)例化被綁定的方法

容易糊涂,重點(diǎn)在于弄清楚標(biāo)準(zhǔn)的bind方法在new的時(shí)候做的事情,然后就可以清晰的實(shí)現(xiàn)

這里我們需要看看 new 這個(gè)方法做了哪些操作 比如說(shuō) var a = new b()

  1. 創(chuàng)建一個(gè)空對(duì)象 a = {},并且this變量引用指向到這個(gè)空對(duì)象a
  2. 繼承被實(shí)例化函數(shù)的原型 :a.__proto__ = b.prototype
  3. 被實(shí)例化方法b的this對(duì)象的屬性和方法將被加入到這個(gè)新的 this 引用的對(duì)象中: b的屬性和方法被加入的 a里面
  4. 新創(chuàng)建的對(duì)象由 this 所引用 :b.call(a)

通過(guò)以上可以得知,如果是var after_new = new bindFun(); 由于這種行為是把原函數(shù)當(dāng)成構(gòu)造器,那么那么最終實(shí)例化之后的對(duì)象this需要繼承自原函數(shù), 而這里的 bindFun 目前是

  1. function(){ 
  2.             return _this.apply(that || window, 
  3.                 args.concat(Array.prototype.slice.apply(arguments,[0])) 
  4.             ) 
  5.         }      

這里apply的作用域是綁定的that || window,在執(zhí)行 testBind()的時(shí)候就已經(jīng)固定,并沒(méi)有把原函數(shù)的this對(duì)象繼承過(guò)來(lái),不符合我們的要求,我們需要根據(jù)apply的特性解決這個(gè)問(wèn)題:

在一個(gè)子構(gòu)造函數(shù)中,你可以通過(guò)調(diào)用父構(gòu)造函數(shù)的 `apply/call` 方法來(lái)實(shí)現(xiàn)繼承

例如

  1. function Product(name, price) { 
  2.   this.name = name
  3.   this.price = price; 
  4.  
  5.   if (price < 0) { 
  6.     throw RangeError('Cannot create product ' + 
  7.                       this.name + ' with a negative price'); 
  8.   } 
  9.  
  10. function Food(name, price) { 
  11.   Product.call(this, name, price);  
  12.   this.category = 'food'
  13.  
  14. //等同于(其實(shí)就是把Product放在Food內(nèi)部執(zhí)行了一次) 
  15. function Food(name, price) {  
  16.     this.name = name
  17.     this.price = price; 
  18.     if (price < 0) { 
  19.         throw RangeError('Cannot create product ' + 
  20.                 this.name + ' with a negative price'); 
  21.     } 
  22.  
  23.     this.category = 'food';  
  24.  

所以在new新的實(shí)例的時(shí)候?qū)崟r(shí)將這個(gè)新的this對(duì)象 進(jìn)行 apply 繼承原函數(shù)的 this 對(duì)象,就可以達(dá)到 new 方法里面的第 3 步的結(jié)果

  1. apply(that||window, 
  2. //修改為 如果是new的情況,需要綁定new之后的作用域,this指向新的實(shí)例對(duì)象 
  3. apply(isNew ? this : that||window,  ==> 
  4.  
  5. Function.prototype.testBind = function(that){ 
  6.     var _this = this, 
  7.         slice = Array.prototype.slice, 
  8.         args = slice.apply(arguments,[1]), 
  9.         fNOP = function () {}, 
  10.         //所以調(diào)用官方bind方法之后 有一個(gè)name屬性值為 'bound ' 
  11.         bound = function(){ 
  12.             return _this.apply(isNew ? this : that||window, 
  13.                 args.concat(Array.prototype.slice.apply(arguments,[0])) 
  14.             ) 
  15.         }     
  16.  
  17.     fNOP.prototype = _this.prototype; 
  18.  
  19.     bound.prototype = new fNOP(); 
  20.  
  21.     return bound; 
  22.  

這里的 isNew 是區(qū)分 bindFun 是直接調(diào)用還是被 new 之后再調(diào)用,通過(guò)原型鏈的繼承關(guān)系可以知道,

bindFun 屬于 after_new的父類(lèi),所以 after_new instanceof bindFun 為 true,同時(shí)

bindFun.prototype = new fNOP() 原型繼承; 所以 fNOP 也是 after_new的父類(lèi), after_new instanceof fNOP 為 true

最終結(jié)果

  1. Function.prototype.testBind = function(that){ 
  2.         var _this = this, 
  3.             slice = Array.prototype.slice, 
  4.             args = slice.apply(arguments,[1]), 
  5.             fNOP = function () {}, 
  6.             bound = function(){ 
  7.                 //這里的this指的是調(diào)用時(shí)候的環(huán)境 
  8.                 return _this.apply(this instanceof  fNOP ? this : that||window, 
  9.                     args.concat(Array.prototype.slice.apply(arguments,[0])) 
  10.                 ) 
  11.             }     
  12.         fNOP.prototype = _this.prototype; 
  13.      
  14.         bound.prototype = new fNOP(); 
  15.      
  16.         return bound; 
  17.     }  

我看到有些地方寫(xiě)的是

  1. this instanceof fNOP && that ? this : that || window, 

我個(gè)人覺(jué)得這里有點(diǎn)不正確,如果綁定時(shí)候不傳參數(shù),那么that就為空,那無(wú)論怎樣就只能綁定 window作用域了。

以上是個(gè)人見(jiàn)解,不對(duì)的地方望指導(dǎo),謝謝!

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2017-01-19 21:08:33

iOS路由構(gòu)建

2018-12-24 10:04:06

Docker存儲(chǔ)驅(qū)動(dòng)

2019-03-05 14:09:27

Docker存儲(chǔ)容器

2019-07-09 15:23:22

Docker存儲(chǔ)驅(qū)動(dòng)

2020-12-24 11:19:55

JavaMapHashMap

2017-12-25 11:50:57

LinuxArch Linux

2010-03-04 16:28:17

Android核心代碼

2023-09-07 11:09:59

連接池本地端口號(hào)

2024-08-30 08:30:29

CPU操作系統(tǒng)寄存器

2024-09-30 09:56:59

2011-05-10 10:28:55

2018-07-13 15:36:52

2009-12-17 16:36:23

無(wú)線路由設(shè)置密碼

2018-06-11 15:30:12

2024-08-06 09:29:54

程序機(jī)器指令字符串

2018-04-23 14:23:12

2019-04-01 10:15:02

2025-02-08 08:21:48

Java排序Spring

2015-07-27 16:06:16

VMware Thin虛擬化

2017-01-06 15:13:25

LinuxVim源代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)