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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...