摘要:當中的是一個用于指向當前上下文對象的關鍵字。創建實例時的構造函數中的,永遠指向那個實例后對象,不是外部環境使用來調用函數時,先改變其上下文環境,在對其構造函數進行調用。
上下文對象概念javascript 當中的 this是一個用于指向當前上下文對象的關鍵字。在面向對象編程及日常開發當中我們經常與其打交道,初學javscript的朋友非常容易誤入歧途從而理解錯誤。
在我的深入貫徹閉包思想一章中其實已經講了很多關于環境棧(即上下文對象)的相關內容,但內容過于冗長,不便閱讀,時間充足的同學可以去看下,但這篇中,我們還是獨立開來,下面我總結了幾條有關上下文的知識,希望能幫助大家。
首先需要聲明的是這些術語的問題,其實我們常說的環境棧,調用棧,上下文棧 等等很多民間自創術語,它們形容的都是一個東東,即環境棧(這里我就拿第一個詞來形容了,大家知道就好)
執行上下文棧 (Execution context stack, ECS)javascript代碼塊有三種運行環境分別是:
1.全局(Global code)
默認執行環境,也就是沒有任何函數包裹在window下執行的代碼片段。
//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)
以上兩種情況指的是在特定情境下的代碼片段,但是不包括另一種情況,也就是eval環境 (這里應指動態環境,因為據我所知以目前JS版本,只有eval可以創建動態執行環境吧? )。
var codeStr = "var a = 12"; eval(codeStr);
以上三種執行上下文構成了執行上下文棧(ECS),在整個棧中有依次排列的上下文(Execution Context),位于最底層的是Global全局環境,上面依次是函數執行環境。而eval則會在運行中javascript引擎臨時去創建,也就是沒有在預編譯前幾毫秒進行編譯代碼整合。
在每個EC當中存有幾個特別重要的屬性,變量對象,作用域鏈,this指向
變量對象(Variable Object,VO)
變量
函數的聲明
函數的參數
作用域鏈(Scope Chain)
構成作用域的開山鼻祖? (確切的說應該是詞法作用域,但不能排除eval創建的動態環境)
this 指向
沒錯,它儲存一個對象的引用地址,但不是在詞法階段定義的,而是在運行時綁定的,它引用的值取決于函數調用時的各種條件。只取決于函數的調用方式。
變量對象里面存儲了在上下文中定義的變量和函數聲明。在函數上下文環境下不能直接使用變量對象,而是要等到執行流進入當前環境下來激活,被稱作活動對象
注:上面說了在一個EC中它們是屬于特別重要的屬性(其構成了最基本的作用域規則),也就是說還有其他javascript內部實現需要的屬性,它們大概包含函數的調用方式,在哪里被調用等等.
好了整理一下,`在這一小節里面,我們大致了解了ECS及EC的概念以及this與他們的關系及其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
在閉包中this是指向window的. 如若不然呢?它沒有任何調用者,它是自調,只能是window,也可以這樣理解 : 所有以函數調用的方式this一定是window,即foo()。而 obj.foo(); new foo()它們的this是有意義的。
創建實例時的構造函數中的this,永遠指向那個實例后對象,不是外部環境. 使用new來調用函數時,先改變其上下文環境,在對其構造函數進行調用。對順序不清楚的同學,請自行查閱關于 javascript生成實例步驟.
call 與 apply這兩個方法位于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必然是這個實例。他的順序大致是
創建上下文環境
執行構造函數
把構造函數的prototype,放到實例的原型上
這里我們只需要知道它先創建上下文,再進行調用構造函數.
var obj = { Pagination:function(){ return this; } }; new obj.Pagination(); // Pagination {}
查找this的機制與作用域一致,都是就近原則。
setTimeout(function(){ return this; },0); //window
setTimeout回調的執行環境下的this對象是window,幾乎瀏覽器的所有原生方法回調函數的this都是window,但是如果我們在其場景使用回調,這個this就變成不可預期的了,因為我們不知道此函數,到底在哪里執行了.
憶之獲this是執行時決定的,也可以說函數調用時,決定的。
EC中附帶的三類對象(非全部),其中this作為其中一員,每次執行流進入EC時,它都有可能會更新。而this的改變正是依賴這種現象。
this使用就近原則,從當前環境向外延伸,直至找到離它最近的那個對象為止。
列表項目
題外話我特別希望大家能在文章中給我提出意見,文中內容都是博主個人理解,算不上是對的,所以我建議大家只做參考,或看其他類似文章做一個自己的總結,每個人理解javascript都不一樣,如果不看底層編譯原理,在這個層面之上,我們只能靠這種方法理解了。。。但是記住,凡事都要自己求證的,別人說的是他人的觀點,自己理解出來的才是最適合你的.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89220.html
摘要:閉包閉包是指有權訪問另一個函數作用域中的變量的函數當某個函數被調用時,會創建一個執行環境及相應的作用域鏈。要注意通過第句聲明的這個方法屬于構造函數生成的對象,而不屬于構造函數的變量對象,也就是說,并不存在于作用域鏈中。 看到評論里有仁兄建議我試試箭頭函數,真是受寵若驚,本來寫這篇文章也只是想記錄寫要點給自己日后看的。今天早上看到一篇總結javascript中this的文章JavaScr...
摘要:其實它們都很簡單,但是在處理一些與相關的函數的時候,用來改變函數中的指向,卻是必不可少的工具,所以必須掌握好它們的用法。 關于javascript中的bind、call、apply等函數的用法 我GitHub上的菜鳥倉庫地址: 點擊跳轉查看其他相關文章 文章在我的博客上的地址: 點擊跳轉 ? ? ? ? 前面的文章已經說到this的指向了,那么這篇文章就要說一說和this相關的三個...
摘要:關于中的坑大家都踩過。那這里的和是嚴格相等的。這里介紹的是通過創建對象時的。提示一下,數組對象的函數本身就是有這個功能的,也就是說可以達到要求。事件有兩種記法,一個是也是類似,那么在中出現的表示觸發該事件的元素,也就是。 TL;DR: this 指向調用該方法的對象,只有函數執行時,this 才有定義。 關于 JavaScript 中 this 的坑大家都踩過。像本文開頭的這句話,道理...
摘要:函數的調用有五種模式方法調用模式,函數調用模式,構造器調用模式,調用模式以及回調模式,下面分別對這幾種模式進行說明。構造器調用模式構造函數的調用方式被稱為構造器調用模式,這是模擬類繼承式語言的一種調用方式。 函數的調用有五種模式:方法調用模式,函數調用模式,構造器調用模式,apply/call調用模式以及回調模式,下面分別對這幾種模式進行說明。 1.函數調用與方法調用模式: 1.1 聲...
摘要:關于中的指向我上的菜鳥倉庫地址點擊跳轉查看其他相關文章文章在我的博客上的地址點擊跳轉學習,必不可少的肯定要理解的指向。 關于javascript中this的指向 我GitHub上的菜鳥倉庫地址: 點擊跳轉查看其他相關文章 文章在我的博客上的地址: 點擊跳轉 ? ? ? ? 學習javascript,必不可少的肯定要理解this的指向。要學習this指向之前,就要先理解了我前面寫的幾...
閱讀 3569·2021-11-15 11:36
閱讀 1060·2021-11-11 16:55
閱讀 694·2021-10-20 13:47
閱讀 2993·2021-09-29 09:35
閱讀 3428·2021-09-08 10:45
閱讀 2553·2019-08-30 15:44
閱讀 848·2019-08-30 11:10
閱讀 1428·2019-08-29 13:43