搜尋
首頁web前端js教程Javascript中從學習bind到實現bind的過程詳解

本文主要介紹了Javascript中從學習bind到實現bind的過程,有興趣的朋友跟著學習下吧,希望能幫助到大家。

bind是什麼

bind()方法建立一個新的函數, 當被呼叫時,將其this關鍵字設定為提供的值,在呼叫新函數時,在任何提供之前提供一個給定的參數序列。

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

沒看懂沒事接著往下看。

bind到底做了什麼

從上面的介紹可以看出三點。首先呼叫bind方法會傳回一個新的函數(這個新的函數的函數體應該和fun是一樣的)。同時bind中傳遞兩個參數,第一個是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

第二個參數為一個序列,你可以將任意數量的參數傳遞到其中。並且會預置到新函數參數之前。

this.value = 2
var foo = {
  value: 1
};
var bar = function(name, age, school) {
 console.log(name) // 'An'
 console.log(age) // 22
 console.log(school) // '家里蹲大学'
}
var result = bar.bind(foo, 'An') //预置了部分参数'An'
result(22, '家里蹲大学') //这个参数会和预置的参数合并到一起放入bar中

我們可以看出在最後調用 result(22, '家裡蹲大學') 的時候,其內部已經包含了在調用bind的時候傳入的 'An'。

一句話總結:呼叫bind,就會回傳一個新的函數。這個函數裡面的this就指向bind的第一個參數,同時this後面的參數會提前傳給這個新的函數。呼叫該新的函數時,再傳遞的參數會放到預置的參數後一起傳遞進新函數。

自己實作一個bind

實作一個bind需要實作以下兩個功能

傳回一個函數,綁定this,傳遞預置參數

bind傳回的函式可以當作建構函式使用。故作為建構函數時應使得this失效,但是傳入的參數依然有效

1、返回一個函數,綁定this,傳遞預置參數

this.value = 2
var foo = {
  value: 1
};
var bar = function(name, age, school) {
  console.log(name) // 'An'
  console.log(age) // 22
  console.log(school) // '家里蹲大学'
  console.log(this.value) // 1
}
Function.prototype.bind = function(newThis) {
  var aArgs  = Array.prototype.slice.call(arguments, 1) //拿到除了newThis之外的预置参数序列
  var that = this
  return function() {
    return that.apply(newThis, aArgs.concat(Array.prototype.slice.call(arguments)))
    //绑定this同时将调用时传递的序列和预置序列进行合并
  }
}
var result = bar.bind(foo, 'An')
result(22, '家里蹲大学')

這裡面有一個細節就是Array.prototype.slice.call(arguments, 1) 這句話,我們知道arguments這個變數可以拿到函數呼叫時傳遞的參數,但不是一個數組,但是其具有一個length屬性。為什麼如此呼叫就可以將其變成純數組了呢。那我們就需要回到V8的源碼來進行分析。 #這個版本的原始碼為早期版本,內容相對少一些。

function ArraySlice(start, end) {
 var len = ToUint32(this.length); 
 //需要传递this指向对象,那么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  len) start_i = len;
 }
 if (end_i  len) end_i = len;
 }
 var result = [];
 if (end_i <p>從原始碼中可以看到透過call將arguments下的length屬性賦給slice後,便可透過start_i & end_i來獲得最後的數組,所以不需要傳遞進slice時就是一個純數組最後也可以得到一個數組變數。 </p><p>2、bind傳回的函式可以作為建構函式使用<br></p><p>被用來當作建構子時,this應指向new出來的實例,同時有prototype屬性,其指向實例的原型。 </p><pre class="brush:php;toolbar:false">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, '家里蹲大学')
console.log('person', person.name) //'Lsc'

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

1.給回傳的函數模擬一個prototype屬性。 ,因為透過建構函式new出來的實例可以查詢到原型上定義的屬性和方法

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

透過上面程式碼可以看出,that總是指向bar。同時傳回的函數已經繼承了that.prototype即bar.prototype。為什麼不直接讓傳回的函數的prototype屬性resultFunc.prototype 等於為bar(that).prototype呢,這是因為任何new出來的實例都可以存取原型鏈。如果直接賦值那麼new出來的物件可以直接修改bar函數的原型鏈,這也就是原型鏈污染。所以我們採用繼承的方式(將建構函式的原型鏈賦值為父級建構子的實例),讓new出來的物件的原型鏈與bar脫離關係。

2.判斷目前被呼叫時,this是用於普通的bind還是用於建構函數從而更改this指向。

如何判斷當前this指向了哪裡呢,透過第一點我們已經知道,透過bind方法返回的新函數已經有了原型鏈,剩下需要我們做的就是改變this的指向就可以模擬完成了。透過什麼來判斷當前被召喚是以何種姿勢呢。答案是instanceof 。

instanceof 運算子用來測試一個物件在其原型鏈中是否存在一個建構函式的 prototype 屬性。

// 定义构造函数
function C(){} 
function D(){} 
var o = new C();
// true,因为 Object.getPrototypeOf(o) === C.prototype
o instanceof C; 
// false,因为 D.prototype不在o的原型链上
o instanceof D;

從上面可以看出,instanceof可以判斷出一個物件是否是由這個函數new出來的,如果是new出來的,那麼這個物件的原型鏈應為該函數的prototype.

所以我們來看這段關鍵的返回的函數結構:

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

在這其中我們要先認清this instanceof that 中的this是bind函數被調用後,返回的新函數中的this。所以這個this可能執行在普通的作用域環境,同時也可能被new一下從而改變自己的指向。再看that,that總是指向了bar,同時其原型鏈that.prototype是一直存在的。所以如果現在這個新函數要做new操作,那麼this指向了新函數,那麼 this instanceof that === true, 所以在apply中傳入this為指向,即指向新函數。如果是普通調用,那麼this不是被new出來的,即新函數不是作為構造函數,this instanceof that === false就很顯而易見了。這個時候是正常的bind呼叫。將呼叫的第一個參數作為this的指向即可。

完整程式碼(MDN下的實作)

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;
 };
}

可以看到,其首先做了当前是否支持bind的判定,不支持再实行兼容。同时判断调用这个方法的对象是否是个函数,如果不是则报错。

同时这个模拟的方法也有一些缺陷,可关注MDN上的Polyfill部分

小结

模拟bind实现最大的一个缺陷是,模拟出来的函数中会一直存在prototype属性,但是原生的bind作为构造函数是没有prototype的,这点打印一下即可知。不过这样子new出来的实例没有原型链,那么它的意义是什么呢。

相关推荐:

Jquery中.bind()、.live()、.delegate()和.on()之间的区别实例分享

jQuery中关于bind()函数详解

Js的this指向 apply().call(),bind()的问题

以上是Javascript中從學習bind到實現bind的過程詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python和JavaScript:了解每個的優勢Python和JavaScript:了解每個的優勢May 06, 2025 am 12:15 AM

Python和JavaScript各有優勢,選擇取決於項目需求和個人偏好。 1.Python易學,語法簡潔,適用於數據科學和後端開發,但執行速度較慢。 2.JavaScript在前端開發中無處不在,異步編程能力強,Node.js使其適用於全棧開發,但語法可能複雜且易出錯。

JavaScript的核心:它是在C還是C上構建的?JavaScript的核心:它是在C還是C上構建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; sanInterpretedlanguagethatrunsonenginesoftenwritteninc.1)JavascriptwasdesignedAsignedAsalightWeight,drackendedlanguageforwebbrowsers.2)Enginesevolvedfromsimpleterterpretpretpretpretpreterterpretpretpretpretpretpretpretpretpretcompilerers,典型地,替代品。

JavaScript應用程序:從前端到後端JavaScript應用程序:從前端到後端May 04, 2025 am 12:12 AM

JavaScript可用於前端和後端開發。前端通過DOM操作增強用戶體驗,後端通過Node.js處理服務器任務。 1.前端示例:改變網頁文本內容。 2.後端示例:創建Node.js服務器。

Python vs. JavaScript:您應該學到哪種語言?Python vs. JavaScript:您應該學到哪種語言?May 03, 2025 am 12:10 AM

選擇Python還是JavaScript應基於職業發展、學習曲線和生態系統:1)職業發展:Python適合數據科學和後端開發,JavaScript適合前端和全棧開發。 2)學習曲線:Python語法簡潔,適合初學者;JavaScript語法靈活。 3)生態系統:Python有豐富的科學計算庫,JavaScript有強大的前端框架。

JavaScript框架:為現代網絡開發提供動力JavaScript框架:為現代網絡開發提供動力May 02, 2025 am 12:04 AM

JavaScript框架的強大之處在於簡化開發、提升用戶體驗和應用性能。選擇框架時應考慮:1.項目規模和復雜度,2.團隊經驗,3.生態系統和社區支持。

JavaScript,C和瀏覽器之間的關係JavaScript,C和瀏覽器之間的關係May 01, 2025 am 12:06 AM

引言我知道你可能會覺得奇怪,JavaScript、C 和瀏覽器之間到底有什麼關係?它們之間看似毫無關聯,但實際上,它們在現代網絡開發中扮演著非常重要的角色。今天我們就來深入探討一下這三者之間的緊密聯繫。通過這篇文章,你將了解到JavaScript如何在瀏覽器中運行,C 在瀏覽器引擎中的作用,以及它們如何共同推動網頁的渲染和交互。 JavaScript與瀏覽器的關係我們都知道,JavaScript是前端開發的核心語言,它直接在瀏覽器中運行,讓網頁變得生動有趣。你是否曾經想過,為什麼JavaScr

node.js流帶打字稿node.js流帶打字稿Apr 30, 2025 am 08:22 AM

Node.js擅長於高效I/O,這在很大程度上要歸功於流。 流媒體匯總處理數據,避免內存過載 - 大型文件,網絡任務和實時應用程序的理想。將流與打字稿的類型安全結合起來創建POWE

Python vs. JavaScript:性能和效率注意事項Python vs. JavaScript:性能和效率注意事項Apr 30, 2025 am 12:08 AM

Python和JavaScript在性能和效率方面的差異主要體現在:1)Python作為解釋型語言,運行速度較慢,但開發效率高,適合快速原型開發;2)JavaScript在瀏覽器中受限於單線程,但在Node.js中可利用多線程和異步I/O提升性能,兩者在實際項目中各有優勢。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)