bind() 函數會建立一個新函數(稱為綁定函數),新函數與被調函數(綁定函數的目標函數)具有相同的函數體(在 ECMAScript 5 規範中內建的call屬性)。當目標函數被呼叫時 this 值綁定到 bind() 的第一個參數,該參數不能被重寫。當綁定函數被呼叫時,bind() 也接受預設的參數提供給原函數。一個綁定函數也能使用new運算元建立物件:這種行為就像把原函數當成建構器。提供的 this 值被忽略,同時呼叫時的參數被提供給模擬函數。
由於javascript中作用域是由其運行時候所處的環境決定的,所以往往函數定義和實際運行的時候所處環境不一樣,那麼作用域也會發生相應的變化。
例如下面這個情況:
var id = 'window'; //定义一个函数,但是不立即执行 var test = function(){ console.log(this.id) } test() // window //把test作为参数传递 var obj = { id:'obj', hehe:test } //此时test函数运行环境发生了改变 obj.hehe() // 'obj' //为了避免这种情况,javascript里面有一个bind方法可以在函数运行之前就绑定其作用域,修改如下 var id = 'window'; var test = function(){ console.log(this.id) }.bind(window) var obj = { id:'obj', hehe:test } test() // window obj.hehe() // window
上面介紹了bind方法的一個重要作用就是為一個函數綁定作用域,但是bind方法在低版本瀏覽器不相容,這裡我們可以手動實現一下。
拆分一下關鍵思路
因為bind方法不會立即執行函數,需要回傳一個待執行的函數(這裡用到閉包,可以回傳一個函數)return function(){}
作用域綁定,這裡可以使用apply或call方法來實現 xx.call(yy)/xx.apply(yy)
參數傳遞,由於參數的不確定性,需要用apply傳遞數組(實例更明了)xx.apply(yy, [...Array...]),如果用call就不太方便了,因為call後面的參數需要一個個列出來
實現
有了上述的思路,大致的雛形已經明了,代碼應該也很容易實現
綁定作用域,綁定傳參
Function.prototype.testBind = function(that){ var _this = this, /* *由于参数的不确定性,统一用arguments来处理,这里的arguments只是一个类数组对象,有length属性 *可以用数组的slice方法转化成标准格式数组,除了作用域对象that以外, *后面的所有参数都需要作为数组参数传递 *Array.prototype.slice.apply(arguments,[1])/Array.prototype.slice.call(arguments,1) */ slice = Array.prototype.slice, args = slice.apply(arguments,[1]); //返回函数 return function(){ //apply绑定作用域,进行参数传递 return _this.apply(that,args) } }
測試
var test = function(a,b){ console.log('作用域绑定 '+ this.value) console.log('testBind参数传递 '+ a.value2) console.log('调用参数传递 ' + b) }var obj = { value:'ok'}var fun_new = test.testBind(obj,{value2:'also ok'}) fun_new ('hello bind')// 作用域绑定 ok// testBind参数传递 also ok// 调用参数传递 undefined
動態參數
上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數上面已經實現了bind方法的作用域綁定,但是美中不足的是,既然我們返回的是一個函數,呼叫的時候應該支援傳遞參數,很顯然,上面的 fun_new 呼叫的時候並不支援傳參,只能在 testBind 綁定的時候傳遞參數,因為我們最終呼叫的是這個回傳函數
function(){ return _this.apply(that,args) } 这里面的args在绑定的时候就已经确定了,调用的时候值已经固定, 我们并没有处理这个function传递的参数。
我們對其進行改造
return function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) }
這裡的 Array.prototype.slice.apply(arguments,[0]) 指的是這個回傳函數執行的時候傳遞的一系列參數,所以是從第一個參數開始 [0] ,之前的args = slice.apply(arguments,[1])指的是 testBind方法執行時候傳遞的參數,所以從第二個開始 [1],兩則有本質區別,不能搞混,只有兩者合併了之後才是返回函數的完整參數
所以有如下實現
Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]); return function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } }
測試
var test = function(a,b){ console.log('作用域绑定 '+ this.value) console.log('testBind参数传递 '+ a.value2) console.log('调用参数传递 ' + b) } var obj = { value:'ok' } var fun_new = test.testBind(obj,{value2:'also ok'}) fun_new ('hello bind') // 作用域绑定 ok // testBind参数传递 also ok // 调用参数传递 hello bind
在以上2種傳參方式中,bind的優先權高,從 args.concat(Array.prototype.slice.apply(arguments,[ 0])) 也可以看出來,bind的參數在陣列前面。
原型鏈
官方文檔上有一句話:
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.
說明綁定過後的函數被new實例化之後,需要繼承原函數的原型鏈方法,且綁定過程中提供的this被忽略(繼承原函數的this物件),但是參數還是會使用。
這裡就需要一個中轉函數把原型鏈傳遞下去
fNOP = function () {} //创建一个中转函数 fNOP.prototype = this.prototype; xx.prototype = new fNOP() 修改如下 Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, //所以调用官方bind方法之后 有一个name属性值为 'bound ' bound = function(){ return _this.apply(that, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
而且bind方法的第一個參數this是可以不傳的,需要分2種情況
直接調用bind之後的方法
var f = function () { console.log('不传默认为'+this) };f.bind()() // 不传默认为 Window
所以直接調用綁定
直接調用bind之後的方法function(){ return _this.apply(that || window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) }所以直接調用綁定定方法時候 apply(that, 建議改為 apply(that||window,,其實不改也可以,因為不傳默認指向window使用new實例化被綁定的方法容易糊塗,重點在於弄清楚標準的bind方法在new的時候做的事情,然後就可以清晰的實現這裡我們需要看看 new 這個方法做了哪些操作比如說 var a = new b()創建一個空對象 a = { },而this變數引用指向到這個空物件a🎜🎜繼承被實例化函數的原型:a.__proto__ = b.prototype🎜
被实例化方法b的this对象的属性和方法将被加入到这个新的 this 引用的对象中: b的属性和方法被加入的 a里面
新创建的对象由 this 所引用 :b.call(a)
通过以上可以得知,如果是var after_new = new bindFun(); 由于这种行为是把原函数当成构造器,那么那么最终实例化之后的对象 this需要继承自原函数, 而这里的 bindFun 目前是
function(){ return _this.apply(that || window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) }
这里apply的作用域是绑定的that || window,在执行 testBind()的时候就已经固定,并没有把原函数的this对象继承过来,不符合我们的要求,我们需要根据apply的特性解决这个问题:
在一个子构造函数中,你可以通过调用父构造函数的 `apply/call` 方法来实现继承
例如
function Product(name, price) { this.name = name; this.price = price; if (price < 0) { throw RangeError('Cannot create product ' + this.name + ' with a negative price'); } } function Food(name, price) { Product.call(this, name, price); this.category = 'food'; } //等同于(其实就是把Product放在Food内部执行了一次) function Food(name, price) { this.name = name; this.price = price; if (price < 0) { throw RangeError('Cannot create product ' + this.name + ' with a negative price'); } this.category = 'food'; }
所以在new新的实例的时候实时将这个新的this对象 进行 apply 继承原函数的 this 对象,就可以达到 new 方法里面的第 3 步的结果
apply(that||window, //修改为 如果是new的情况,需要绑定new之后的作用域,this指向新的实例对象 apply(isNew ? this : that||window, ==> Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, //所以调用官方bind方法之后 有一个name属性值为 'bound ' bound = function(){ return _this.apply(isNew ? this : that||window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
这里的 isNew 是区分 bindFun 是直接调用还是被 new 之后再调用,通过原型链的继承关系可以知道,
bindFun 属于 after_new的父类,所以 after_new instanceof bindFun 为 true,同时
bindFun.prototype = new fNOP() 原型继承; 所以 fNOP 也是 after_new的父类, after_new instanceof fNOP 为 true
最终结果
Function.prototype.testBind = function(that){ var _this = this, slice = Array.prototype.slice, args = slice.apply(arguments,[1]), fNOP = function () {}, bound = function(){ //这里的this指的是调用时候的环境 return _this.apply(this instanceof fNOP ? this : that||window, args.concat(Array.prototype.slice.apply(arguments,[0])) ) } fNOP.prototype = _this.prototype; bound.prototype = new fNOP(); return bound; }
我看到有些地方写的是
this instanceof fNOP && that ? this : that || window,
我个人觉得这里有点不正确,如果绑定时候不传参数,那么that就为空,那无论怎样就只能绑定 window作用域了。

JavaScript字符串替換方法詳解及常見問題解答 本文將探討兩種在JavaScript中替換字符串字符的方法:在JavaScript代碼內部替換和在網頁HTML內部替換。 在JavaScript代碼內部替換字符串 最直接的方法是使用replace()方法: str = str.replace("find","replace"); 該方法僅替換第一個匹配項。要替換所有匹配項,需使用正則表達式並添加全局標誌g: str = str.replace(/fi

本教程向您展示瞭如何將自定義的Google搜索API集成到您的博客或網站中,提供了比標準WordPress主題搜索功能更精緻的搜索體驗。 令人驚訝的是簡單!您將能夠將搜索限制為Y

本文系列在2017年中期進行了最新信息和新示例。 在此JSON示例中,我們將研究如何使用JSON格式將簡單值存儲在文件中。 使用鍵值對符號,我們可以存儲任何類型的

因此,在這裡,您準備好了解所有稱為Ajax的東西。但是,到底是什麼? AJAX一詞是指用於創建動態,交互式Web內容的一系列寬鬆的技術。 Ajax一詞,最初由Jesse J創造

利用輕鬆的網頁佈局:8 ESTISSEL插件jQuery大大簡化了網頁佈局。 本文重點介紹了簡化該過程的八個功能強大的JQuery插件,對於手動網站創建特別有用

核心要點 JavaScript 中的 this 通常指代“擁有”該方法的對象,但具體取決於函數的調用方式。 沒有當前對象時,this 指代全局對象。在 Web 瀏覽器中,它由 window 表示。 調用函數時,this 保持全局對象;但調用對象構造函數或其任何方法時,this 指代對象的實例。 可以使用 call()、apply() 和 bind() 等方法更改 this 的上下文。這些方法使用給定的 this 值和參數調用函數。 JavaScript 是一門優秀的編程語言。幾年前,這句話可

jQuery是一個很棒的JavaScript框架。但是,與任何圖書館一樣,有時有必要在引擎蓋下發現發生了什麼。也許是因為您正在追踪一個錯誤,或者只是對jQuery如何實現特定UI感到好奇

該帖子編寫了有用的作弊表,參考指南,快速食譜以及用於Android,BlackBerry和iPhone應用程序開發的代碼片段。 沒有開發人員應該沒有他們! 觸摸手勢參考指南(PDF)是Desig的寶貴資源


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3漢化版
中文版,非常好用

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

禪工作室 13.0.1
強大的PHP整合開發環境

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