乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      JavaScript 中的 this 并不難

       新進(jìn)小設(shè)計(jì) 2020-03-07

      js中的this,如果沒(méi)有深入的學(xué)習(xí)了解,那么this將會(huì)是讓開(kāi)發(fā)人員很頭疼的問(wèn)題。下面,我就針對(duì)this,來(lái)做一個(gè)學(xué)習(xí)筆記。

      1.調(diào)用位置

      在理解this的綁定過(guò)程之前,首先要理解調(diào)用位置:調(diào)用位置就是函數(shù)在代碼中被調(diào)用的位置(而不是聲明的位置)。只有分析好調(diào)用位置,才能明白這個(gè)this到底引用的是什么?
      尋找調(diào)用位置,最重要的是分析調(diào)用棧(就是為了到達(dá)當(dāng)前執(zhí)行位置所調(diào)用的所有函數(shù))。調(diào)用位置就在當(dāng)前正在執(zhí)行的前一個(gè)調(diào)用中。
      下面舉例說(shuō)明:

      function baz (){
          // 當(dāng)前調(diào)用棧是:baz
          //因此,當(dāng)前調(diào)用位置是全局作用域
          console.log("baz");
          bar();// <-- bar的調(diào)用位置
      }
      function bar(){
          // 當(dāng)前調(diào)用棧是 baz -> bar
          // 因此,當(dāng)前調(diào)用位置在 baz 中
          console.log('bar');
          foo();// <-- foo 的調(diào)用位置
      }
      function foo(){
          // 當(dāng)前調(diào)用棧是baz -> bar -> foo
          // 因此,當(dāng)前調(diào)用位置在bar中
          console.log("foo");
      }
      baz(); // <-- baz的調(diào)用位置

      2.綁定規(guī)則

      2.1 默認(rèn)綁定

      首先看一下最常用的函數(shù)調(diào)用類(lèi)型:獨(dú)立函數(shù)調(diào)用。可以把這條規(guī)則看作是無(wú)法應(yīng)用其他規(guī)則時(shí)的默認(rèn)規(guī)則。
      如下例:

      function foo() {
          console.log(this.a);
      }
      var a = 2;
      foo(); // 2
      // 在本代碼中,foo() 是直接使用不帶任何修飾的函數(shù)引用進(jìn)行調(diào)用的,因此只能使用默認(rèn)綁定,無(wú)法應(yīng)用其他規(guī)則。
      // 如果使用嚴(yán)格模式,那么全局對(duì)象無(wú)法使用默認(rèn)綁定,因此this會(huì)綁定到undefined。

      2.2 隱式綁定

      另一條需要考慮的規(guī)則是調(diào)用位置是否有上下文對(duì)象,或者說(shuō)是否被某個(gè)對(duì)象擁有或者包含。舉例來(lái)說(shuō):

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

      首先要注意的是foo()的聲明方式,以及之后是如何被當(dāng)做引用屬性添加到obj的。但是無(wú)論是直接在obj中定義還是先定義再添加為引用屬性,這個(gè)函數(shù)嚴(yán)格來(lái)說(shuō)都不屬于obj 對(duì)象。

      然而,調(diào)用位置會(huì)使用obj的上下文來(lái)引用函數(shù),因此,可以說(shuō)函數(shù)被調(diào)用時(shí)obj對(duì)象“擁有”或者“包含”它。

      無(wú)論如何稱(chēng)呼這個(gè)模式,當(dāng)foo()被調(diào)用時(shí),落腳點(diǎn)確實(shí)指向obj對(duì)象。當(dāng)函數(shù)引用有上下文對(duì)象時(shí),隱式綁定規(guī)則會(huì)把函數(shù)調(diào)用中的this綁定到這個(gè)上下文對(duì)象。所以this.a和obj.a是一樣的。

      對(duì)象屬性引用鏈中只有最后一層會(huì)影響調(diào)用位置。上代碼:

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

      2.2.1 隱式丟失

      一個(gè)最常見(jiàn)額this綁定問(wèn)題就是被隱式綁定的函數(shù)會(huì)丟失綁定對(duì)象,會(huì)應(yīng)用默認(rèn)綁定,從而把this綁定到全局對(duì)象或者undefined上,取決于是否是嚴(yán)格模式??聪旅娴拇a:

      function foo() {
          console.log(this.a);
      }
      var obj = {
          a:2,
          foo
      };
      var bar = obj.foo; // 函數(shù)別名!
      var a = "What?"; // a 是全局對(duì)象的屬性
      bar();//"What?"

      雖然bar是obj.foo 的一個(gè)引用,但是實(shí)際上,它引用的是foo函數(shù)本身,因此此時(shí)的bar()其實(shí)是一個(gè)不帶任何修飾符的函數(shù)調(diào)用,因此應(yīng)用了默認(rèn)綁定。
      下面舉一個(gè)回調(diào)函數(shù)中隱式丟失的例子:

      function foo() {
          console.log(this.a);
      }
      function doFoo(fn){
          // fn 其實(shí)引用的是foo
          fn(); // <- 調(diào)用位置
      }
      var obj = {
          a:2,
          foo
      };
      var a = "What?"; // a 是全局對(duì)象的屬性
      doFoo(obj.foo);//"What?"

      參數(shù)傳遞其實(shí)就是一種隱式賦值,傳入函數(shù)時(shí)也會(huì)被隱式賦值,所以結(jié)果和上一個(gè)例子一樣。

      2.3 顯示綁定

      在上面隱式綁定的時(shí)候,必須在一個(gè)對(duì)象內(nèi)部包含一個(gè)指向函數(shù)的屬性,并通過(guò)這個(gè)屬性間接引用函數(shù),從而把this間接(隱式)的綁定到這個(gè)對(duì)象上。

      如果我們不想在對(duì)象內(nèi)部包含函數(shù)引用,而想在某個(gè)對(duì)象上強(qiáng)制調(diào)用函數(shù),該如何處理?

      基本上大部分函數(shù)會(huì)包含call(..)和apply(..)方法。但是有的時(shí)候JavaScript的宿主環(huán)境有時(shí)候會(huì)提供一些非常特殊的函數(shù),可能沒(méi)有這兩個(gè)方法,但是極為罕見(jiàn)。

      這兩個(gè)函數(shù)的第一個(gè)參數(shù)是一個(gè)對(duì)象,會(huì)把這個(gè)對(duì)象綁定到this,接著在調(diào)用函數(shù)時(shí)指定這個(gè)this。因?yàn)檫@種方式可以直接指定this的綁定對(duì)象,因此我們稱(chēng)之為顯示綁定。

      上代碼:

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

      通過(guò)foo.call(...),我們可以在調(diào)用foo的時(shí)候強(qiáng)制將this綁定在obj上。

      如果從傳入了一個(gè)原始值(字符串類(lèi)型、布爾類(lèi)型或者數(shù)字類(lèi)型)來(lái)當(dāng)做this的綁定對(duì)象,這個(gè)原始值會(huì)被轉(zhuǎn)換成它的對(duì)象形式(也就是new String(...)、new Boolean(...)或者new Number(...)),這通常稱(chēng)為“裝箱”。

      從this的綁定的角度來(lái)說(shuō),call(...)和apply(...)是一樣的,他們的區(qū)別體現(xiàn)在其他的參數(shù)上。

      不過(guò)上述的代碼不能很好地解決我們提出的丟失綁定的問(wèn)題。

      2.3.1 硬綁定

      不過(guò)顯示綁定的一個(gè)變種可以解決這個(gè)問(wèn)題。
      上代碼:

      function foo() {
          console.log(this.a);
      }
      var obj = {
          a:2
      };
      var bar = function(){
          foo.call(obj);
      }
      var a = '123';
      bar(); // 2 
      setTimeout(bar,10); // 2
      bar.call(window); // 2 此時(shí)硬綁定的bar不能修改foo的this。foo總會(huì)在obj上調(diào)用。

      由于硬綁定是一種非常常用的模式,所以在ES5中提供了內(nèi)置方法 bind ,它的用法如下:

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

      2.3.2 API調(diào)用的“上下文”

      第三方庫(kù)的許多函數(shù),以及javaScript語(yǔ)言和宿主環(huán)境中的許多新的內(nèi)置函數(shù),都提供了一個(gè)可選的參數(shù),通常被稱(chēng)為上下文,其作用和bind一樣,確?;卣{(diào)函數(shù)使用指定的this。上代碼:

      function foo (item){
          console.log(item,this.id);
      }
      var obj = {
          id:"cool"
      };
      
      // 調(diào)用foo()時(shí)把this綁定到obj
      [1,2,3].forEach(foo,obj);
      // 1 cool 2 cool 3 cool

      2.4 new綁定

      js中使用new可以構(gòu)造一個(gè)新的對(duì)象,使用new來(lái)調(diào)用函數(shù),或者說(shuō)發(fā)生構(gòu)造函數(shù)調(diào)用時(shí),會(huì)自動(dòng)執(zhí)行下面的操作。

      1. 創(chuàng)建(或者說(shuō)構(gòu)造)一個(gè)全新的對(duì)象;

      2. 創(chuàng)建這個(gè)新對(duì)象會(huì)被執(zhí)行[[原型]]連接;

      3. 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的this;

      4. 如果函數(shù)沒(méi)有返回其他對(duì)象,那么new表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象。

      上代碼:

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

      使用new來(lái)調(diào)用foo()時(shí),會(huì)構(gòu)造一個(gè)新對(duì)象并綁定到foo()調(diào)用中的this上。

      3.優(yōu)先級(jí)。

      • 毫無(wú)疑問(wèn),默認(rèn)綁定的優(yōu)先級(jí)是最低的。

      那么隱式綁定和顯示綁定誰(shuí)更高?上代碼:

      function foo() { 
           console.log( this.a );
      }
      var obj1 = { 
           a: 2,
           foo: foo
      };
      var obj2 = { 
            a: 3,
           foo: foo 
      };
      obj1.foo(); // 2 
      obj2.foo(); // 3
      obj1.foo.call( obj2 ); // 3 
      obj2.foo.call( obj1 ); // 2

      可以看到,顯式綁定優(yōu)先級(jí)更高,也就是說(shuō)在判斷時(shí)應(yīng)當(dāng)先考慮是否可以應(yīng)用顯式綁定。

      • new 綁定 VS 隱式綁定:

      function foo(something) { 
           this.a = something;
      }
      var obj1 = { 
           foo: foo
      };
      var obj2 = {};
      obj1.foo( 2 ); 
      console.log( obj1.a ); // 2
      obj1.foo.call( obj2, 3 ); 
      console.log( obj2.a ); // 3
      var bar = new obj1.foo( 4 ); 
      console.log( obj1.a ); // 2 
      console.log( bar.a ); // 4

      可以看到 new 綁定比隱式綁定優(yōu)先級(jí)高。

      • new 綁定 VS 顯示綁定:

      new 和 call/apply 無(wú)法一起使用,因此無(wú)法通過(guò) new foo.call(obj1) 來(lái)直接
      進(jìn)行測(cè)試。但是我們可以使用硬綁定來(lái)測(cè)試它倆的優(yōu)先級(jí)。

      function foo(something) { 
       this.a = something;
      }
      var obj1 = {};
      var bar = foo.bind( obj1 ); 
      bar( 2 );
      console.log( obj1.a ); // 2
      var baz = new bar(3); 
      console.log( obj1.a ); // 2 
      console.log( baz.a ); // 3

      可以看到,new 修改了硬綁定(到 obj1 的)調(diào)用 bar(..) 中的 this。因?yàn)槭褂昧薾ew 綁定,我們得到了一個(gè)名字為 baz 的新對(duì)象,并且 baz.a 的值是 3。

      總結(jié)

      現(xiàn)在我們可以根據(jù)優(yōu)先級(jí)來(lái)判斷函數(shù)在某個(gè)調(diào)用位置應(yīng)用的是哪條規(guī)則??梢园凑障旅娴?br>順序來(lái)進(jìn)行判斷:
      1.函數(shù)是否在 new 中調(diào)用(new 綁定)?如果是的話(huà) this 綁定的是新創(chuàng)建的對(duì)象。

      var bar = new foo()

      2.函數(shù)是否通過(guò) call、apply(顯式綁定)或者硬綁定調(diào)用?如果是的話(huà),this 綁定的是指定的對(duì)象

      var bar = foo.call(obj2)

      3.函數(shù)是否在某個(gè)上下文對(duì)象中調(diào)用(隱式綁定)?如果是的話(huà),this 綁定的是那個(gè)上下文對(duì)象。

      var bar = obj1.foo()

      4.如果都不是的話(huà),使用默認(rèn)綁定。如果在嚴(yán)格模式下,就綁定到 undefined,否則綁定到全局對(duì)象。

      var bar = foo()

      4.箭頭函數(shù)

      之前介紹的四條規(guī)則已經(jīng)可以包含所有正常的函數(shù)。但是 ES6 中介紹了一種無(wú)法使用這些規(guī)則的特殊函數(shù)類(lèi)型:箭頭函數(shù)。
      箭頭函數(shù)并不是使用 function 關(guān)鍵字定義的,而是使用被稱(chēng)為“胖箭頭”的操作符 => 定義的。箭頭函數(shù)不使用 this 的四種標(biāo)準(zhǔn)規(guī)則,而是根據(jù)外層(函數(shù)或者全局)作用域來(lái)決定 this。

      function foo() {
           // 返回一個(gè)箭頭函數(shù)
           return (a) => {
           //this 繼承自 foo()
           console.log( this.a ); 
       };
      }
      var obj1 = { 
           a:2
      };
      var obj2 = { 
           a:3
       };
      var bar = foo.call( obj1 );
      bar.call( obj2 ); // 2, 不是 3 !

      foo() 內(nèi)部創(chuàng)建的箭頭函數(shù)會(huì)捕獲調(diào)用時(shí) foo() 的 this。由于 foo() 的 this 綁定到 obj1,
      bar(引用箭頭函數(shù))的 this 也會(huì)綁定到 obj1,箭頭函數(shù)的綁定無(wú)法被修改。(new 也不
      行!)

      箭頭函數(shù)最常用于回調(diào)函數(shù)中,例如事件處理器或者定時(shí)器:

      function foo() { 
           setTimeout(() => {
           // 這里的 this 在此法上繼承自 foo()
           console.log( this.a ); 
       },100);
      }
      var obj = { 
           a:2
      };
      foo.call( obj ); // 2

      箭頭函數(shù)可以像 bind(..) 一樣確保函數(shù)的 this 被綁定到指定對(duì)象,此外,其重要性還體
      現(xiàn)在它用更常見(jiàn)的詞法作用域取代了傳統(tǒng)的 this 機(jī)制。實(shí)際上,在 ES6 之前我們就已經(jīng)
      在使用一種幾乎和箭頭函數(shù)完全一樣的模式。

      function foo() {
           var self = this; // lexical capture of this 
           setTimeout( function(){
           console.log( self.a );
           }, 100 );
      } 
      var obj = {
           a: 2
      };
      foo.call( obj ); // 2

      雖然 self = this 和箭頭函數(shù)看起來(lái)都可以取代 bind(..),但是從本質(zhì)上來(lái)說(shuō),它們想替
      代的是 this 機(jī)制。

      參考資料

      • 《你不知道的javaScript》---上卷

      你好!我是 JHCan333,公眾號(hào):愛(ài)生活的前端狗的作者。公眾號(hào)專(zhuān)注前端工程師方向,包括但不限于技術(shù)提高、職業(yè)規(guī)劃、生活品質(zhì)、個(gè)人理財(cái)等方面,會(huì)持續(xù)發(fā)布優(yōu)質(zhì)文章,從各個(gè)方面提升前端開(kāi)發(fā)的幸福感。關(guān)注公眾號(hào),我們一起向前走!

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多