Javascript中從學(xué)習(xí)bind到實(shí)現(xiàn)bind的示例分析

這篇文章給大家分享的是有關(guān)Javascript中從學(xué)習(xí)bind到實(shí)現(xiàn)bind的示例分析的內(nèi)容。小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過來(lái)看看吧。

成都創(chuàng)新互聯(lián)是專業(yè)的衡陽(yáng)縣網(wǎng)站建設(shè)公司,衡陽(yáng)縣接單;提供網(wǎng)站制作、網(wǎng)站建設(shè),網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行衡陽(yáng)縣網(wǎng)站開發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來(lái)合作!

bind是什么

bind()方法創(chuàng)建一個(gè)新的函數(shù), 當(dāng)被調(diào)用時(shí),將其this關(guān)鍵字設(shè)置為提供的值,在調(diào)用新函數(shù)時(shí),在任何提供之前提供一個(gè)給定的參數(shù)序列。

var result = fun.bind(thisArg[, arg1[, arg2[, ...]]]) 
result(newArg1, newArg2...)

沒看懂沒事接著往下看。

bind到底做了什么

從上面的介紹中可以看出三點(diǎn)。首先調(diào)用bind方法會(huì)返回一個(gè)新的函數(shù)(這個(gè)新的函數(shù)的函數(shù)體應(yīng)該和fun是一樣的)。同時(shí)bind中傳遞兩個(gè)參數(shù),第一個(gè)是this指向,即傳入了什么this就等于什么。如下代碼所示:

this.value = 2
var foo = {
  value: 1
}
var bar = function() {
 console.log(this.value)
}
var result = bar.bind(foo)
bar() // 2
result() // 1,即this === foo

第二個(gè)參數(shù)為一個(gè)序列,你可以傳遞任意數(shù)量的參數(shù)到其中。并且會(huì)預(yù)置到新函數(shù)參數(shù)之前。

this.value = 2
var foo = {
  value: 1
};
var bar = function(name, age, school) {
 console.log(name) // 'An'
 console.log(age) // 22
 console.log(school) // '家里蹲大學(xué)'
}
var result = bar.bind(foo, 'An') //預(yù)置了部分參數(shù)'An'
result(22, '家里蹲大學(xué)') //這個(gè)參數(shù)會(huì)和預(yù)置的參數(shù)合并到一起放入bar中

我們可以看出在最后調(diào)用 result(22, '家里蹲大學(xué)') 的時(shí)候,其內(nèi)部已經(jīng)包含了在調(diào)用bind的時(shí)候傳入的 'An'。

一句話總結(jié):調(diào)用bind,就會(huì)返回一個(gè)新的函數(shù)。這個(gè)函數(shù)里面的this就指向bind的第一個(gè)參數(shù),同時(shí)this后面的參數(shù)會(huì)提前傳給這個(gè)新的函數(shù)。調(diào)用該新的函數(shù)時(shí),再傳遞的參數(shù)會(huì)放到預(yù)置的參數(shù)后一起傳遞進(jìn)新函數(shù)。

自己實(shí)現(xiàn)一個(gè)bind

實(shí)現(xiàn)一個(gè)bind需要實(shí)現(xiàn)以下兩個(gè)功能

返回一個(gè)函數(shù),綁定this,傳遞預(yù)置參數(shù)

bind返回的函數(shù)可以作為構(gòu)造函數(shù)使用。故作為構(gòu)造函數(shù)時(shí)應(yīng)使得this失效,但是傳入的參數(shù)依然有效

1、返回一個(gè)函數(shù),綁定this,傳遞預(yù)置參數(shù)

this.value = 2
var foo = {
  value: 1
};
var bar = function(name, age, school) {
  console.log(name) // 'An'
  console.log(age) // 22
  console.log(school) // '家里蹲大學(xué)'
  console.log(this.value) // 1
}
Function.prototype.bind = function(newThis) {
  var aArgs  = Array.prototype.slice.call(arguments, 1) //拿到除了newThis之外的預(yù)置參數(shù)序列
  var that = this
  return function() {
    return that.apply(newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
    //綁定this同時(shí)將調(diào)用時(shí)傳遞的序列和預(yù)置序列進(jìn)行合并
  }
}
var result = bar.bind(foo, 'An')
result(22, '家里蹲大學(xué)')

這里面有一個(gè)細(xì)節(jié)就是Array.prototype.slice.call(arguments, 1) 這句話,我們知道arguments這個(gè)變量可以拿到函數(shù)調(diào)用時(shí)傳遞的參數(shù),但不是一個(gè)數(shù)組,但是其具有一個(gè)length屬性。為什么如此調(diào)用就可以將其變?yōu)榧償?shù)組了呢。那么我們就需要回到V8的源碼來(lái)進(jìn)行分析。#這個(gè)版本的源碼為早期版本,內(nèi)容相對(duì)少一些。

function ArraySlice(start, end) {
 var len = ToUint32(this.length); 
 //需要傳遞this指向?qū)ο?,那么call(arguments),
 //便可將this綁定到arguments,拿到其length屬性。
 var start_i = TO_INTEGER(start);
 var end_i = len;
 if (end !== void 0) end_i = TO_INTEGER(end);
 if (start_i < 0) {
  start_i += len;
  if (start_i < 0) start_i = 0;
 } else {
  if (start_i > len) start_i = len;
 }
 if (end_i < 0) {
  end_i += len;
  if (end_i < 0) end_i = 0;
 } else {
  if (end_i > len) end_i = len;
 }
 var result = [];
 if (end_i < start_i)
  return result;
 if (IS_ARRAY(this))
  SmartSlice(this, start_i, end_i - start_i, len, result);
 else 
  SimpleSlice(this, start_i, end_i - start_i, len, result);
 result.length = end_i - start_i;
 return result;
};

從源碼中可以看到通過call將arguments下的length屬性賦給slice后,便可通過 start_i & end_i來(lái)獲得最后的數(shù)組,所以不需要傳遞進(jìn)slice時(shí)就是一個(gè)純數(shù)組最后也可以得到一個(gè)數(shù)組變量。

2、bind返回的函數(shù)可以作為構(gòu)造函數(shù)使用

被用作構(gòu)造函數(shù)時(shí),this應(yīng)指向new出來(lái)的實(shí)例,同時(shí)有prototype屬性,其指向?qū)嵗脑汀?/p>

this.value = 2
var foo = {
 value: 1
};
var bar = function(name, age, school) {
 ...
 console.log('this.value', this.value)
}
Function.prototype.bind = function(newThis) {
 var aArgs  = Array.prototype.slice.call(arguments, 1)
 var that = this //that始終指向bar
 var NoFunc = function() {}
 var resultFunc = function() {
  return that.apply(this instanceof that ? this : newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
 } 
 NoFunc.prototype = that.prototype //that指向bar
 resultFunc.prototype = new NoFunc()
 return resultFunc
}
var result = bar.bind(foo, 'An')
result.prototype.name = 'Lsc' // 有prototype屬性
var person = new result(22, '家里蹲大學(xué)')
console.log('person', person.name) //'Lsc'

上面這段模擬代碼做了兩件重要的事。

1.給返回的函數(shù)模擬一個(gè)prototype屬性。,因?yàn)橥ㄟ^構(gòu)造函數(shù)new出來(lái)的實(shí)例可以查詢到原型上定義的屬性和方法

var NoFunc = function() {}
...
NoFunc.prototype = that.prototype //that指向bar
resultFunc.prototype = new NoFunc()
return resultFunc

通過上面代碼可以看出,that始終指向bar。同時(shí)返回的函數(shù)已經(jīng)繼承了that.prototype即bar.prototype。為什么不直接讓返回的函數(shù)的prototype屬性resultFunc.prototype 等于為bar(that).prototype呢,這是因?yàn)槿魏蝞ew出來(lái)的實(shí)例都可以訪問原型鏈。如果直接賦值那么new出來(lái)的對(duì)象可以直接修改bar函數(shù)的原型鏈,這也就是是原型鏈污染。所以我們采用繼承的方式(將構(gòu)造函數(shù)的原型鏈賦值為父級(jí)構(gòu)造函數(shù)的實(shí)例),讓new出來(lái)的對(duì)象的原型鏈與bar脫離關(guān)系。

2.判斷當(dāng)前被調(diào)用時(shí),this是用于普通的bind還是用于構(gòu)造函數(shù)從而更改this指向。

如何判斷當(dāng)前this指向了哪里呢,通過第一點(diǎn)我們已經(jīng)知道,通過bind方法返回的新函數(shù)已經(jīng)有了原型鏈,剩下需要我們做的就是改變this的指向就可以模擬完成了。通過什么來(lái)判斷當(dāng)前被調(diào)用是以何種姿勢(shì)呢。答案是instanceof 。

instanceof 運(yùn)算符用來(lái)測(cè)試一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的 prototype 屬性。

// 定義構(gòu)造函數(shù)
function C(){} 
function D(){} 
var o = new C();
// true,因?yàn)?nbsp;Object.getPrototypeOf(o) === C.prototype
o instanceof C; 
// false,因?yàn)?nbsp;D.prototype不在o的原型鏈上
o instanceof D;

從上面可以看出,instanceof可以判斷出一個(gè)對(duì)象是否是由這個(gè)函數(shù)new出來(lái)的,如果是new出來(lái)的,那么這個(gè)對(duì)象的原型鏈應(yīng)為該函數(shù)的prototype.

所以我們來(lái)看這段關(guān)鍵的返回的函數(shù)結(jié)構(gòu):

var resultFunc = function() {
  return that.apply(this instanceof that ? 
    this : 
    newThis, 
    aArgs.concat(Array.prototype.slice.call(arguments)))
 }

在這其中我們要先認(rèn)清this instanceof that 中的this是bind函數(shù)被調(diào)用后,返回的新函數(shù)中的this。所以這個(gè)this可能執(zhí)行在普通的作用域環(huán)境,同時(shí)也可能被new一下從而改變自己的指向。再看that,that始終指向了bar,同時(shí)其原型鏈that.prototype是一直存在的。所以如果現(xiàn)在這個(gè)新函數(shù)要做new操作,那么this指向了新函數(shù),那么 this instanceof that === true, 所以在apply中傳入this為指向,即指向新函數(shù)。如果是普通調(diào)用,那么this不是被new出來(lái)的,即新函數(shù)不是作為構(gòu)造函數(shù),this instanceof that === false就很顯而易見了。這個(gè)時(shí)候是正常的bind調(diào)用。將調(diào)用的第一個(gè)參數(shù)作為this的指向即可。

完整代碼(MDN下的實(shí)現(xiàn))

if (!Function.prototype.bind) {
 Function.prototype.bind = function(oThis) {
  if (typeof this !== 'function') {
   // closest thing possible to the ECMAScript 5
   // internal IsCallable function
   throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
  }

  var aArgs  = Array.prototype.slice.call(arguments, 1),
    fToBind = this,
    fNOP  = function() {},
    fBound = function() {
     return fToBind.apply(this instanceof fNOP
         ? this
         : oThis,
         aArgs.concat(Array.prototype.slice.call(arguments)));
    };

  if (this.prototype) {
   // Function.prototype doesn't have a prototype property
   fNOP.prototype = this.prototype; 
  }
  fBound.prototype = new fNOP();
  return fBound;
 };
}

可以看到,其首先做了當(dāng)前是否支持bind的判定,不支持再實(shí)行兼容。同時(shí)判斷調(diào)用這個(gè)方法的對(duì)象是否是個(gè)函數(shù),如果不是則報(bào)錯(cuò)。

感謝各位的閱讀!關(guān)于“Javascript中從學(xué)習(xí)bind到實(shí)現(xiàn)bind的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!

分享題目:Javascript中從學(xué)習(xí)bind到實(shí)現(xiàn)bind的示例分析
轉(zhuǎn)載注明:http://muchs.cn/article34/geedpe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航網(wǎng)站設(shè)計(jì)公司、網(wǎng)站建設(shè)、網(wǎng)站維護(hù)服務(wù)器托管軟件開發(fā)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)