javascript 當中的
this
是一個用於指向當前上下文對象
的關鍵字。在面向對象編程及平常開發當中咱們常常與其打交道,初學javscript的朋友很是容易誤入歧途從而理解錯誤。javascript
在個人深刻貫徹閉包思想一章中其實已經講了不少關於環境棧(即上下文對象)的相關內容,但內容過於冗長,不便閱讀,時間充足的同窗能夠去看下,但這篇中,咱們仍是獨立開來,下面我總結了幾條有關上下文的知識,但願能幫助你們。java
首先須要聲明的是這些術語的問題,其實咱們常說的環境棧,調用棧,上下文棧 等等不少民間自創術語,它們形容的都是一個東東,即環境棧
(這裏我就拿第一個詞來形容了,你們知道就好)面試
javascript代碼塊有三種運行環境分別是:編程
1.全局(Global code)redux
默認執行環境,也就是沒有任何函數包裹在window下執行的代碼片斷。segmentfault
//window var a = 12; (function(){ a = 13; }())
就好比這兩段代碼前者在全局window下執行了一個新建變量與賦值的操做。後者是一個匿名自調函數,它的上下文必定是Global,因此,不管在哪裏運行它,它只能訪問自身的活動對象以及Global的變量對象。數組
2.函數(Function code)瀏覽器
function fun(){ a = 24; return a; } var foo = new Function("var a = 12; return a;"); foo(); fun();
除外的Function它表示在函數當中執行的代碼.閉包
3.eval (Eval code)app
以上兩種狀況指的是在特定情境下的代碼片斷,可是不包括另外一種狀況,也就是eval環境 (這裏應指動態環境,由於據我所知以目前JS版本,只有eval能夠建立動態執行環境吧? )。
var codeStr = "var a = 12"; eval(codeStr);
以上三種執行上下文構成了執行上下文棧(ECS),在整個棧中有依次排列的上下文(Execution Context),位於最底層的是Global全局環境,上面依次是函數執行環境。而eval則會在運行中javascript引擎臨時去建立,也就是沒有在預編譯前幾毫秒進行編譯代碼整合。
在每一個EC當中存有幾個特別重要的屬性,變量對象,做用域鏈,this指向
變量對象(Variable Object,VO)
對象的引用地址
,但不是在詞法階段定義的,而是在運行時綁定的,它引用的值取決於函數調用時的各類條件
。只取決於函數的調用方式。變量對象裏面存儲了在上下文中定義的變量和函數聲明。在函數上下文環境下不能直接使用變量對象,而是要等到執行流
進入當前環境下來激活,被稱做活動對象
注:上面說了在一個EC中它們是屬於特別重要的屬性(其構成了最基本的做用域規則),也就是說還有其餘javascript內部實現須要的屬性,它們大概包含函數的調用方式,在哪裏被調用等等.
好了整理一下,`在這一小節裏面,咱們大體瞭解了ECS及EC的概念以及this與他們的關係及其this的定義.
// Global Context var obj = { fun:()=>()=>{ console.log(this); }, foo:()=>{ console.log(this); } } obj.foo.name = "tongtong"; /* 把foo函數轉賦給context變量,這時foo函數對象身上的屬性是具有`複合類型條件`的,即引用. */ var context = obj.foo; //經過返回的函數來二次調用,一樣屬於全局環境下,由於內部函數調用時已經不屬於obj對象了. obj.fun()(); //window context(); //window context.name; // "tongtong"
函數不是簡單類型值,是複合類型
,咱們同樣能夠用.
操做符像對待對象同樣爲它添加屬性與方法。與其餘兩位小夥伴(Array Object)不一樣的是,它能夠調用呀。只要調用環境變化了,this固然也就隨波逐流。
以上這個例子,咱們須要注意,在把它們賦給其餘變量時,因爲它的執行環境是會變化的,即每次調用都會刷新this
,對於新手來講,這點必定要搞清楚。
//global Context //no.1 (function(){ console.log(this); }()) //window //no.2 var x = 12; var foo = function(){ this.x = 12; } new foo(); x //12
foo()
。而 obj.foo(); new foo()
它們的this是有意義的。外部環境
. 使用new來調用函數時,先改變其上下文環境,在對其構造函數進行調用。對順序不清楚的同窗,請自行查閱關於 javascript生成實例步驟.這兩個方法位於Function.prototype,擁有更改上下文的功能,前者需手動填寫函數參數,後者能夠經過數組來表示。
// Global Context var obj = {}; //call var foo = function(num){ console.log(this); console.log(num + 8); }; foo.call(12); // obj 20 foo(12); //window 12 //apply var fun = function(){ console.log(this); console.log(Object.prototype.slice.call(arguments).redux((x,y)=> x + y); ) } fun.call(obj,[12,34,56,78]); fun(null,[12,34,56,78]);
他們都把第一個參數做爲上下文
並調用。那麼如何更改一個函數的上下文調用環境呢?咱們以bind來簡單模擬一下。
var _bind = function(fun,context){ //取第一個以後的參數列表. var params = [].slice.call(arguments).slice(2); //給咱們的函數指定上下文對象。 context.fun = fun; return function(){ var result = params.concat(Array.from(arguments)); return context.fun(result)}; }
至於call,apply方法的模擬實現,這裏就不說了,感興趣的童鞋能夠去網上搜索一下.
var a = 「111」; var b = 「222」; function test() { var a = 「333」; var b = 「444」; alert(this.a); alert(this.b); } var test= new test() ; alert(test.a); alert(test.b);
在建立一個實例時,它的類
內部的this必然是這個實例。他的順序大體是
這裏咱們只須要知道它先建立上下文,再進行調用構造函數.
var obj = { Pagination:function(){ return this; } }; new obj.Pagination(); // Pagination {}
查找this的機制與做用域一致,都是就近原則。
setTimeout(function(){ return this; },0); //window
setTimeout回調的執行環境下的this對象是window,幾乎瀏覽器的全部原生方法回調函數的this都是window,可是若是咱們在其場景使用回調,這個this就變成不可預期
的了,由於咱們不知道此函數,到底在哪裏執行了.
離它最近
的那個對象爲止。我特別但願你們能在文章中給我提出意見,文中內容都是博主我的理解,算不上是對的,因此我建議你們只作參考,或看其餘相似文章作一個本身的總結,每一個人理解javascript都不同,若是不看底層編譯原理,在這個層面之上,咱們只能靠這種方法理解了。。。可是記住,凡事都要本身求證的,別人說的是他人的觀點,本身理解出來的纔是最適合你的.