Javascript中bind()方法的使用與實現

来源:http://www.cnblogs.com/androidshouce/archive/2016/07/19/5683421.html
-Advertisement-
Play Games

我們先來看一道題目 1 2 3 4 var write = document.write; write("hello"); //1.以上代碼有什麼問題 //2.正確操作是怎樣的 1 2 3 4 var write = document.write; write("hello"); //1.以上代碼有 ...


我們先來看一道題目

1 2 3 4 var write = document.write;  write("hello");  //1.以上代碼有什麼問題 //2.正確操作是怎樣的

不能正確執行,因為write函數丟掉了上下文,此時this的指向global或window對象,導致執行時提示非法調用異常,所以我們需要改變this的指向

正確的方案就是使用 bind/call/apply來改變this指向

bind方法

1 2 var write = document.write; write.bind(document)('hello');

call方法

1 2 var write = document.write; write.call(document,'hello');

apply方法

1 2 var write = document.write; write.apply(document,['hello']);

bind函數

bind()最簡單的用法是創建一個函數,使這個函數不論怎麼調用都有同樣的this值。常見的錯誤就像上面的例子一樣,將方法從對象中拿出來,然後調用,並且希望this指向原來的對象。如果不做特殊處理,一般會丟失原來的對象。使用bind()方法能夠很漂亮的解決這個問題:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script type="text/javascript">   this.num = 9;  var module = {      num: 81,     getNum: function(){         console.log(this.num);     } };   module.getNum(); // 81 ,this->module   var getNum = module.getNum; getNum(); // 9, this->window or global   var boundGetNum = getNum.bind(module);  boundGetNum(); // 81,this->module   </script>

偏函數(Partial Functions)

Partial Functions也叫Partial Applications,這裡截取一段關於偏函數的定義:

Partial application can be described as taking a function that accepts some number of arguments, binding values to one or more of those arguments, and returning a new function that only accepts the remaining, un-bound arguments.

這是一個很好的特性,使用bind()我們設定函數的預定義參數,然後調用的時候傳入其他參數即可:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <script type="text/javascript">   function list() {    return Array.prototype.slice.call(arguments); }   var list1 = list(1, 2, 3); console.log(list1);// [1, 2, 3]   // 預定義參數37 var leadingThirtysevenList = list.bind(undefined, 37);   var list2 = leadingThirtysevenList(); console.log(list2);// [37]    var list3 = leadingThirtysevenList(1, 2, 3); console.log(list3);// [37, 1, 2, 3]  </script>

和setTimeout or setInterval一起使用

一般情況下setTimeout()的this指向window或global對象。當使用類的方法時需要this指向類實例,就可以使用bind()將this綁定到回調函數來管理實例。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <script type="text/javascript">   function Bloomer() {    this.petalCount = Math.ceil(Math.random() * 12) + 1; }   // 1秒後調用declare函數 Bloomer.prototype.bloom = function() {    window.setTimeout(this.declare.bind(this), 1000); };   Bloomer.prototype.declare = function() {    console.log('我有 ' this.petalCount + ' 朵花瓣!'); };   var test = new Bloomer();   test.bloom();   </script>

綁定函數作為構造函數

綁定函數也適用於使用new操作符來構造目標函數的實例。當使用綁定函數來構造實例,註意:this會被忽略,但是傳入的參數仍然可用。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <script type="text/javascript">   function Point(x, y) {      this.x = x;   this.y = y; }   Point.prototype.toString = function() {    console.log(this.x + ',' this.y); };   var p = new Point(1, 2);  p.toString(); // 1,2   var YAxisPoint = Point.bind(null,10); var axisPoint = new YAxisPoint(5);  axisPoint.toString(); // 10,5   console.log(axisPoint instanceof Point); // true  console.log(axisPoint instanceof YAxisPoint); // true  console.log(new Point(17, 42) instanceof YAxisPoint); // true   </script>

上面例子中Point和YAxisPoint共用原型,因此使用instanceof運算符判斷時為true

偽數組的轉化

上面的幾個小節可以看出bind()有很多的使用場景,但是bind()函數是在 ECMA-262 第五版才被加入;它可能無法在所有瀏覽器上運行。這就需要我們自己實現bind()函數了。

首先我們可以通過給目標函數指定作用域來簡單實現bind()方法:

1 2 3 4 5 6 Function.prototype.bind = function(context){    self = this;  //保存this,即調用bind方法的目標函數   return function(){       return self.apply(context,arguments);   }; };

考慮到函數柯里化的情況,我們可以構建一個更加健壯的bind()

1 2 3 4 5 6 7 8 Function.prototype.bind = function(context){    var args = Array.prototype.slice.call(arguments, 1),   self = this;   return function(){       var innerArgs = Array.prototype.slice.call(arguments);       var finalArgs = args.concat(innerArgs);       return self.apply(context,finalArgs);   };<br>}

這次的bind()方法可以綁定對象,也支持在綁定的時候傳參。

繼續,Javascript的函數還可以作為構造函數,那麼綁定後的函數用這種方式調用時,情況就比較微妙了,需要涉及到原型鏈的傳遞:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 Function.prototype.bind = function(context){    var args = Array.prototype.slice(arguments, 1),   F = function(){},   self = this,   bound = function(){       var innerArgs = Array.prototype.slice.call(arguments);       var finalArgs = args.concat(innerArgs);       return self.apply((this instanceof F ? this : context), finalArgs);   };     F.prototype = self.prototype;   bound.prototype = new F();   return bound; };

這是《JavaScript Web Application》一書中對bind()的實現:通過設置一個中轉構造函數F,使綁定後的函數與調用bind()的函數處於同一原型鏈上,用new操作符調用綁定後的函數,返回的對象也能正常使用instanceof,因此這是最嚴謹的bind()實現。

對於為了在瀏覽器中能支持bind()函數,只需要對上述函數稍微修改即可:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Function.prototype.bind = function (oThis) {      if (typeof this !== "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 && oThis ? this : oThis || window,               aArgs.concat(Array.prototype.slice.call(arguments))           );         };       fNOP.prototype = this.prototype;     fBound.prototype = new fNOP();       return fBound; };

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、SpEL:Spring 表達式語言,在使用的時候類似於 EL 表達式,但是需要註意的是,SpEL 使用在 Spring Config 文件中。 二、格式:使用 #{} 作為界定符,所有在大括弧中的字元都將被認為成是 SeEL 三、作用: 1.通過 Bean 的 id 對 Bean 進行引用 2. ...
  • 新建一個線程並啟動,開銷會很大,因為運行線程需要的資源比調用對象方法需要的資源多得多。在很多情況下,線程被用於執行一類任務,而這類任務數量很多,發生的時間分佈不均,如果為每個新任務都啟用一個新線程來執行,則開銷會太大,可以採用一種性能優化技術,就是使用線程池。 將若幹執行任務的線程放在池中,當有任務 ...
  • css是網頁的外衣,好不好看全憑css樣式,而佈局是css中比較重要的部分,下麵來分析一下常見的幾種佈局。 流動模型 流動模型是網頁佈局的預設模式,也是最常見的佈局模式,他有兩個特點: 1.塊狀元素都在所處包含元素內自上而下按順序垂直延伸分佈。常見的塊狀元素有:div,p,ul,ol,h1~h6,a ...
  • 方法:直接判斷瀏覽器是否支持某個CSS屬性才是王道,document.documentElement.style 如:判斷是否支持 transform if( 'MozTransform' in document.documentElement.style || 'WebkitTransform' ...
  • 作者:白狼 出處:http://www.manks.top/javascript-dynamic-event.html 本文版權歸作者,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。 其所謂的動態添加事件實質就是指js中的事件委托。 我們知道 ...
  • 線上實例 實例演示 使用方法 複製 複製 下載 ...
  • 本文標題的這副圖片,是用Phosotshop製作的。但是,在搜索引擎中你卻無法搜索到它,搜索引擎還沒有強大到能夠識別圖片裡面的文字。並且由於圖片的體積不算太小,可能網速慢的網友在瀏覽的時候不得不耐心的等待圖片的刷新。那麼,有沒有一種新的方法可以避免這些缺點呢? 有的,HTML5和CSS3就可以滿足你 ...
  • 別人的代碼,拿過來調,發現修改功能都不能用,修改時通過ajax發json獲取數據的,看chrome開發者工具發現有發送數據,也有返回值; 發起請求並獲取數據,發現回調函數不執行! php返回數據代碼: 返回的數據在瀏覽器里看上去也很正常: {"data":{"id":"1","name":"admi ...
一周排行
    -Advertisement-
    Play Games
  • 概述:本文代碼示例演示瞭如何在WPF中使用LiveCharts庫創建動態條形圖。通過創建數據模型、ViewModel和在XAML中使用`CartesianChart`控制項,你可以輕鬆實現圖表的數據綁定和動態更新。我將通過清晰的步驟指南包括詳細的中文註釋,幫助你快速理解並應用這一功能。 先上效果: 在 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • openGauss(GaussDB ) openGauss是一款全面友好開放,攜手伙伴共同打造的企業級開源關係型資料庫。openGauss採用木蘭寬鬆許可證v2發行,提供面向多核架構的極致性能、全鏈路的業務、數據安全、基於AI的調優和高效運維的能力。openGauss深度融合華為在資料庫領域多年的研 ...
  • 概述:本示例演示了在WPF應用程式中實現多語言支持的詳細步驟。通過資源字典和數據綁定,以及使用語言管理器類,應用程式能夠在運行時動態切換語言。這種方法使得多語言支持更加靈活,便於維護,同時提供清晰的代碼結構。 在WPF中實現多語言的一種常見方法是使用資源字典和數據綁定。以下是一個詳細的步驟和示例源代 ...
  • 描述(做一個簡單的記錄): 事件(event)的本質是一個委托;(聲明一個事件: public event TestDelegate eventTest;) 委托(delegate)可以理解為一個符合某種簽名的方法類型;比如:TestDelegate委托的返回數據類型為string,參數為 int和 ...
  • 1、AOT適合場景 Aot適合工具類型的項目使用,優點禁止反編 ,第一次啟動快,業務型項目或者反射多的項目不適合用AOT AOT更新記錄: 實實在在經過實踐的AOT ORM 5.1.4.117 +支持AOT 5.1.4.123 +支持CodeFirst和非同步方法 5.1.4.129-preview1 ...
  • 總說周知,UWP 是運行在沙盒裡面的,所有許可權都有嚴格限制,和沙盒外交互也需要特殊的通道,所以從根本杜絕了 UWP 毒瘤的存在。但是實際上 UWP 只是一個應用模型,本身是沒有什麼許可權管理的,許可權管理全靠 App Container 沙盒控制,如果我們脫離了這個沙盒,UWP 就會放飛自我了。那麼有沒... ...
  • 目錄條款17:讓介面容易被正確使用,不易被誤用(Make interfaces easy to use correctly and hard to use incorrectly)限制類型和值規定能做和不能做的事提供行為一致的介面條款19:設計class猶如設計type(Treat class de ...
  • title: 從零開始:Django項目的創建與配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 後端開發 tags: Django WebDev Python ORM Security Deployment Op ...
  • 1、BOM對象 BOM:Broswer object model,即瀏覽器提供我們開發者在javascript用於操作瀏覽器的對象。 1.1、window對象 視窗方法 // BOM Browser object model 瀏覽器對象模型 // js中最大的一個對象.整個瀏覽器視窗出現的所有東西都 ...