摘要:引用是從匿名函數(shù)內(nèi)部引用自身的唯一方法,不過(guò),最好的方法是避免使用匿名函數(shù),至少在那些需要引用自身的時(shí)候,使用命名函數(shù)或者表達(dá)式。
[翻譯]Chapter1 this or that
第一次翻譯,翻譯的不好,已經(jīng)再盡全力s去翻譯了,如果哪里看不明點(diǎn),請(qǐng)出門左轉(zhuǎn)下邊原文地址
英文原文點(diǎn)擊這里
javascript中最令人困惑的東西就是this關(guān)鍵字,它在每個(gè)函數(shù)作用域中都會(huì)自動(dòng)定義的一個(gè)特殊的標(biāo)識(shí)符,但是,它折磨著每一個(gè)javascript開發(fā)者,哪怕是有經(jīng)驗(yàn)的。
任何科技的充分進(jìn)步,都和魔法沒什么區(qū)別
javascript的this機(jī)制事實(shí)上并不是先進(jìn)的,但是開發(fā)者經(jīng)常按照他們自己的觀點(diǎn)把this解釋的復(fù)雜和混亂。毫無(wú)疑問(wèn)這是因?yàn)槿鄙偕羁痰睦斫狻?/p> why this
既然this機(jī)制讓開發(fā)者甚至是經(jīng)驗(yàn)豐富的程序員感到困惑。那為什么會(huì)是有用的,thia弊大于利嗎?在此之前,我們先跳過(guò)how的問(wèn)題,去審查一下why的問(wèn)題。
讓我們來(lái)說(shuō)明使用this的動(dòng)機(jī)和實(shí)用性。
function identify() { return this.name.toUpperCase(); } function speak() { var greeting = "Hello, I"m " + identify.call( this ); console.log( greeting ); } var me = { name: "Kyle" }; var you = { name: "Reader" }; identify.call( me ); // KYLE identify.call( you ); // READER speak.call( me ); // Hello, I"m KYLE speak.call( you ); // Hello, I"m READER
如果看不明白這個(gè)代碼塊,不要著急!我們很快會(huì)解釋,把這些問(wèn)題放到一邊,我們先來(lái)看關(guān)于why的問(wèn)題。
這個(gè)代碼塊允許identify()和speak()兩個(gè)函數(shù)分別被兩個(gè)對(duì)象me和you重用,而不是為每個(gè)對(duì)象多帶帶寫一個(gè)版本的函數(shù)。
通過(guò)依賴this。你可以用一種更明確的方式在環(huán)境對(duì)象中使用兩個(gè)方法。
function identify(context) { return context.name.toUpperCase(); } function speak(context) { var greeting = "Hello, I"m " + identify( context ); console.log( greeting ); } identify( you ); // READER speak( me ); // Hello, I"m KYLE
this機(jī)制提供一種更加優(yōu)雅的方式去傳遞一個(gè)對(duì)象引用,從而實(shí)現(xiàn)更加清楚的API設(shè)計(jì)和更簡(jiǎn)單的重用。
你用的模式越復(fù)雜,你就越能清晰的看到,傳遞上下文的時(shí)候一個(gè)顯示的參數(shù)要比傳遞一個(gè)this上下文更加麻煩。當(dāng)我們查看對(duì)象和原型的時(shí)候,你將能看到一個(gè)能夠自動(dòng)引用正確的函數(shù)集合上下文對(duì)象的實(shí)用性。
困惑點(diǎn)我們很快會(huì)開始解釋this實(shí)際上是如何工作的,但是首先要糾正一下錯(cuò)誤的觀念。
itself第一個(gè)經(jīng)常被解釋成困惑的是this指代函數(shù)本身,至少這倒是一個(gè)合理的解釋。
為什么你會(huì)需要在函數(shù)內(nèi)部引用函數(shù)自身,最可能的原因是遞歸(在函數(shù)內(nèi)部調(diào)用函數(shù)自身)或者事件處理的時(shí)候當(dāng)?shù)谝淮握{(diào)用時(shí)解綁事件。
開發(fā)者最新的js機(jī)制是把函數(shù)當(dāng)成一個(gè)對(duì)象一樣來(lái)引用(js里所有的函數(shù)都是對(duì)象),這樣會(huì)導(dǎo)致需要在函數(shù)互相調(diào)用之間存儲(chǔ)狀態(tài)(屬性值)。當(dāng)然這種機(jī)制可行的,也有一些有限的用處。這本書剩下的部分將會(huì)闡述許多其他的模式,以便更好的存儲(chǔ)函數(shù)對(duì)象之外的狀態(tài)。
但是等一會(huì),我們將會(huì)探索一種模式,來(lái)說(shuō)明this是如何不讓函數(shù)獲得自身的引用,就像我們之前假設(shè)的一樣。
考慮下邊的代碼,我們嘗試去跟蹤一下foo函數(shù)被調(diào)用了多少次。
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called this.count++; } foo.count = 0; var i; for (i=0; i<10; i++) { if (i > 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // how many times was `foo` called? console.log( foo.count ); // 0 -- WTF?
foo.count依然是0,即使經(jīng)過(guò)了四次console之后我們能清晰的看到foo事實(shí)上調(diào)用了四次,這個(gè)讓人失望的源頭在于對(duì)this是什么的解釋太字面。
當(dāng)代碼執(zhí)行到foo.count = 0的時(shí)候,確實(shí),它給foo函數(shù)對(duì)象添加了一個(gè)屬性count,但是this.count只是在函數(shù)內(nèi)部引用,this并不是指向所有其他的函數(shù)對(duì)象,即使屬性名稱相同,因?yàn)閠his所在的對(duì)象不一樣,所以困惑就產(chǎn)生了。
一個(gè)有責(zé)任的開發(fā)者應(yīng)該問(wèn)到這一點(diǎn):如果我增加一個(gè)count屬性但是這個(gè)屬性并不是我想要的,那我是否增加了。事實(shí)上,如果開發(fā)者更深的挖掘,她會(huì)發(fā)現(xiàn)她創(chuàng)建了一個(gè)變量count,這個(gè)變量當(dāng)前的值是NAN,一旦她注意到這個(gè)奇怪的結(jié)果,她將有一系列的疑問(wèn),全局對(duì)象是什么,為什么這個(gè)值是NAN而不是數(shù)值。(在foo中打印count,會(huì)顯示出NAN)。
有責(zé)任的開發(fā)者不應(yīng)該在這一點(diǎn)停止而是應(yīng)當(dāng)深入挖掘?yàn)槭裁催@個(gè)引用的表現(xiàn)沒有想預(yù)想的那樣,去回答這個(gè)棘手但是很重要的問(wèn)題。許多開發(fā)者簡(jiǎn)單的避免這個(gè)問(wèn)題,并且采取一些其他的解決辦法,比如創(chuàng)建另一個(gè)對(duì)象去存儲(chǔ)count這個(gè)屬性:
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called data.count++; } var data = { count: 0 }; var i; for (i=0; i<10; i++) { if (i > 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // how many times was `foo` called? console.log( data.count ); // 4
雖然這個(gè)確實(shí)解決了問(wèn)題,但是不幸的是它忽略了真正的問(wèn)題,不能理解this到底是什么以及她是如何工作的,而是回到了一個(gè)更加熟悉的舒適環(huán)境,詞法作用域(靜態(tài)作用域)。
詞法作用域是一個(gè)完美而有用的機(jī)制,我不會(huì)以任何方式輕視它,但是,不斷的猜測(cè)如何使用this,但是通常會(huì)帶來(lái)錯(cuò)誤,退回到詞法作用域去解決問(wèn)題并不是一個(gè)好原因。
為了在一個(gè)函數(shù)對(duì)象引用自身,this有著顯而易見的不足,你通常需要通過(guò)指向詞法標(biāo)識(shí)符(變量)去引用函數(shù)對(duì)象。
function foo() { foo.count = 4; // `foo` refers to itself } setTimeout( function(){ // anonymous function (no name), cannot // refer to itself }, 10 );
在第一個(gè)方法中,稱為命名函數(shù),foo是一個(gè)引用,可以被用來(lái)在函數(shù)內(nèi)部引用函數(shù)本身。
但是第二個(gè)例子,這個(gè)沒有名稱標(biāo)識(shí)的函數(shù)是通過(guò)setTimeout執(zhí)行回調(diào)(所以叫匿名函數(shù)),所以,這個(gè)時(shí)候沒有合適的方法通過(guò)函數(shù)名引用函數(shù)對(duì)象自身。
上邊是舊的用法,現(xiàn)在已經(jīng)棄用,一個(gè)函數(shù)中的arguments.callee引用指向當(dāng)前執(zhí)行的函數(shù)的函數(shù)對(duì)象。this引用是從匿名函數(shù)內(nèi)部引用自身的唯一方法,不過(guò),最好的方法是避免使用匿名函數(shù),至少在那些需要引用自身的時(shí)候,使用命名函數(shù)或者表達(dá)式。而arguments.callee已經(jīng)被棄用,不建議使用。
所以,另外一個(gè)解決方法解決我們運(yùn)行的例子是我們?cè)诿總€(gè)地方用foo這個(gè)標(biāo)識(shí)符作為函數(shù)對(duì)象的引用,而不是this。
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called foo.count++; } foo.count = 0; var i; for (i=0; i<10; i++) { if (i > 5) { foo( i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // how many times was `foo` called? console.log( foo.count ); // 4
然而這個(gè)方法依然側(cè)重于this的實(shí)際理解,并且依賴于foo的詞法作用域中的變量。
還有另外一種方法更關(guān)注this在foo函數(shù)對(duì)象中實(shí)際指向的問(wèn)題。
function foo(num) { console.log( "foo: " + num ); // keep track of how many times `foo` is called // Note: `this` IS actually `foo` now, based on // how `foo` is called (see below) this.count++; } foo.count = 0; var i; for (i=0; i<10; i++) { if (i > 5) { // using `call(..)`, we ensure the `this` // points at the function object (`foo`) itself foo.call( foo, i ); } } // foo: 6 // foo: 7 // foo: 8 // foo: 9 // how many times was `foo` called? console.log( foo.count ); // 4
避免用this,我們擁抱這種方法,我們將會(huì)稍微解釋一下這個(gè)技術(shù)是如何更完整的工作的,所以,如果你仍然有一點(diǎn)困惑,別著急。
its scope第二個(gè)常見的錯(cuò)誤觀點(diǎn)是認(rèn)為this的意思是以某種形式指代函數(shù)作用域,這是一個(gè)讓人困惑的問(wèn)題,因?yàn)樵谝欢ㄒ饬x上這是有道理的,但是另一方面,這是很讓人誤導(dǎo)的。
要明確的是,在任何情況下,this并不是指代函數(shù)的詞法作用域,在內(nèi)部,作用域是一個(gè)類似可以訪問(wèn)每個(gè)標(biāo)識(shí)符屬性的對(duì)象,但是這個(gè)作用域?qū)ο笤趈s代碼中是不能訪問(wèn)的,他是引擎內(nèi)部執(zhí)行的一部分。
下邊代碼嘗試去跨越代碼的邊界去使用this隱式的指向函數(shù)作用域。
function foo() { var a = 2; this.bar(); } function bar() { console.log( this.a ); } foo(); //undefined
這個(gè)代碼塊里有不止一個(gè)錯(cuò)誤,似乎它可能得出想要的結(jié)果,這個(gè)你看到的代碼。。。(原文這里嘲諷了這塊代碼)。
首先,你想通過(guò)this.bar()來(lái)引用bar方法,當(dāng)運(yùn)行起來(lái)無(wú)疑會(huì)出事故,我們要盡快解釋為何報(bào)錯(cuò),調(diào)用bar()最自然的方式是省略this,只對(duì)標(biāo)識(shí)符進(jìn)行詞法引用。
然而,開發(fā)者嘗試使用this的目的是在foo和bar兩個(gè)函數(shù)之間創(chuàng)造一個(gè)橋梁,是的bar可以訪問(wèn)到foo內(nèi)部作用域中的變量,但是并沒有這樣的橋梁,你不能用this引用去查找詞法作用域下的一些東西,這是不可能的。
每當(dāng)你感覺你想嘗試把慈父作用域的查找和this混合的時(shí)候,記得,這樣的橋是不存在的。
what this拋開那些不正確的解釋,現(xiàn)在我們把注意力,轉(zhuǎn)移到this機(jī)制是如何工作的。
我們之前說(shuō)過(guò),this是運(yùn)行的時(shí)候綁定而不是聲明的時(shí)候綁定,它是基于函數(shù)調(diào)用情況下的上下文,this綁定和函數(shù)生命的位置沒有關(guān)系,而是與函數(shù)的調(diào)用方式有關(guān)系。
當(dāng)一個(gè)函數(shù)被調(diào)用,將會(huì)創(chuàng)建一個(gè)激活記錄,也就是所謂的執(zhí)行上下文。該記錄包含了函數(shù)調(diào)用的位置信息(堆棧),以及函數(shù)是如何被調(diào)用的,還有參數(shù)是如何傳遞的等等,這個(gè)記錄的其中一個(gè)屬性是this引用,將會(huì)在函數(shù)執(zhí)行的期間被使用。
在下一章,我們要學(xué)習(xí)去找一個(gè)函數(shù)的調(diào)用點(diǎn)去確定在函數(shù)執(zhí)行的時(shí)候是如何綁定this的。
回顧this綁定對(duì)于沒有花時(shí)間去搞懂這個(gè)機(jī)制具體如何工作的js開發(fā)者來(lái)說(shuō)是一個(gè)恒定不斷的困難。來(lái)自stackoverfolw的回答者的猜測(cè),試錯(cuò)和盲目的復(fù)制粘貼并不是利用這種機(jī)制的有效方法。
學(xué)習(xí)this,首先要去了解this不是什么,盡管任何的假設(shè)或者誤解都可能導(dǎo)致你。。。this既不是函數(shù)本身的引用,也不是函數(shù)詞法作用域的引用。
this實(shí)際上是函數(shù)調(diào)用時(shí)進(jìn)行的綁定,它的引用綁定完全由函數(shù)的調(diào)用位置所決定。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/82472.html
摘要:運(yùn)行規(guī)則根據(jù)的運(yùn)作原理,我們可以看到,的值和調(diào)用棧通過(guò)哪些函數(shù)的調(diào)用運(yùn)行到調(diào)用當(dāng)前函數(shù)的過(guò)程以及如何被調(diào)用有關(guān)。 1. this的誕生 假設(shè)我們有一個(gè)speak函數(shù),通過(guò)this的運(yùn)行機(jī)制,當(dāng)使用不同的方法調(diào)用它時(shí),我們可以靈活的輸出不同的name。 var me = {name: me}; function speak() { console.log(this.name); }...
摘要:基本概念首先,函數(shù)不能存儲(chǔ)的值,指向哪里,取決于調(diào)用它的對(duì)象。如果沒有這個(gè)對(duì)象,那默認(rèn)就是調(diào)用非嚴(yán)格模式下。也就是說(shuō)是在運(yùn)行的時(shí)候定義的,不是在綁定的時(shí)候定義的。 基本概念 首先,函數(shù)不能存儲(chǔ)this的值,this指向哪里,取決于調(diào)用它的對(duì)象。如果沒有這個(gè)對(duì)象,那默認(rèn)就是window調(diào)用(非嚴(yán)格模式下)。也就是說(shuō)this是在運(yùn)行的時(shí)候定義的,不是在綁定的時(shí)候定義的。 funct...
摘要:為什么會(huì)存在跨域問(wèn)題同源策略由于出于安全考慮,瀏覽器規(guī)定不能操作其他域下的頁(yè)面,不能接受其他域下的請(qǐng)求不只是,引用非同域下的字體文件,還有引用非同域下的圖片,也被同源策略所約束只要協(xié)議域名端口有一者不同,就被視為非同域。 showImg(https://segmentfault.com/img/remote/1460000017093859?w=1115&h=366); Why 為什么...
摘要:在我們的程序中有很多變量標(biāo)識(shí)符,我們現(xiàn)在或者將來(lái)將使用它。當(dāng)我們使用時(shí),如果并沒有找到這個(gè)變量,在非嚴(yán)格模式下,程序會(huì)默認(rèn)幫我們?cè)谌謩?chuàng)建一個(gè)變量。詞法作用域也就是說(shuō),變量的作用域就是他聲明的時(shí)候的作用域。 作用域 定義 首先我們來(lái)想想作用域是用來(lái)干什么的。在我們的程序中有很多變量(標(biāo)識(shí)符identifier),我們現(xiàn)在或者將來(lái)將使用它。那么多變量,我咋知道我有沒有聲明或者定義過(guò)他呢,...
摘要:一到底是一門什么樣的計(jì)算機(jī)編程語(yǔ)言表里不一表面上是動(dòng)態(tài)解釋執(zhí)行的腳本語(yǔ)言,實(shí)際上它是一門編譯語(yǔ)言。與眾不同與傳統(tǒng)語(yǔ)言不同的是,它不是提前編譯的,編譯記過(guò)也不能在分布式系統(tǒng)中進(jìn)行移植。千篇一律引擎進(jìn)行編譯的步驟和傳統(tǒng)的編譯語(yǔ)言非常相似。 一、JavaScript到底是一門什么樣的計(jì)算機(jī)編程語(yǔ)言? JavaScript表里不一:表面上是動(dòng)態(tài)、解釋執(zhí)行的腳本語(yǔ)言,實(shí)際上它是一門編譯語(yǔ)言。 ...
閱讀 1648·2021-10-14 09:42
閱讀 3915·2021-09-07 09:59
閱讀 1367·2019-08-30 15:55
閱讀 638·2019-08-30 11:17
閱讀 3399·2019-08-29 16:06
閱讀 569·2019-08-29 14:06
閱讀 3196·2019-08-28 18:14
閱讀 3718·2019-08-26 13:55