欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Javascript中this綁定的3種方法與比較

 更新時間:2016年10月13日 08:43:38   作者:justjs  
大家都知道JS是一門動態(tài)語言,與傳統(tǒng)的c和c++最大的區(qū)別就是js是在運行時動態(tài)檢測值的類型和變化。this是js中的一個關鍵字,它代表當前作用域的上下文環(huán)境,而且隨著上下文的改變而動態(tài)變化。這篇文章我們將詳細介紹Javascript中綁定this的三種方法與簡單的比較。

介紹

this 可以說是 javascript 中最耐人尋味的一個特性,學習this 的第一步就是明白 this 既不是指向函數自身也不指向函數的作用域。 this 實際上是在函數被調用時發(fā)生的綁定,它指向什么地方完全取決于函數在哪里被調用。

為什么需要綁定this

this代指當前的上下文環(huán)境,在不經意間容易改變:

var info = "This is global info";
var obj = {
 info: 'This is local info',
 getInfo: getInfo
}
function getInfo() {
 console.log(this.info);
}
obj.getInfo() //This is local info

getInfo() //This is global info 當前上下文環(huán)境被修改了

在上面的例子中,我們在對象內部創(chuàng)建一個屬性getInfo,對全局作用域下的getInfo進行引用,而它的作用是打印當前上下文中info的值,當我們使用obj.getInfo進行調用時,它會打印出對象內部的info的值,此時this指向該對象。而當我們使用全局的函數時,它會打印全局環(huán)境下的info變量的值,此時this指向全局對象。

這個例子告訴我們:

     1、同一個函數,調用的方式不同,this的指向就會不同,結果就會不同。

     2、對象內部的屬性的值為引用類型時,this的指向不會一直綁定在原對象上。

其次,還有不經意間this丟失的情況:

var info = "This is global info";
var obj = {
 info: 'This is local info',
 getInfo: function getInfo() {
 console.log(this.info);

 var getInfo2 = function getInfo2() {
  console.log(this.info);
 }
 getInfo2();
 }
}
obj.getInfo();

//This is local info
//This is global info

上面的例子中,對象obj中定義了一個getInfo方法,方法內定義了一個新的函數,也希望拿到最外層的該對象的info屬性的值,但是事與愿違,函數內函數的this被錯誤的指向了window全局對象上面,這就導致了錯誤。

解決的方法也很簡單,在最外層定義一個變量,存儲當前詞法作用域內的this指向的位置,根據變量作用域的關系,之后的函數內部還能訪問這個變量,從而得到上層函數內部this的真正指向。

var info = "This is global info";
var obj = {
 info: 'This is local info',
 getInfo: function getInfo() {
 console.log(this.info);

 var self = this;  //將外層this保存到變量中
 var getInfo2 = function getInfo2() {
  console.log(self.info); //指向外層變量代表的this
 }
 getInfo2();
 }
}
obj.getInfo();

//This is local info
//This is local info

然而這樣也會有一些問題,上面的self變量等于重新引用了obj對象,這樣的話可能會在有些時候不經意間修改了整個對象,而且當需要取得多個環(huán)境下的this指向時,就需要聲明多個變量,不利于管理。

有一些方法,可以在不聲明類似于self這種變量的條件下,綁定當前環(huán)境下的上下文,確保編程內容的安全。

如何綁定this

1. call, apply

call和apply是定義在Function.prototype上的兩個函數,他們的作用就是修正函數執(zhí)行的上下文,也就是this的指向問題。

以call為例,上述anotherFun想要輸出local thing就要這樣修改:

...
var anotherFun = obj.getInfo;
anotherFun.call(obj) //This is local info

函數調用的參數:

      Function.prototype.call(context [, argument1, argument2 ])

      Function.prototype.apply(context [, [ arguments ] ])

從這里就可以看到,call和apply的第一參數是必須的,接受一個重新修正的上下文,第二個參數都是可選的,他們兩個的區(qū)別在于,call從第二個參數開始,接受傳入調用函數的值是一個一個單獨出現的,而apply是接受一個數組傳入。

function add(num1, num2, num3) {
 return num1 + num2 + num3;
}
add.call(null, 10, 20, 30); //60
add.apply(null, [10, 20, 30]); //60

當接受的context為undefined或null時,會自動修正為全局對象,上述例子中為window

2. 使用Function.prototype.bind進行綁定

ES5中在Function.prototype新增了bind方法,它接受一個需要綁定的上下文對象,并返回一個調用的函數的副本,同樣的,它也可以在后面追加參數,實現函數的柯里化。

Function.prototype.bind(context[, argument1, argument2])
//函數柯里化部分
function add(num1 ,num2) {
 return num1 + num2;
}
var anotherFun = window.add.bind(window, 10);
anotherFun(20); //30

同時,他返回一個函數的副本,并將函數永遠的綁定在傳入的上下文中。

...
var anotherFun = obj.getInfo.bind(obj)
anotherFun(); //This is local info

polyfill

polyfill是一種為了向下兼容的解決方案,在不支持ES5的老舊瀏覽器上,如何使用bind方法呢,就得需要使用舊的方法重寫一個bind方法。

if (!Function.prototype.bind) {
 Function.prototype.bind = function (obj) {
 var self = this;
 return function () {
 self.call(obj);
 }
 }
}

上面的寫法實現了返回一個函數,并且將上下文修正為傳入的參數,但是沒有實現柯里化部分。

...
Function.prototype.bind = function(obj) {
 var args = Array.prototype.slice.call(arguments, 1); 
 //記錄下所有第一次傳入的參數

 var self = this;
 return function () {
 self.apply(obj, args.concat(Array.prototype.slice.call(arguments)));
 }
}

當使用bind進行綁定之后,即不能再通過call,apply進行修正this指向,所以bind綁定又稱為硬綁定。

3. 使用new關鍵字進行綁定

在js中,函數有兩種調用方式,一種是直接進行調用,一種是通過new關鍵字進行構造調用。

function fun(){console.log("function called")}
//直接調用
fun() //function called
//構造調用
var obj = new fun() //function called

那普通的調用和使用new關鍵字的構造調用之間,又有哪些區(qū)別呢?

準確的來說,就是new關鍵字只是在調用函數的基礎上,多增加了幾個步驟,其中就包括了修正this指針到return回去的對象上。

var a = 5;
function Fun() {
 this.a = 10;
}
var obj = new Fun();
obj.a //10

幾種綁定方式的優(yōu)先級比較

以下面這個例子來進行幾種綁定狀態(tài)的優(yōu)先級權重的比較

var obj1 = {
 info: "this is obj1",
 getInfo: () => console.log(this.info)
}

var obj2 = {
 info: "this is obj2",
 getInfo: () => console.log(this.info)
}

1. call,apply和默認指向比較

首先很顯然,根據使用頻率來想,使用call和apply會比直接調用的優(yōu)先級更高。

obj1.getInfo() //this is obj1
obj2.getInfo() //this is obj2
obj1.getInfo.call(obj2) //this is obj2
obj2.getInfo.call(obj1) //this is obj1

使用call和apply相比于使用new呢?

這個時候就會出現問題了,因為我們沒辦法運行類似 new function.call(something)這樣的代碼。所以,我們通過bind方法返回一個新的函數,再通過new判斷優(yōu)先級。

var obj = {}
function foo(num){
 this.num = num;
}

var setNum = foo.bind(obj);
setNum(10);
obj.num //10

var obj2 = new setNum(20);
obj.num //10
obj2.num //20

通過這個例子我們可以看出來,使用new進行構造調用時,會返回一個新的對象,并將this修正到這個對象上,但是它并不會改變之前的對象內容。

那么問題來了,上面我們寫的bind的polyfill明顯不具備這樣的能力。而在MDN上有一個bind的polyfill方法,它的方法如下:

if (!Function.prototype.bind) { 
 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 ? 
    this : oThis || this, 
   aArgs.concat(Array.prototype.slice.call(arguments))); 
 }; 
 fNOP.prototype = this.prototype; 
 fBound.prototype = new fNOP(); 
 return fBound; 
 };
}

上面的polyfill首先判斷需要綁定的對象是否為函數,防止使用Function.prototype.bind.call(something)時,something不是一個函數造成未知錯誤。之后讓需要返回的fBound函數繼承自this,返回fBound。

特殊情況

當然,在某些情況下,this指針指向也存在一些意外。

箭頭函數

ES6中新增了一種定義函數方式,使用"=>"進行函數的定義,在它的內部,this的指針不會改變,永遠指向最外層的詞法作用域。

var obj = {
 num: 1,
 getNum: function () {
 return function () {
 //this丟失
 console.log(this.num); 
 //此處的this指向window
 }
 }
}
obj.getNum()(); //undefined

var obj2 = {
 num: 2,
 getNum: function () {
 return () => console.log(this.num); 
 //箭頭函數內部綁定外部getNum的this,外部this指向調用的對象
 }
}
obj2.getNum()(); //2

軟綁定

上面提供的bind方法可以通過強制修正this指向,并且再不能通過call,apply進行修正。如果我們希望即能有bind效果,但是也能通過call和apply對函數進行二次修正,這個時候就需要我們重寫一個建立在Function.prototype上的方法,我們給它起名為"軟綁定"。

if (!Function.prototype.softBind) {
 Function.prototype.softbind = function (obj) {
 var self = this;
 var args = Array.prototype.slice.call(arguments, 1);
 return function () {
 return self.apply((!this || this === (window || global)) ? 
   obj : this, 
   args.concat(Array.prototype.slice.call(arguments)));
 }
 }
}

bind,call的妙用

在平日里我們需要將偽數組元素變?yōu)檎5臄到M元素時,往往通過Array.prototype.slice方法,正如上面的實例那樣。將arguments這個對象變?yōu)檎嬲臄到M對象,使用 Array.prototype.slice.call(arguments)進行轉化.。但是,每次使用這個方法太長而且繁瑣。所以有時候我們就會這樣寫:

var slice = Array.prototype.slice;
slice(arguments);
//error

同樣的問題還出現在:

var qsa = document.querySelectorAll;
qsa(something);
//error

上面的問題就出現在,內置的slice和querySelectorAll方法,內部使用了this,當我們簡單引用時,this在運行時成為了全局環(huán)境window,當然會造成錯誤。我們只需要簡單的使用bind,就能創(chuàng)建一個函數副本。

var qsa = document.querySelectorAll.bind(document);
qsa(something);

同樣的,使用因為call和apply也是一個函數,所以也可以在它們上面調用bind方法。從而使返回的函數的副本本身就帶有修正指針的功能。

var slice = Function.prototype.call.bind(Array.prototype.slice);
slice(arguments);

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或工作能帶來一定的幫助,如果有疑問大家可以留言交流。

相關文章

  • js表格分頁實現代碼

    js表格分頁實現代碼

    js表格分頁實現代碼,需要的朋友可以參考下。
    2009-09-09
  • JS實現讓網頁背景圖片斜向移動的方法

    JS實現讓網頁背景圖片斜向移動的方法

    這篇文章主要介紹了JS實現讓網頁背景圖片斜向移動的方法,涉及javascript操作背景圖片特效的技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • JavaScript中對于SPA單頁面的理解

    JavaScript中對于SPA單頁面的理解

    這篇文章主要介紹了JavaScript中對于SPA單頁面的理解,單頁應用SPA是一種網絡應用程序或網站的模型,它通過動態(tài)重寫當前頁面來與用戶交互
    2023-02-02
  • JS實現圖片自動播放效果

    JS實現圖片自動播放效果

    這篇文章主要為大家詳細介紹了JS實現圖片自動播放效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • javascript實現5秒倒計時并跳轉功能

    javascript實現5秒倒計時并跳轉功能

    這篇文章主要為大家詳細介紹了javascript實現5秒倒計時并跳轉功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • JavaScript基礎篇(3)之Object、Function等引用類型

    JavaScript基礎篇(3)之Object、Function等引用類型

    這篇文章主要介紹了JavaScript基礎篇(3)之Object、Function等引用類型的相關資料,需要的朋友可以參考下
    2015-11-11
  • 解讀new?Object()和Object.create()的區(qū)別

    解讀new?Object()和Object.create()的區(qū)別

    這篇文章主要介紹了解讀new?Object()和Object.create()的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • JS co 函數庫的含義和用法實例總結

    JS co 函數庫的含義和用法實例總結

    這篇文章主要介紹了JS co 函數庫的含義和用法,結合實例形式總結分析了JS co 函數庫的基本含義、功能、用法及操作注意事項,需要的朋友可以參考下
    2020-04-04
  • JavaScript實現各種排序的代碼詳解

    JavaScript實現各種排序的代碼詳解

    這篇文章給大家介紹了js實現各種排序功能,包括冒泡排序,選擇排序和插入排序,代碼簡單易懂,非常不錯,具有參考借鑒價值,需要的的朋友參考下吧
    2017-08-08
  • javascript 終止函數執(zhí)行操作

    javascript 終止函數執(zhí)行操作

    本篇文章主要是對javascript 終止函數執(zhí)行的操作方法進行了介紹,需要的朋友可以過來參考下,希望對大家有所幫助
    2014-02-02

最新評論