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

資訊專(zhuān)欄INFORMATION COLUMN

Javascript之bind

王巖威 / 358人閱讀

摘要:返回的函數(shù)可以作為構(gòu)造函數(shù)使用被用作構(gòu)造函數(shù)時(shí),應(yīng)指向出來(lái)的實(shí)例,同時(shí)有屬性,其指向?qū)嵗脑?。判斷?dāng)前被調(diào)用時(shí),是用于普通的還是用于構(gòu)造函數(shù)從而更改指向。運(yùn)算符用來(lái)測(cè)試一個(gè)對(duì)象在其原型鏈中是否存在一個(gè)構(gòu)造函數(shù)的屬性。

寫(xiě)在最前

最近開(kāi)始重新學(xué)習(xí)一波js,框架用久了有些時(shí)候覺(jué)得這樣子應(yīng)該可以實(shí)現(xiàn)發(fā)現(xiàn)就真的實(shí)現(xiàn)了,但是為什么這么寫(xiě)好像又說(shuō)不太清楚,之前讀了LucasHC以及冴羽的兩篇關(guān)于bind的文章感覺(jué)自己好像基礎(chǔ)知識(shí)都還給體育老師了哈哈哈,所以危機(jī)感爆棚,趕緊重頭復(fù)習(xí)一遍。本次主要圍繞bind是什么;做了什么;自己怎么實(shí)現(xiàn)一個(gè)bind,這三個(gè)部分。其中會(huì)包含一些細(xì)節(jié)代碼的探究,往下看就知道。

所以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...)

沒(méi)看懂沒(méi)事接著往下看。

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ū)ο螅敲碿all(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;
};

從源碼中可以看到通過(guò)call將arguments下的length屬性賦給slice后,便可通過(guò) 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)橥ㄟ^(guò)構(gòu)造函數(shù)new出來(lái)的實(shí)例可以查詢(xún)到原型上定義的屬性和方法
var NoFunc = function() {}
...
NoFunc.prototype = that.prototype //that指向bar
resultFunc.prototype = new NoFunc()
return resultFunc

通過(guò)上面代碼可以看出,that始終指向bar。同時(shí)返回的函數(shù)已經(jīng)繼承了that.prototype即bar.prototype。為什么不直接讓返回的函數(shù)的prototype屬性resultFunc.prototype 等于為bar(that).prototype呢,這是因?yàn)槿魏蝞ew出來(lái)的實(shí)例都可以訪問(wèn)原型鏈。如果直接賦值那么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指向了哪里呢,通過(guò)第一點(diǎn)我們已經(jīng)知道,通過(guò)bind方法返回的新函數(shù)已經(jīng)有了原型鏈,剩下需要我們做的就是改變this的指向就可以模擬完成了。通過(guò)什么來(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)?Object.getPrototypeOf(o) === C.prototype
o instanceof C; 
// false,因?yàn)?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就很顯而易見(jiàn)了。這個(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ò)。

同時(shí)這個(gè)模擬的方法也有一些缺陷,可關(guān)注MDN上的Polyfill部分

小結(jié)

模擬bind實(shí)現(xiàn)最大的一個(gè)缺陷是,模擬出來(lái)的函數(shù)中會(huì)一直存在prototype屬性,但是原生的bind作為構(gòu)造函數(shù)是沒(méi)有prototype的,這點(diǎn)打印一下即可知。不過(guò)這樣子new出來(lái)的實(shí)例沒(méi)有原型鏈,那么它的意義是什么呢。如果哪天作者知道了意義會(huì)更新在這里的=。= 如果說(shuō)錯(cuò)的地方歡迎指正,一起交流哈哈。

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

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

相關(guān)文章

  • JavaScript深入bind的模擬實(shí)現(xiàn)

    摘要:也就是說(shuō)當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)的時(shí)候,時(shí)指定的值會(huì)失效,但傳入的參數(shù)依然生效。構(gòu)造函數(shù)效果的優(yōu)化實(shí)現(xiàn)但是在這個(gè)寫(xiě)法中,我們直接將,我們直接修改的時(shí)候,也會(huì)直接修改函數(shù)的。 JavaScript深入系列第十一篇,通過(guò)bind函數(shù)的模擬實(shí)現(xiàn),帶大家真正了解bind的特性 bind 一句話介紹 bind: bind() 方法會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)...

    FingerLiu 評(píng)論0 收藏0
  • JavaScript柯里化

    摘要:簡(jiǎn)介柯里化,又稱(chēng)部分求值,是把接收多個(gè)參數(shù)的函數(shù)變成接受一個(gè)單一參數(shù)最初函數(shù)的第一個(gè)參數(shù)的函數(shù),并且返回接受剩余的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。按照作者的說(shuō)法,所謂柯里化就是使函數(shù)理解并處理部分應(yīng)用。的思想極大地助于提升函數(shù)的復(fù)用性。 簡(jiǎn)介 柯里化(Currying),又稱(chēng)部分求值(Partial Evaluation),是把接收多個(gè)參數(shù)的函數(shù)變成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)...

    since1986 評(píng)論0 收藏0
  • JavaScript專(zhuān)題模擬實(shí)現(xiàn)bind

    摘要:但是三作為構(gòu)造函數(shù)時(shí)函數(shù)其實(shí)還有一個(gè)非常重要的特點(diǎn)返回的函數(shù)如果作為構(gòu)造函數(shù),搭配關(guān)鍵字出現(xiàn)的話,我們的綁定就需要被忽略。其次,當(dāng)返回的函數(shù)作為構(gòu)造函數(shù)時(shí),之前綁定的會(huì)失效。 本文共 1100 字,讀完只需 4 分鐘 概述 前一篇文章我們嘗試模擬實(shí)現(xiàn)了 call 和 apply 方法,其實(shí) bind 函數(shù)也可以用來(lái)改變 this 的指向。bind 和 call和 apply 兩者的區(qū)別...

    劉明 評(píng)論0 收藏0
  • javascript 基礎(chǔ) call, apply, bind

    摘要:系統(tǒng),扎實(shí)的語(yǔ)言基礎(chǔ)是一個(gè)優(yōu)秀的前端工程師必須具備的。第一個(gè)參數(shù)為調(diào)用函數(shù)時(shí)的指向,隨后的參數(shù)則作為函數(shù)的參數(shù)并調(diào)用,也就是。和的區(qū)別只有一個(gè),就是它只有兩個(gè)參數(shù),而且第二個(gè)參數(shù)為調(diào)用函數(shù)時(shí)的參數(shù)構(gòu)成的數(shù)組。 系統(tǒng),扎實(shí)的 javascript 語(yǔ)言基礎(chǔ)是一個(gè)優(yōu)秀的前端工程師必須具備的。在看了一些關(guān)于 call,apply,bind 的文章后,我還是打算寫(xiě)下這篇總結(jié),原因其實(shí)有好幾個(gè)。...

    xeblog 評(píng)論0 收藏0
  • 魔幻語(yǔ)言 JavaScript 系列 call、bind 以及上下文

    摘要:那么,它到底是如何工作的呢讓我們從一種更簡(jiǎn)單的實(shí)現(xiàn)開(kāi)始實(shí)際上這種實(shí)現(xiàn)代碼更短,并且更易讀是函數(shù)原型中的一個(gè)函數(shù),它調(diào)用函數(shù),使用第一個(gè)參數(shù)作為參數(shù),并傳遞剩余參數(shù)作為被調(diào)用函數(shù)的參數(shù)。 原文:The Most Clever Line of JavaScript 作者:Seva Zaikov 原文 最近 一個(gè)朋友 發(fā)給我一段非常有趣的 JavaScript 代碼,是他在某個(gè) 開(kāi)源庫(kù)中...

    cuieney 評(píng)論0 收藏0
  • Emscripten教程C++和JavaScript綁定(三)

    摘要:支持綁定大多數(shù)的結(jié)構(gòu),包括和中引入的。枚舉支持枚舉和枚舉類(lèi)。雖然還有進(jìn)一步優(yōu)化的空間,但到目前為止,它在實(shí)際應(yīng)用程序中的性能已經(jīng)被證明是完全可以接受的。 翻譯:云荒杯傾 Embind用于綁定C++函數(shù)和類(lèi)到JavaScript,這樣編譯代碼就能在js中以一種很自然的方式來(lái)使用。Embind也支持從C++調(diào)JavaScript的class。 Embind支持綁定大多數(shù)C++的結(jié)構(gòu),包括C...

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

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

0條評(píng)論

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