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