摘要:把這個執行上下文壓入調用棧的頂部,即設置成運行執行上下文。函數作為構造函數調用沒有繼承關系有繼承關系我們把一個函數被當作構造函數,使用操作符調用時發生的主要步驟新建一個普通對象,把其原型指向構造函數的屬性的值。把當前執行上下文彈出調用棧。
var currentTime = Date() 能生成一個當前時間的日期對象,var currentTime = new Date() 也能生成一個同樣的對象。如果你看過一些框架,那么你會發現有的框架生成對象寫法是 new ClassName(),有的框架是 className()。 那么兩種方式有什么區別呢?
普通函數/方法調用假設我們定義了一個函數:
function normalFunc() { console.log( this ); } // 第一種調法 normalFunc(); // 第二種調法 normalFunc.call( null ); // 第三種調法 var obj = { method: normalFunc } obj.method();
我們把一個函數被當作一個普通函數或者方法調用歸為一類,其被調用時發生的主要步驟:
生成一個新的執行上下文和對應的作用域。(如果對執行上下文是什么不了解的話,可以參考我上一篇《什么是作用域和執行上下文》)
把當前函數和這個新的執行上下文和作用域關聯起來。
2.1. 如果當前函數是箭頭函數,那么把作用域中的 environment record 對象的內部屬性 [[thisBindingStatus]] 設置成 lexical。
把這個執行上下文壓入調用棧的頂部,即設置成運行執行上下文(running execution context)。
接下來處理當前函數的屬性 this 的取值:
4.1. 如果當前函數是箭頭函數,那么這步就不做任何處理(因為已經在步驟2.1中做了標志位)。
4.2. 如果不是箭頭函數,那么先查看當前函數是否處在嚴格模式下。
4.2.1. 嚴格模式:this 的取值取決于如何調用當前函數,譬如上例代碼中第一種調法,取值為 undefined,第二種調法取值為 `normalFunc.call(` 的第一個參數,第三種調法取值為 obj。 4.2.2. 非嚴格模式:先按4.2.1的分類獲得 this 的取值,如果是 null 或者 undefined,用全局對象代替 null 或者 undefined。如果 this 的取值是非空值那么把 this 指向這個非空值(注1)。
4.3. 把 this 的取值保存在作用域中的 environment record 對象的內部屬性 [[thisValue]] 中(步驟4中并非把 this 直接指向這些取值,而是把值保存在作用域特定內部屬性中,this 的尋值過程還有額外一步,下面會說明)。
執行函數體。
把當前執行上下文彈出調用棧。
如果步驟5有返回,則返回這個結果。如果步驟5沒有返回,則返回 undefined。
注1:這里非空值還要判斷是原始類型(primitive value),還是對象類型。如果是原始類型,取值還要再把原始類型包裝成對象才能作為 this 的取值。步驟中避免太繁瑣,省略了細節顧特地加上注釋。
函數中 this 的取值過程(ResolveThisBinding)結合上面描述的步驟,我們來看看當你在函數中使用 this 時(上面的步驟5中 this 已經可用),程序時如何尋找 this 的:
根據當前執行上下文查找到對應的 enviroment record(execution context -> scope -> environment record)。
判斷當前這個 record 是否存儲過[[thisValue]],如果沒有的就沿著作用域鏈向上查找,以全局作用域為終點。
如果找到了,則返回。
如上所述,箭頭函數本身的作用域并沒有存儲[[thisValue]],所以其內部使用 this 會去定義箭頭函數的地方(函數)去取 this,如果取不到繼續向上查找。
函數作為構造函數調用// 沒有繼承關系 function normalFuncAsContructor() { // return a new object? // return {} // or not // [return] } var o = new normalFuncAsContructor(); // 有繼承關系 function Parent(){} function Child(){} Child.prototype = new Parent(); var c = new Child();
我們把一個函數被當作構造函數,使用 new 操作符調用時發生的主要步驟:
新建一個普通對象,把其原型 [[Prototype]] 指向構造函數的 prototype 屬性的值。
如普通函數調用的步驟1一樣,生成一個新的執行上下文和對應的作用域,并把當前構造函數和兩者關聯起來。
把這個執行上下文壓入調用棧的頂部。
把第一步生成的對象當作 this 的取值保存到作用域中的 environment record 對象的內部屬性 [[thisValue]] 中。
執行函數體。
把當前執行上下文彈出調用棧。
處理函數執行的結果,即 new 了之后返回啥:
7.1 如果步驟5返回一個對象,那么就把這個對象作為此次 new 操作的返回值。
7.2 如果返回的不是對象,而且這個函數不是 generator 函數,那么返回第一步生成的對象(generator 就先不在這里討論了)。
知道了兩者的區別,我們就能在函數體里面搞文章了,你可以通過如下代碼檢測用戶怎么調用你的函數。如果你知道了用戶怎么調用,你自然可以根據你想要的結果限制用戶的使用方法。
function myFunc() { if ( this && myFunc.prototype.isPrototypeOf( this ) ) { console.log( "called by new operator" ); } else { console.log( "commonly invoked" ); } } myFunc(); // commonly invoked new myFunc(); // called by new operator var obj = { method: myFunc } obj.method(); // commonly invoked
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/83196.html
摘要:作為構造函數何為構造函數所謂構造函數就是用來對象的函數,像等都是全局定義的構造函數。正在跑步正在說話正在跑步正在說話如上,如果函數作為構造函數用,那么其中的就代表它即將出來的對象。 前言 總括:詳解JavaScript中的this的一篇總結,不懂this這個難點,很多時候會造成一些困擾,寫出一些bug不知如何收場,所以一起來寫bug吧,不對,一起來寫代碼吧。 原文地址:JavaScr...
摘要:在用創建對象時,指向發生改變是在第二步創建一個對象實例將構造函數中的指向這個對象執行構造函數中的代碼返回這個新創建的對象箭頭函數中的箭頭函數內部是不會綁定的,它會捕獲外層作用域中的,作為自己的值。參考你不知道的上卷 前言 this 是 JavaScript 中不可不談的一個知識點,它非常重要但又不容易理解。因為 JavaScript 中的 this 不同于其他語言。不同場景下的 thi...
摘要:一是什么函數的內部屬性,引用的是函數據以執行的環境對象。函數做為節點事件調用時指向節點本身做為構造函數實力化方法時指向實例對象箭頭函數里的普通函數,由于閉包函數是執行的,所以指向箭頭函數的指向函數創建時的作用域。 一、this是什么? 函數的內部屬性,this引用的是函數據以執行的環境對象。也就是說函數的this會指向調用函數的執行環境。 function a(){ retur...
閱讀 3451·2019-08-30 10:54
閱讀 3147·2019-08-29 16:38
閱讀 2166·2019-08-26 14:06
閱讀 1512·2019-08-23 15:39
閱讀 3034·2019-08-23 15:37
閱讀 2883·2019-08-23 13:50
閱讀 3190·2019-08-22 17:14
閱讀 2376·2019-08-22 15:44