成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

js中的this學(xué)習(xí)

roadtogeek / 3395人閱讀

摘要:考慮這個(gè)問(wèn)題還需要了解一個(gè)概念調(diào)用棧到達(dá)當(dāng)前執(zhí)行位置而被調(diào)用的所有方法的堆棧。之后只要調(diào)用函數(shù),就會(huì)調(diào)用函數(shù),綁定的值始終不變。就是中新增的函數(shù)箭頭函數(shù)。

在之前的對(duì)象原型的文章中,我們講到了在函數(shù)前面加new然后進(jìn)行調(diào)用之后發(fā)生的4件事情,當(dāng)時(shí)只把跟原型有關(guān)的東西介紹了一下?,F(xiàn)在我們來(lái)學(xué)習(xí)一下其他的內(nèi)容。

首先先來(lái)回顧一下會(huì)有哪四件事情發(fā)生吧:

一個(gè)全新的對(duì)象被創(chuàng)建

這個(gè)新的對(duì)象會(huì)被接入到原型鏈

這個(gè)新的對(duì)象被設(shè)置為函數(shù)調(diào)用的this綁定

除非函數(shù)有返回對(duì)象,否則這個(gè)被new調(diào)用的函數(shù)將自動(dòng)返回這個(gè)新的對(duì)象

這里有個(gè)新的東西this綁定,也就是接下來(lái)我們要介紹的東西啦。

第一個(gè)問(wèn)題就是this是什么?(以下回答摘自You-Dont-Know-JS)

this是在函數(shù)被調(diào)用的時(shí)候建立的一個(gè)綁定,指向的內(nèi)容完全由函數(shù)被調(diào)用的調(diào)用點(diǎn)來(lái)決定的。

簡(jiǎn)單點(diǎn)說(shuō),this就是一個(gè)綁定,指向一個(gè)內(nèi)容。

那么this指向的內(nèi)容又是什么呢?前面說(shuō)到這個(gè)內(nèi)容由函數(shù)被調(diào)用的調(diào)用點(diǎn)來(lái)決定。所謂的調(diào)用點(diǎn),就是函數(shù)在代碼中被調(diào)用的地方。也就是說(shuō),我們需要找到函數(shù)在哪里被調(diào)用,從而確定this指向的內(nèi)容。考慮這個(gè)問(wèn)題還需要了解一個(gè)概念:調(diào)用棧(到達(dá)當(dāng)前執(zhí)行位置而被調(diào)用的所有方法的堆棧)。
看段代碼來(lái)深入理解一下調(diào)用棧和調(diào)用點(diǎn)這兩個(gè)概念:

function foo() {
    // 調(diào)用棧是: `foo`
    // 調(diào)用點(diǎn)是global scope(全局作用域)
    console.log( "foo" );
    bar(); // <-- `bar`的調(diào)用點(diǎn)
}
function bar() {
    // 調(diào)用棧是: `foo` -> `bar`
    // 調(diào)用點(diǎn)位于`foo`
    console.log( "bar" );
    baz(); // <-- `baz`的調(diào)用點(diǎn)
}
function baz() {
    // 調(diào)用棧是: `foo` -> `bar` -> `baz`
    // 調(diào)用點(diǎn)位于`bar`
    console.log( "baz" );
}
foo(); // <-- `foo`的調(diào)用點(diǎn)

上面這個(gè)代碼跟注釋?xiě)?yīng)該已經(jīng)很清楚了解釋了調(diào)用棧和調(diào)用點(diǎn)這兩個(gè)概念了。

搞清楚這些概念之后,我們還是不知道this會(huì)指向什么。既然說(shuō)this指向的內(nèi)容完全由調(diào)用點(diǎn)決定,那么調(diào)用點(diǎn)又是怎么決定的呢?

還記得文章最開(kāi)始提到的東西么,關(guān)于new的4件事情,第三點(diǎn)講的是新對(duì)象被設(shè)置為函數(shù)調(diào)用的this綁定。
看下代碼:

   function foo(){
      this.a = a;
  }
  var bar = new foo(2); //調(diào)用foo函數(shù)來(lái)創(chuàng)建一個(gè)新對(duì)象bar
  console.log(bar.a);

使用new來(lái)調(diào)用函數(shù)foo的時(shí)候,我們創(chuàng)建了一個(gè)新對(duì)象bar并且把bar綁定到了foo()里面的this.這就是所謂的new綁定。

那么在JavaScript中,關(guān)于this綁定,除了new綁定,還有3種其它的規(guī)則:

默認(rèn)綁定

隱式綁定

顯示綁定

下面我們依次來(lái)一一介紹。

默認(rèn)綁定
看名字我們就能看出來(lái),這是最普通最基礎(chǔ)的綁定。一般來(lái)說(shuō),獨(dú)立函數(shù)調(diào)用的時(shí)候this就是默認(rèn)綁定。
來(lái)看個(gè)例子:

function foo(){
    console.log(this.a);
}
var a = 2;
foo(); //2

代碼很簡(jiǎn)單,我們主要關(guān)心的是this。我們先看結(jié)果:this綁定到了全局變量。
具體分析一下也很簡(jiǎn)單,這里的函數(shù)調(diào)用就是我們平常在使用的最簡(jiǎn)單的獨(dú)立函數(shù)的調(diào)用,跟前面介紹的規(guī)則也很符合。
這里有一個(gè)要注意的小細(xì)節(jié)就是如果是在嚴(yán)格模式下,默認(rèn)綁定的值會(huì)變成undefined。如果是非嚴(yán)格模式的話,就是綁定到全局變量了。

隱式綁定
這個(gè)規(guī)則一般是看函數(shù)調(diào)用的位置是否有上下文對(duì)象,或者說(shuō)是否被某個(gè)對(duì)象擁有或者包含。
通過(guò)代碼來(lái)深入理解一下:

 function foo(){
    console.log(this.a);
}
var obj = {
    a:2,
    foo:foo
};
obj.foo(); //2

代碼同樣很好理解,函數(shù)foo()作為引用屬性添加在對(duì)象obj里面,但這并不能說(shuō)明函數(shù)foo()屬于obj對(duì)象。但是從調(diào)用的位置上看,會(huì)使用obj這個(gè)對(duì)象來(lái)引用函數(shù)foo,那么函數(shù)在被調(diào)用的時(shí)候,是被obj這個(gè)對(duì)象擁有或者包含的。
簡(jiǎn)單點(diǎn)說(shuō),函數(shù)在被調(diào)用的時(shí)候,是通過(guò)對(duì)象來(lái)引用的,那么函數(shù)里的this就會(huì)綁定到這個(gè)對(duì)象上面。
再來(lái)看一個(gè)稍微復(fù)雜一點(diǎn)的例子:

 function foo(){
    console.log(this.a);
}
var obj = {
    a:1,
    foo:foo
};
var obj1 = {
    a:2,
    obj:obj
}
obj1.obj.foo(); //1

這里的話,我們會(huì)發(fā)現(xiàn)多了一個(gè)obj1這個(gè)對(duì)象,而且這個(gè)對(duì)象里有屬性a和對(duì)象obj。然后我們調(diào)用的時(shí)候會(huì)發(fā)現(xiàn)結(jié)果輸出的是obj里面的屬性a的值。
簡(jiǎn)單的結(jié)論就是,在多層的對(duì)象引用屬性中,只有最頂層或者說(shuō)最后一層才會(huì)影響調(diào)用位置。

顯式綁定
通過(guò)上面隱式綁定的規(guī)則介紹可以知道,它是通過(guò)對(duì)象間接綁定this的,那么很明顯顯式綁定就是直接的,或者說(shuō)就是強(qiáng)行指定我們想要讓this綁定的對(duì)象。那么怎么來(lái)進(jìn)行強(qiáng)行綁定呢?
一般來(lái)說(shuō),是使用函數(shù)的call()和apply()方法(絕大部分函數(shù)都會(huì)有這兩個(gè)方法)。
這兩個(gè)方法的作用都是一樣的,就是替換this指向。唯一不同的就是接收參數(shù)的方法不一樣。apply()方法接收兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)對(duì)象(也就是我們想要讓this指向的新的對(duì)象,不填的話就是全局對(duì)象),第二個(gè)參數(shù)一個(gè)參數(shù)數(shù)組。call()方法的話第一個(gè)參數(shù)跟apply是一樣的,但是后面要把傳遞的參數(shù)全部都列舉出來(lái)。
簡(jiǎn)單來(lái)看個(gè)例子:

function foo(){
   console.log(this.a);
   }
   var obj = {
   a:2
   };
   foo.call(obj); //2

最后一行代碼,函數(shù)foo調(diào)用了call方法,強(qiáng)行把this綁定到了obj對(duì)象上。

至此,關(guān)于this綁定的基礎(chǔ)的4種規(guī)則就介紹得差不多了,實(shí)際上有些規(guī)則在應(yīng)用的時(shí)候可能不那么盡如人意,我們依舊從代碼入手:

 function foo(){
     console.log(this.a);
 }
 var obj = {
    a:2,
    foo:foo
 };
 var bar = obj.foo;
 var a = 1;
 bar(); //1

一開(kāi)始我們可能都會(huì)覺(jué)得輸出的結(jié)果應(yīng)該是2。因?yàn)閎ar這個(gè)對(duì)象在創(chuàng)建的時(shí)候調(diào)用了obj里面的foo函數(shù)。但實(shí)際上只是另一個(gè)foo自己的引用。而且bar函數(shù)在調(diào)用的時(shí)候是作為獨(dú)立函數(shù)調(diào)用的,跟我們前面講的默認(rèn)綁定的規(guī)則很符合,所以這里的this就綁定到了全局對(duì)象。
這種情況在回調(diào)函數(shù)里更容易發(fā)生,比如下面的代碼:

 function foo(){
     console.log(this.a);
 }
 function doFoo(f){
    f();
 }
 var obj = {
    a:2,
    foo:foo
 };
 var a = 1;
 doFoo(obj.foo); //1

最后一行代碼實(shí)際上就是f = obj.foo,自然結(jié)果就跟上面是一樣的。
那么有什么方法可以解決這個(gè)問(wèn)題呢?
在顯示綁定中,有一個(gè)它的變種,我們稱之為硬綁定,可以解決上面的問(wèn)題。
繼續(xù)看代碼:

 function foo(){
     console.log(this.a);
 }
 var obj = {
    a:2
 };
 var bar = function(){
    foo.call(obj);
 }
 bar(); //2
 setTimeout(bar,1000);
 bar.call(window); //2

這段代碼解釋了硬綁定的工作原理:它創(chuàng)建了一個(gè)函數(shù)bar,然后在函數(shù)里面通過(guò)foo.call(..)強(qiáng)行把this綁定到了obj對(duì)象上面。之后只要調(diào)用函數(shù)bar,就會(huì)調(diào)用函數(shù)foo,綁定的值始終不變。

然后我們稍微改變一下,讓它變成一個(gè)可復(fù)用的幫助函數(shù):

 function foo(){
     console.log(this.a);
 }
 function bind(f,obj){
     return function(){
         return f.apply(obj,arguments);
     };
 }
 var obj = {
    a:2
 };
 var bar = bind(foo,obj);
 var b = bar(3);  
 console.log(b);  //2

由于硬綁定經(jīng)常被使用,所以它在ES5的時(shí)候就作為內(nèi)建工具了:Function.prototype.bind。上面的代碼就是bind方法的原理。
bind方法的作用和call和apply一樣,都是替換this指向,它的參數(shù)也和call一樣。不一樣的就是bind方法返回的是一個(gè)函數(shù)。

然后我們要介紹一個(gè)比較特殊的函數(shù),因?yàn)樗荒芨鶕?jù)前面介紹的4條規(guī)則來(lái)判斷this的指向。就是ES6中新增的函數(shù):箭頭函數(shù)(=>)。它是根據(jù)外層作用域或者全局作用域來(lái)決定this指向的。
看段代碼:

function foo(){
    return (a) => {
        console.log(this.a);
    };
}
var obj1 = {
    a:1
};
var obj2 = {
    a:2
};
var bar = foo.call(obj1);
bar.call(obj2);//1

foo()內(nèi)部創(chuàng)建的箭頭函數(shù)會(huì)捕獲調(diào)用時(shí)foo()的this。因?yàn)閒oo使用了call方法,所以foo()的this綁定到了obj1。然后bar對(duì)象被創(chuàng)建的時(shí)候引用了箭頭函數(shù),所以bar的this也被綁定到了obj1上面。而且箭頭函數(shù)的綁定是無(wú)法被修改的。所以最后輸出的結(jié)果是1而不是2。

最后,雖然我們已經(jīng)了解了this綁定的基本規(guī)則,但是如果說(shuō)我們找到了函數(shù)在哪里調(diào)用,然后又發(fā)現(xiàn)4種規(guī)則里有多種規(guī)則可以適用,那我們應(yīng)該選擇哪一種呢?

這就涉及到了這些規(guī)則的優(yōu)先級(jí):

首先看是不是有new調(diào)用,如果是的話就綁定到新創(chuàng)建的對(duì)象;

然后看是不是有call或者apply或者bind調(diào)用,如果是那就綁定到指定對(duì)象;

再之后看是不是由上下文調(diào)用,如果是就綁定到那個(gè)上下文對(duì)象;

最后的話就只剩下默認(rèn)綁定了(注意嚴(yán)格模式下是undefined,非嚴(yán)格模式下綁定到全局對(duì)象)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93700.html

相關(guān)文章

  • should.js源碼分析與學(xué)習(xí)

    摘要:結(jié)構(gòu)其中為整個(gè)項(xiàng)目入口,為中的類,負(fù)責(zé)對(duì)測(cè)試信息進(jìn)行記錄。通過(guò)拋出錯(cuò)誤而不是返回布爾值的方式來(lái)通知用戶,能夠更加明顯的通知用戶,也方便向上拋出異常進(jìn)行傳遞。 背景 為了研究與學(xué)習(xí)某些測(cè)試框架的工作原理,同時(shí)也為了完成培訓(xùn)中實(shí)現(xiàn)一個(gè)簡(jiǎn)單的測(cè)試框架的原因,我對(duì)should.js的代碼進(jìn)行了學(xué)習(xí)與分析,現(xiàn)在與大家來(lái)進(jìn)行交流下。 目錄 ext assertion.js assertion-e...

    Turbo 評(píng)論0 收藏0
  • 用Node.js實(shí)現(xiàn)機(jī)器學(xué)習(xí)中的K最近鄰分類算法

    摘要:簡(jiǎn)介源于數(shù)據(jù)挖掘的一個(gè)作業(yè),這里用來(lái)實(shí)現(xiàn)一下這個(gè)機(jī)器學(xué)習(xí)中最簡(jiǎn)單的算法之一算法最近鄰分類法。其實(shí)這些標(biāo)簽就對(duì)應(yīng)于機(jī)器學(xué)習(xí)中的特征這一重要概念,而訓(xùn)練我們識(shí)別的過(guò)程就對(duì)應(yīng)于泛化這一概念。 1. 簡(jiǎn)介 源于數(shù)據(jù)挖掘的一個(gè)作業(yè), 這里用Node.js來(lái)實(shí)現(xiàn)一下這個(gè)機(jī)器學(xué)習(xí)中最簡(jiǎn)單的算法之一k-nearest-neighbor算法(k最近鄰分類法)。 k-nearest-neighbor-cl...

    Cc_2011 評(píng)論0 收藏0
  • 案例學(xué)習(xí)總結(jié):原生JS實(shí)現(xiàn)表格排序

    摘要:最近在學(xué)習(xí)的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多知識(shí)點(diǎn)。二實(shí)現(xiàn)表格排序使用獲取數(shù)據(jù)之所以使用動(dòng)態(tài)獲取數(shù)據(jù),是為了使用文檔碎片綁定數(shù)據(jù)。 最近在學(xué)習(xí)js的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多JS知識(shí)點(diǎn)。在這里記錄一下此次學(xué)習(xí)過(guò)程。希望對(duì)大家也有所幫助。 完整的表格排序涉及了下列這些知識(shí)點(diǎn): call方法使用 sort方法深入 數(shù)據(jù)綁定 DOM映射 下面...

    ShevaKuilin 評(píng)論0 收藏0
  • 案例學(xué)習(xí)總結(jié):原生JS實(shí)現(xiàn)表格排序

    摘要:最近在學(xué)習(xí)的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多知識(shí)點(diǎn)。二實(shí)現(xiàn)表格排序使用獲取數(shù)據(jù)之所以使用動(dòng)態(tài)獲取數(shù)據(jù),是為了使用文檔碎片綁定數(shù)據(jù)。 最近在學(xué)習(xí)js的表格排序,沒(méi)想到看不起眼的表格排序?qū)嶋H上卻暗含了眾多JS知識(shí)點(diǎn)。在這里記錄一下此次學(xué)習(xí)過(guò)程。希望對(duì)大家也有所幫助。 完整的表格排序涉及了下列這些知識(shí)點(diǎn): call方法使用 sort方法深入 數(shù)據(jù)綁定 DOM映射 下面...

    lauren_liuling 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<