<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12
      代碼改變世界

      深入理解JavaScript系列(13):This? Yes,this!

      2012-01-17 08:50  湯姆大叔  閱讀(46679)  評論(78)    收藏  舉報(bào)

      介紹

      在這篇文章里,我們將討論跟執(zhí)行上下文直接相關(guān)的更多細(xì)節(jié)。討論的主題就是this關(guān)鍵字。實(shí)踐證明,這個(gè)主題很難,在不同執(zhí)行上下文中this的確定經(jīng)常會發(fā)生問題。

      許多程序員習(xí)慣的認(rèn)為,在程序語言中,this關(guān)鍵字與面向?qū)ο蟪绦蜷_發(fā)緊密相關(guān),其完全指向由構(gòu)造器新創(chuàng)建的對象。在ECMAScript規(guī)范中也是這樣實(shí)現(xiàn)的,但正如我們將看到那樣,在ECMAScript中,this并不限于只用來指向新創(chuàng)建的對象。

      英文翻譯: Dmitry A. Soshnikov在Stoyan Stefanov的幫助下
      發(fā)布: 2010-03-07
      http://dmitrysoshnikov.com/ecmascript/chapter-3-this/

      俄文原文: Dmitry A. Soshnikov
      修正: Zeroglif
      發(fā)布: 2009-06-28;
      更新:2010-03-07
      http://dmitrysoshnikov.com/ecmascript/ru-chapter-3-this/

      本文絕大部分內(nèi)容參考了:http://www.denisdeng.com/?p=900
      部分句子參考了:justin的中文翻譯

      讓我們更詳細(xì)的了解一下,在ECMAScript中this到底是什么?

      定義

      this是執(zhí)行上下文中的一個(gè)屬性:

      activeExecutionContext = {
      VO: {...},
      this: thisValue
      };

      這里VO是我們前一章討論的變量對象。

      this與上下文中可執(zhí)行代碼的類型有直接關(guān)系,this值在進(jìn)入上下文時(shí)確定,并且在上下文運(yùn)行期間永久不變。

      下面讓我們更詳細(xì)研究這些案例:

      全局代碼中的this

      在這里一切都簡單。在全局代碼中,this始終是全局對象本身,這樣就有可能間接的引用到它了。

      // 顯示定義全局對象的屬性
      this.a = 10; // global.a = 10
      alert(a); // 10

      // 通過賦值給一個(gè)無標(biāo)示符隱式
      b = 20;
      alert(this.b); // 20

      // 也是通過變量聲明隱式聲明的
      //
      因?yàn)槿稚舷挛牡淖兞繉ο笫侨謱ο笞陨?/span>
      var c = 30;
      alert(this.c); // 30

      函數(shù)代碼中的this

      在函數(shù)代碼中使用this時(shí)很有趣,這種情況很難且會導(dǎo)致很多問題。

      這種類型的代碼中,this值的首要特點(diǎn)(或許是最主要的)是它不是靜態(tài)的綁定到一個(gè)函數(shù)。

      正如我們上面曾提到的那樣,this是進(jìn)入上下文時(shí)確定,在一個(gè)函數(shù)代碼中,這個(gè)值在每一次完全不同。

      不管怎樣,在代碼運(yùn)行時(shí)的this值是不變的,也就是說,因?yàn)樗皇且粋€(gè)變量,就不可能為其分配一個(gè)新值(相反,在Python編程語言中,它明確的定義為對象本身,在運(yùn)行期間可以不斷改變)。

      var foo = {x: 10};

      var bar = {
      x: 20,
      test: function () {

      alert(this === bar); // true
      alert(this.x); // 20

      this = foo; // 錯(cuò)誤,任何時(shí)候不能改變this的值

      alert(this.x); // 如果不出錯(cuò)的話,應(yīng)該是10,而不是20

      }

      };

      // 在進(jìn)入上下文的時(shí)候
      //
      this被當(dāng)成bar對象
      //
      determined as "bar" object; why so - will
      //
      be discussed below in detail

      bar.test(); // true, 20

      foo.test = bar.test;

      // 不過,這里this依然不會是foo
      //
      盡管調(diào)用的是相同的function

      foo.test(); // false, 10

      那么,影響了函數(shù)代碼中this值的變化有幾個(gè)因素:

      首先,在通常的函數(shù)調(diào)用中,this是由激活上下文代碼的調(diào)用者來提供的,即調(diào)用函數(shù)的父上下文(parent context )。this取決于調(diào)用函數(shù)的方式。

      為了在任何情況下準(zhǔn)確無誤的確定this值,有必要理解和記住這重要的一點(diǎn)。正是調(diào)用函數(shù)的方式影響了調(diào)用的上下文中的this值,沒有別的什么(我們可以在一些文章,甚至是在關(guān)于javascript的書籍中看到,它們聲稱:“this值取決于函數(shù)如何定義,如果它是全局函數(shù),this設(shè)置為全局對象,如果函數(shù)是一個(gè)對象的方法,this將總是指向這個(gè)對象。–這絕對不正確”)。繼續(xù)我們的話題,可以看到,即使是正常的全局函數(shù)也會被調(diào)用方式的不同形式激活,這些不同的調(diào)用方式導(dǎo)致了不同的this值。

      function foo() {
      alert(this);
      }

      foo(); // global

      alert(foo === foo.prototype.constructor); // true

      // 但是同一個(gè)function的不同的調(diào)用表達(dá)式,this是不同的

      foo.prototype.constructor(); // foo.prototype

      有可能作為一些對象定義的方法來調(diào)用函數(shù),但是this將不會設(shè)置為這個(gè)對象。

      var foo = {
      bar: function () {
      alert(this);
      alert(this === foo);
      }
      };

      foo.bar(); // foo, true

      var exampleFunc = foo.bar;

      alert(exampleFunc === foo.bar); // true

      // 再一次,同一個(gè)function的不同的調(diào)用表達(dá)式,this是不同的

      exampleFunc(); // global, false

      那么,調(diào)用函數(shù)的方式如何影響this值?為了充分理解this值的確定,需要詳細(xì)分析其內(nèi)部類型之一——引用類型(Reference type)。

      引用類型(Reference type)

      使用偽代碼我們可以將引用類型的值可以表示為擁有兩個(gè)屬性的對象——base(即擁有屬性的那個(gè)對象),和base中的propertyName 。

      var valueOfReferenceType = {
      base: <base object>,
      propertyName: <property name>
      };

      引用類型的值只有兩種情況:

      1.     當(dāng)我們處理一個(gè)標(biāo)示符時(shí)
      2.     或一個(gè)屬性訪問器

      標(biāo)示符的處理過程在下一篇文章里詳細(xì)討論,在這里我們只需要知道,在該算法的返回值中,總是一個(gè)引用類型的值(這對this來說很重要)。

      標(biāo)識符是變量名,函數(shù)名,函數(shù)參數(shù)名和全局對象中未識別的屬性名。例如,下面標(biāo)識符的值:

      var foo = 10;
      function bar() {}

      在操作的中間結(jié)果中,引用類型對應(yīng)的值如下:

      var fooReference = {
      base: global,
      propertyName: 'foo'
      };

      var barReference = {
      base: global,
      propertyName: 'bar'
      };

      為了從引用類型中得到一個(gè)對象真正的值,偽代碼中的GetValue方法可以做如下描述:

      function GetValue(value) {

      if (Type(value) != Reference) {
      return value;
      }

      var base = GetBase(value);

      if (base === null) {
      throw new ReferenceError;
      }

      return base.[[Get]](GetPropertyName(value));

      }

      內(nèi)部的[[Get]]方法返回對象屬性真正的值,包括對原型鏈中繼承的屬性分析。

      GetValue(fooReference); // 10
      GetValue(barReference); // function object "bar"

      屬性訪問器都應(yīng)該熟悉。它有兩種變體:點(diǎn)(.)語法(此時(shí)屬性名是正確的標(biāo)示符,且事先知道),或括號語法([])。

      foo.bar();
      foo['bar']();

      在中間計(jì)算的返回值中,我們有了引用類型的值。

      var fooBarReference = {
      base: foo,
      propertyName: 'bar'
      };

      GetValue(fooBarReference); // function object "bar"

      引用類型的值與函數(shù)上下文中的this值如何相關(guān)?——從最重要的意義上來說。 這個(gè)關(guān)聯(lián)的過程是這篇文章的核心。 一個(gè)函數(shù)上下文中確定this值的通用規(guī)則如下:

      在一個(gè)函數(shù)上下文中,this由調(diào)用者提供,由調(diào)用函數(shù)的方式來決定。如果調(diào)用括號()的左邊是引用類型的值,this將設(shè)為引用類型值的base對象(base object),在其他情況下(與引用類型不同的任何其它屬性),這個(gè)值為null。不過,實(shí)際不存在this的值為null的情況,因?yàn)楫?dāng)this的值為null的時(shí)候,其值會被隱式轉(zhuǎn)換為全局對象。注:第5版的ECMAScript中,已經(jīng)不強(qiáng)迫轉(zhuǎn)換成全局變量了,而是賦值為undefined。

      我們看看這個(gè)例子中的表現(xiàn):

      function foo() {
      return this;
      }

      foo(); // global

      我們看到在調(diào)用括號的左邊是一個(gè)引用類型值(因?yàn)閒oo是一個(gè)標(biāo)示符)。

      var fooReference = {
      base: global,
      propertyName: 'foo'
      };

      相應(yīng)地,this也設(shè)置為引用類型的base對象。即全局對象。

      同樣,使用屬性訪問器:

      var foo = {
      bar: function () {
      return this;
      }
      };

      foo.bar(); // foo

      我們再次擁有一個(gè)引用類型,其base是foo對象,在函數(shù)bar激活時(shí)用作this。

      var fooBarReference = {
      base: foo,
      propertyName: 'bar'
      };

      但是,用另外一種形式激活相同的函數(shù),我們得到其它的this值。

      var test = foo.bar;
      test(); // global

      因?yàn)閠est作為標(biāo)示符,生成了引用類型的其他值,其base(全局對象)用作this 值。

      var testReference = {
      base: global,
      propertyName: 'test'
      };

      現(xiàn)在,我們可以很明確的告訴你,為什么用表達(dá)式的不同形式激活同一個(gè)函數(shù)會不同的this值,答案在于引用類型(type Reference)不同的中間值。

      function foo() {
      alert(this);
      }

      foo(); // global, because

      var fooReference = {
      base: global,
      propertyName: 'foo'
      };

      alert(foo === foo.prototype.constructor); // true

      // 另外一種形式的調(diào)用表達(dá)式

      foo.prototype.constructor(); // foo.prototype, because

      var fooPrototypeConstructorReference = {
      base: foo.prototype,
      propertyName: 'constructor'
      };

      另外一個(gè)通過調(diào)用方式動態(tài)確定this值的經(jīng)典例子:

      function foo() {
      alert(this.bar);
      }

      var x = {bar: 10};
      var y = {bar: 20};

      x.test = foo;
      y.test = foo;

      x.test(); // 10
      y.test(); // 20

      函數(shù)調(diào)用和非引用類型

      因此,正如我們已經(jīng)指出,當(dāng)調(diào)用括號的左邊不是引用類型而是其它類型,這個(gè)值自動設(shè)置為null,結(jié)果為全局對象。

      讓我們再思考這種表達(dá)式:

      (function () {
      alert(this); // null => global
      })();

      在這個(gè)例子中,我們有一個(gè)函數(shù)對象但不是引用類型的對象(它不是標(biāo)示符,也不是屬性訪問器),相應(yīng)地,this值最終設(shè)為全局對象。

      更多復(fù)雜的例子:

      var foo = {
      bar: function () {
      alert(this);
      }
      };

      foo.bar(); // Reference, OK => foo
      (foo.bar)(); // Reference, OK => foo

      (foo.bar = foo.bar)(); // global?
      (false || foo.bar)(); // global?
      (foo.bar, foo.bar)(); // global?

      為什么我們有一個(gè)屬性訪問器,它的中間值應(yīng)該為引用類型的值,在某些調(diào)用中我們得到的this值不是base對象,而是global對象?

      問題在于后面的三個(gè)調(diào)用,在應(yīng)用一定的運(yùn)算操作之后,在調(diào)用括號的左邊的值不在是引用類型。

      1. 第一個(gè)例子很明顯———明顯的引用類型,結(jié)果是,this為base對象,即foo。
      2. 在第二個(gè)例子中,組運(yùn)算符并不適用,想想上面提到的,從引用類型中獲得一個(gè)對象真正的值的方法,如GetValue。相應(yīng)的,在組運(yùn)算的返回中———我們得到仍是一個(gè)引用類型。這就是this值為什么再次設(shè)為base對象,即foo。
      3. 第三個(gè)例子中,與組運(yùn)算符不同,賦值運(yùn)算符調(diào)用了GetValue方法。返回的結(jié)果是函數(shù)對象(但不是引用類型),這意味著this設(shè)為null,結(jié)果是global對象。
      4. 第四個(gè)和第五個(gè)也是一樣——逗號運(yùn)算符和邏輯運(yùn)算符(OR)調(diào)用了GetValue 方法,相應(yīng)地,我們失去了引用而得到了函數(shù)。并再次設(shè)為global。

      引用類型和this為null

      有一種情況是這樣的:當(dāng)調(diào)用表達(dá)式限定了call括號左邊的引用類型的值, 盡管this被設(shè)定為null,但結(jié)果被隱式轉(zhuǎn)化成global。當(dāng)引用類型值的base對象是被活動對象時(shí),這種情況就會出現(xiàn)。

      下面的實(shí)例中,內(nèi)部函數(shù)被父函數(shù)調(diào)用,此時(shí)我們就能夠看到上面說的那種特殊情況。正如我們在第12章知道的一樣,局部變量、內(nèi)部函數(shù)、形式參數(shù)儲存在給定函數(shù)的激活對象中。

      function foo() {
      function bar() {
      alert(this); // global
      }
      bar(); // the same as AO.bar()
      }

      活動對象總是作為this返回,值為null——(即偽代碼的AO.bar()相當(dāng)于null.bar())。這里我們再次回到上面描述的例子,this設(shè)置為全局對象。

      有一種情況除外:如果with對象包含一個(gè)函數(shù)名屬性,在with語句的內(nèi)部塊中調(diào)用函數(shù)。With語句添加到該對象作用域的最前端,即在活動對象的前面。相應(yīng)地,也就有了引用類型(通過標(biāo)示符或?qū)傩栽L問器), 其base對象不再是活動對象,而是with語句的對象。順便提一句,它不僅與內(nèi)部函數(shù)相關(guān),也與全局函數(shù)相關(guān),因?yàn)閣ith對象比作用域鏈里的最前端的對象(全局對象或一個(gè)活動對象)還要靠前。

      var x = 10;

      with ({

      foo: function () {
      alert(this.x);
      },
      x: 20

      }) {

      foo(); // 20

      }

      // because

      var fooReference = {
      base: __withObject,
      propertyName: 'foo'
      };

      同樣的情況出現(xiàn)在catch語句的實(shí)際參數(shù)中函數(shù)調(diào)用:在這種情況下,catch對象添加到作用域的最前端,即在活動對象或全局對象的前面。但是,這個(gè)特定的行為被確認(rèn)為ECMA-262-3的一個(gè)bug,這個(gè)在新版的ECMA-262-5中修復(fù)了。這樣,在特定的活動對象中,this指向全局對象。而不是catch對象。

      try {
      throw function () {
      alert(this);
      };
      } catch (e) {
      e(); // ES3標(biāo)準(zhǔn)里是__catchObject, ES5標(biāo)準(zhǔn)里是global
      }

      // on idea

      var eReference = {
      base: __catchObject,
      propertyName: 'e'
      };

      // ES5新標(biāo)準(zhǔn)里已經(jīng)fix了這個(gè)bug,
      //
      所以this就是全局對象了
      var eReference = {
      base: global,
      propertyName: 'e'
      };

      同樣的情況出現(xiàn)在命名函數(shù)(函數(shù)的更對細(xì)節(jié)參考第15章Functions)的遞歸調(diào)用中。在函數(shù)的第一次調(diào)用中,base對象是父活動對象(或全局對象),在遞歸調(diào)用中,base對象應(yīng)該是存儲著函數(shù)表達(dá)式可選名稱的特定對象。但是,在這種情況下,this總是指向全局對象。

      (function foo(bar) {

      alert(this);

      !bar && foo(1); // "should" be special object, but always (correct) global

      })(); // global

      作為構(gòu)造器調(diào)用的函數(shù)中的this

      還有一個(gè)與this值相關(guān)的情況是在函數(shù)的上下文中,這是一個(gè)構(gòu)造函數(shù)的調(diào)用。

      function A() {
      alert(this); // "a"對象下創(chuàng)建一個(gè)新屬性
      this.x = 10;
      }

      var a = new A();
      alert(a.x); // 10

      在這個(gè)例子中,new運(yùn)算符調(diào)用“A”函數(shù)的內(nèi)部的[[Construct]] 方法,接著,在對象創(chuàng)建后,調(diào)用內(nèi)部的[[Call]] 方法。 所有相同的函數(shù)“A”都將this的值設(shè)置為新創(chuàng)建的對象。

      函數(shù)調(diào)用中手動設(shè)置this

      在函數(shù)原型中定義的兩個(gè)方法(因此所有的函數(shù)都可以訪問它)允許去手動設(shè)置函數(shù)調(diào)用的this值。它們是.apply和.call方法。他們用接受的第一個(gè)參數(shù)作為this值,this 在調(diào)用的作用域中使用。這兩個(gè)方法的區(qū)別很小,對于.apply,第二個(gè)參數(shù)必須是數(shù)組(或者是類似數(shù)組的對象,如arguments,反過來,.call能接受任何參數(shù)。兩個(gè)方法必須的參數(shù)是第一個(gè)——this。

      例如:

      var b = 10;

      function a(c) {
      alert(this.b);
      alert(c);
      }

      a(20); // this === global, this.b == 10, c == 20

      a.call({b: 20}, 30); // this === {b: 20}, this.b == 20, c == 30
      a.apply({b: 30}, [40]) // this === {b: 30}, this.b == 30, c == 40

      結(jié)論

      在這篇文章中,我們討論了ECMAScript中this關(guān)鍵字的特征(對比于C++ 和 Java,它們的確是特色)。我希望這篇文章有助于你準(zhǔn)確的理解ECMAScript中this關(guān)鍵字如何工作。同樣,我很樂意在評論中回到你的問題。

      其它參考

      同步與推薦

      本文已同步至目錄索引:深入理解JavaScript系列

      深入理解JavaScript系列文章,包括了原創(chuàng),翻譯,轉(zhuǎn)載等各類型的文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。

      主站蜘蛛池模板: 国产成人a∨激情视频厨房| 文中字幕一区二区三区视频播放| 久久国产成人午夜av影院| 视频一区视频二区视频三区 | 国产亚洲精品第一综合另类无码无遮挡又大又爽又黄的视频 | 亚洲日韩国产成网在线观看| 亚洲综合色婷婷中文字幕| 无遮挡高潮国产免费观看| 国产AV无码专区亚洲AV潘金链| 亚洲理论在线A中文字幕| 亚洲综合伊人五月天中文| 日韩精品区一区二区三vr| 99蜜桃在线观看免费视频网站 | 99久久无色码中文字幕| 亚洲国产精品无码一区二区三区| 玩弄放荡人妻少妇系列| 亚洲精品777| 中文字幕国产原创国产| 性做久久久久久久久| 妖精视频yjsp毛片永久| 国产影片AV级毛片特别刺激| 国精品午夜福利不卡视频| 亚洲国产精品久久久久4婷婷| 动漫AV纯肉无码AV电影网| 日本九州不卡久久精品一区| 亚洲欧美人成人综合在线播放| 狠狠躁夜夜躁无码中文字幕 | 成人啪精品视频网站午夜| 嫩江县| 国产亚洲精品在av| 天堂va亚洲va欧美va国产| 国产另类ts人妖一区二区| 色老头在线一区二区三区| 亚洲第一区二区快射影院| 亚洲AV无码一二区三区在线播放| 国产一区二区三区18禁| 亚洲精品国产电影| 自拍偷在线精品自拍偷99| 鲁丝一区二区三区免费| 国产成人综合色就色综合 | 久久中文字幕一区二区|