沒搞錯吧!js寫了那麼多年,this仍是會搞錯!沒搞錯,javascript就是回搞錯!javascript
…………html
文章來源自——周陸軍的我的網站:http://zhoulujun.cn/zhoulujun/html/webfront/ECMAScript/jsBase/2016_0329_7729.htmljava
在寫java的時候,this用錯了,idea都會直接報錯!程序員
好比……web
可是,js,……idea,心有餘而力不足了……面試
在面向對象編程裏有兩個重要的概念:一個是類,一個是實例化的對象,類是一個抽象的概念,用個形象的比喻表述的話,類就像一個模具,而實例化對象就是經過這個模具製造出來的產品,實例化對象纔是咱們須要的實實在在的東西,類和實例化對象有着很密切的關係,可是在使用上類的功能是絕對不能取代實例化對象,就像模具和模具製造的產品的關係,兩者的用途是不相同的。編程
有上面代碼咱們能夠看到,this指針在java語言裏只能在實例化對象裏使用,this指針等於這個被實例化好的對象,而this後面加上點操做符,點操做符後面的東西就是this所擁有的東西,例如:姓名,工做,手,腳等等。閉包
其實javascript裏的this指針邏輯上的概念也是實例化對象,這一點和java語言裏的this指針是一致的,可是javascript裏的this指針卻比java裏的this難以理解的多,究其根本緣由我我的以爲有三個緣由:app
緣由一:javascript是一個函數編程語言,怪就怪在它也有this指針,說明這個函數編程語言也是面向對象的語言,說的具體點,javascript裏的函數是一個高階函數,編程語言裏的高階函數是能夠做爲對象傳遞的,同時javascript裏的函數還有能夠做爲構造函數,這個構造函數能夠建立實例化對象,結果致使方法執行時候this指針的指向會不斷髮生變化,很難控制。編程語言
緣由二:javascript裏的全局做用域對this指針有很大的影響,由上面java的例子咱們看到,this指針只有在使用new操做符後纔會生效,可是javascript裏的this在沒有進行new操做也會生效,這時候this每每會指向全局對象window。
緣由三:javascript裏call和apply操做符能夠隨意改變this指向,這看起來很靈活,可是這種不合常理的作法破壞了咱們理解this指針的本意,同時也讓寫代碼時候很難理解this的真正指向
上面的三個緣由都違反了傳統this指針使用的方法,它們都擁有有別於傳統this原理的理解思路,而在實際開發裏三個緣由又每每會交織在一塊兒,so,this,雲裏霧裏了……
入門書:professionnal Javascript for web devolopers,——高級的說法是這樣的:
this老是指向調用該方法的對象!
var name="zhoulujun";
function say(){ console.log(this.name) } say(); //zhoulujun
在script標籤裏咱們能夠直接使用this指針,this指針(指向window對象,結果)就是window對象,即便使用三等號它們也是相等的。全局做用域經常會干擾咱們很好的理解javascript語言的特性,這種干擾的本質就是:
在javascript語言裏全局做用域能夠理解爲window對象,記住window是對象而不是類,也就是說window是被實例化的對象,這個實例化的過程是在頁面加載時候由javascript引擎完成的,整個頁面裏的要素都被濃縮到這個window對象,由於程序員沒法經過編程語言來控制和操做這個實例化過程,因此開發時候咱們就沒有構建這個this指針的感受,經常會忽視它,這就是干擾咱們在代碼裏理解this指針指向window的情形。
這裏this指向window對象,因此this.name->zhoulujun!
當執行 say函數的時候, JavaScript 會建立一個 Execute context (執行上下文),執行上下文中就包含了 say函數運行期所須要的全部信息。 Execute context 也有本身的 Scope chain, 當函數運行時, JavaScript 引擎會首先從用 say函數的做用域鏈來初始化執行上下文的做用域鏈。
這方面的知識,建議參考:
http://blog.csdn.net/wangxiaohu__/article/details/7260668
http://www.jb51.net/article/30706.htm
這裏能夠大體記一下:
var myObj={ name:"zhoulujun", fn:function(){ console.log(this.name) } }; myObj.fn();
這裏的this指向obj,由於fn()運行在obj裏面……
而後再來看……
var name="zhoulujun"; function say(){ console.log(this.name) console.log(this) } say(); function say2(){ var site="zhoulujun.cn"; console.log(this.site); } say2();
myObj2={ site:"zhoulujun.cn", fn:function(){ console.log(this.site) } }
這裏的this指向的是對象myObj2,由於你調用這個fn是經過myObj2.fn()執行的,那天然指向就是對象myObj2,這裏再次強調一點,this的指向在函數建立的時候是決定不了的,在調用的時候才能決定,誰調用的就指向誰,必定要搞清楚這個。
而後,咱們更深刻(受不了 …………
myObj3={ site:"zhoulujun.cn", andy:{ site:"www.zhoulujun.cn", fn:function(){ console.log(this.site) } } }; myObj3.andy.fn();
這裏一樣也是對象Object點出來的,可是一樣this並無執行它,那你確定會說我一開始說的那些不就都是錯誤的嗎?其實也不是,只是一開始說的不許確,接下來我將補充一句話,我相信你就能夠完全的理解this的指向的問題。
若是,你實在理解不了,就這麼樣背下來吧!
狀況1:若是一個函數中有this,可是它沒有被上一級的對象所調用,那麼this指向的就是window,這裏須要說明的是在js的嚴格版中this指向的不是window,可是咱們這裏不探討嚴格版的問題,你想了解能夠自行上網查找。
狀況2:若是一個函數中有this,這個函數有被上一級的對象所調用,那麼this指向的就是上一級的對象。
狀況3:若是一個函數中有this,這個函數中包含多個對象,儘管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象,若是不相信,那麼接下來咱們繼續看幾個例子。
這樣既對了嗎??深刻點(就受不了了……討厭……
myObj3={ site:"zhoulujun.cn", andy:{ site:"www.zhoulujun.cn", fn:function(){ console.log(this) console.log(this.site) } } }; // myObj3.andy.fn(); var fn=myObj3.andy.fn; fn();
其實,這裏的 fn等價於
fn:function(age){
console.log(this.name+age);
}
下面咱們來聊聊函數的定義方式:聲明函數和函數表達式
咱們最上面第一個案例定義函數的方式在javascript語言稱做聲明函數,第二種定義函數的方式叫作函數表達式,這兩種方式咱們一般認爲是等價的,可是它們實際上是有區別的,而這個區別經常會讓咱們混淆this指針的使用,咱們再看看下面的代碼:
爲何say能夠執行,say3不能夠?那是由於:
爲何say3打印結果是undefined,我在前文裏講到了undefined是在內存的棧區已經有了變量的名稱,可是沒有棧區的變量值,同時堆區是沒有具體的對象,這是javascript引擎在預加載掃描變量定義所致,可是ftn01的打印結果很使人意外,既然打印出完成的函數定義了,並且代碼並無按順序執行,這隻能說明一個問題:
在javascript語言經過聲明函數方式定義函數,javascript引擎在預處理過程裏就把函數定義和賦值操做都完成了,在這裏我補充下javascript裏預處理的特性,其實預處理是和執行環境相關,在上篇文章裏我講到執行環境有兩大類:全局執行環境和局部執行環境,執行環境是經過上下文變量體現的,其實這個過程都是在函數執行前完成,預處理就是構造執行環境的另外一個說法,總而言之預處理和構造執行環境的主要目的就是明確變量定義,分清變量的邊界,可是在全局做用域構造或者說全局變量預處理時候對於聲明函數有些不一樣,聲明函數會將變量定義和賦值操做同時完成,所以咱們看到上面代碼的運行結果。因爲聲明函數都會在全局做用域構造時候完成,所以聲明函數都是window對象的屬性,這就說明爲何咱們無論在哪裏聲明函數,聲明函數最終都是屬於window對象的緣由了。
http://www.zhoulujun.cn/zhoulujun/html/java/javaBase/7704.html
其實在javascript語言裏任何匿名函數都是屬於window對象,它們也都是在全局做用域構造時候完成定義和賦值,可是匿名函數是沒有名字的函數變量,可是在定義匿名函數時候它會返回本身的內存地址,若是此時有個變量接收了這個內存地址,那麼匿名函數就能在程序裏被使用了,由於匿名函數也是在全局執行環境構造時候定義和賦值,因此匿名函數的this指向也是window對象,因此上面代碼執行時候fn的this都是指向window,由於javascript變量名稱無論在那個做用域有效,堆區的存儲的函數都是在全局執行環境時候就被固定下來了,變量的名字只是一個指代而已。
相似的狀況(面試題喜歡這麼考!)……好比:
this都是指向實例化對象,前面講到那麼多狀況this都指向window,就是由於這些時候只作了一次實例化操做,而這個實例化都是在實例化window對象,因此this都是指向window。咱們要把this從window變成別的對象,就得要讓function被實例化,那如何讓javascript的function實例化呢?答案就是使用new操做符。
再來看 構造函數:
function User(){ this.name="zhoulujun"; console.log(this); } var andy=new User(); console.log(andy.name)
why andy 的name 是 zhoulujun,那是:由於:
new關鍵字能夠改變this的指向,將這個this指向對象andy,
那andy何時又成了思密達,oh,no,is Object?
由於用了new關鍵字就是建立一個對象實例(重要的事情默讀三遍)
咱們這裏用變量andy建立了一個User用戶實例(至關於複製了一份User到對象andy裏面),此時僅僅只是建立,並無執行,而調用這個函數User的是對象andy,那麼this指向的天然是對象andy,那麼爲何對象User中會有name,由於你已經複製了一份User函數到對象andy中,用了new關鍵字就等同於複製了一份。
java 程序猿: Class user=new User();似曾相識木有……
function既是函數又能夠表示對象,function是函數時候還能當作構造函數,javascript的構造函數我常認爲是把類和構造函數合二爲一,固然在javascript語言規範裏是沒有類的概念,可是我這種理解能夠做爲構造函數和普通函數的一個區別,這樣理解起來會更加容易些。
下面我貼出在《javascript高級編程》裏對new操做符的解釋:
new操做符會讓構造函數產生以下變化:
1. 建立一個新對象;
2. 將構造函數的做用域賦給新對象(所以this就指向了這個新對象);
3. 執行構造函數中的代碼(爲這個新對象添加屬性);
4. 返回新對象
……
媽的:讀的那麼拗口,不明覺厲…………看圖……還不
不明白……
var myObj5={ name:"andy" }; var myObj6=new Object(); myObj6.name="andy"; function say5(name){ console.log(name) } var say6=new Function("name","console.log(name)"); console.log(myObj5) console.log(myObj6) say5("andy"); say6("andy");
還不明白,就請奶奶買塊豆腐,撞死算了……
第四點也要着重講下,記住構造函數被new操做,要讓new正常做用最好不能在構造函數裏寫return,沒有return的構造函數都是按上面四點執行,有了return狀況就複雜了
return這王八蛋……
那麼我這樣呢……
does it have to be like this?Tell me why(why),is there something I have missed?
Tell me why(why),cos I don't understand…………
那是由於……because of u?no return……
因此:若是返回的是基本類型,就會丟掉…只能返回Object類型……typeof xx ===「object」
看到called 沒有?什麼鬼!!
其實new關鍵字會建立一個空的對象,而後會自動調用一個函數apply方法,將this指向這個空對象,這樣的話函數內部的this就會被這個空的對象替代。
var a={ name:"andy", site:"zhoulujun.cn", fn:function(age){ console.log(this.name+age); } }; var b={ name:"zhoulujun", site:"www.zhoulujun.cn", fn:function(age){ console.log(this.name+age); } }; a.fn(2); //andy2 a.fn.call(b,2) //zhoulujun2 a.fn.apply(b,[2])//zhoulujun2
固然,還有bind……
…………
寫到這裏,都看不下去,邏輯有點混亂,有的是從前輩哪裏引用的。
改天有時間整理下,而後,在去講下閉包(……closer
參考文章:
http://blog.jobbole.com/81018/
http://blog.jobbole.com/74110/
http://www.cnblogs.com/aaronjs/archive/2011/09/02/2164009.html#commentform
http://www.codeceo.com/article/javascript-this-pointer.html
文章來源自——周陸軍的我的網站:http://zhoulujun.cn/zhoulujun/html/webfront/ECMAScript/jsBase/2016_0329_7729.html