關於javascript中的this指向

javascript 當中的 this是一個用於指向當前上下文對象的關鍵字。在面向對象編程及平常開發當中咱們常常與其打交道,初學javscript的朋友很是容易誤入歧途從而理解錯誤。javascript

上下文對象概念

在個人深刻貫徹閉包思想一章中其實已經講了不少關於環境棧(即上下文對象)的相關內容,但內容過於冗長,不便閱讀,時間充足的同窗能夠去看下,但這篇中,咱們仍是獨立開來,下面我總結了幾條有關上下文的知識,但願能幫助你們。java

首先須要聲明的是這些術語的問題,其實咱們常說的環境棧調用棧上下文棧 等等不少民間自創術語,它們形容的都是一個東東,即環境棧(這裏我就拿第一個詞來形容了,你們知道就好)面試

執行上下文棧 (Execution context stack, ECS)

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)

    • 變量
    • 函數的聲明
    • 函數的參數
  • 做用域鏈(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必然是這個實例。他的順序大體是

  1. 建立上下文環境
  2. 執行構造函數
  3. 把構造函數的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都不同,若是不看底層編譯原理,在這個層面之上,咱們只能靠這種方法理解了。。。可是記住,凡事都要本身求證的,別人說的是他人的觀點,本身理解出來的纔是最適合你的.

相關文章
相關標籤/搜索