摘要:否則如果是或,則設(shè)綁定為全局對象。令為解釋執(zhí)行的結(jié)果。返回一個值類型的引用,其基值為且其引用名為,嚴格模式標記為。進入函數(shù)代碼,為,非嚴格模式下將賦值為全局對象。內(nèi)置函數(shù)如何使用的內(nèi)置函數(shù)修改是通過給的內(nèi)置方法傳遞來實現(xiàn)的。
this
說到this,需要明確三方面內(nèi)容:
this何時被賦值
this被賦了什么值
內(nèi)置函數(shù)如何使用this的
this何時被賦值 進入函數(shù)代碼當(dāng)控制流根據(jù)一個函數(shù)對象 F、調(diào)用者提供的 thisArg 以及調(diào)用者提供的 argumentList,進入函數(shù)代碼的執(zhí)行環(huán)境時,執(zhí)行以下步驟:
如果函數(shù)代碼是嚴格模式下的代碼,設(shè) this 綁定 為 thisArg。
否則如果 thisArg 是 null 或 undefined ,則設(shè) this 綁定為全局對象。
……
以上信息來源:進入函數(shù)代碼
從上訴信息中可以知道:
this 與調(diào)用者提供的 thisArg 密切相關(guān)
this 在嚴格模式下為 thisArg
this 在非嚴格模式下為 thisArg 或 全局對象
那么 thisArg 又是怎么來的呢?下面來看下函數(shù)調(diào)用過程:
函數(shù)調(diào)用令 ref 為解釋執(zhí)行 MemberExpression 的結(jié)果。
令 func 為 GetValue(ref)。
令 argList 為解釋執(zhí)行 Arguments 的結(jié)果,產(chǎn)生參數(shù)值們的內(nèi)部列表(參見 11.2.4)。
如果 Type(func) 不是 Object,拋出一個 TypeError 異常。
如果 IsCallable(func) 為 false,拋出一個 TypeError 異常。
如果 Type(ref) 為 Reference,那么
如果 IsPropertyReference(ref) 為 true,
那么令 thisValue 為 GetBase(ref)。
否則,ref 的基值是一個環(huán)境記錄項。
令 thisValue 為調(diào)用 GetBase(ref) 的 ImplicitThisValue 具體方法的結(jié)果。
否則,Type(ref) 不是 Reference。
令 thisValue 為 undefined。
返回調(diào)用 func 的 [[Call]] 內(nèi)置方法的結(jié)果,傳入 thisValue 作為 this 值和列表 argList 作為參數(shù)列表。
以上信息來源:函數(shù)調(diào)用
從上訴信息中可以知道:
thisArg 即 thisValue
thisValue 與 ref 的類型密切相關(guān)
如果 ref 的類型是 Reference(引用規(guī)范類型)
如果 ref 是屬性引用,通過 GetBase(ref)(返回引用值ref的基值部分) 獲取 thisValue
否則,通過 ImplicitThisValue 方法獲取 thisValue
否則,thisValue 為 undefined
那么,了解到這里可能有許多新的疑問,比如:
Reference 是怎樣的類型
ref 是怎么來的
ref 什么時候是 Reference,什么時候不是。
GetBase(ref) 和 ImplicitThisValue 是如何產(chǎn)生結(jié)果的
Reference 是怎樣的類型首先先解釋下 Reference。
其實ES中的類型分為ECMAScript語言類型和規(guī)范類型
ECMAScript語言類型對應(yīng)的是程序員使用 ECMAScript 語言直接操作的值,如 Undefined、Null、Boolean、String、Number、Object等。
規(guī)范類型可用來描述 ECMAScript 表達式運算的中間結(jié)果,但這樣的值不能儲存為對象的屬性或 ECMAScript 語言的變量值。引用尤雨溪的解釋:
這里的 Reference 是一個 Specification Type,也就是 “只存在于規(guī)范里的抽象類型”。它們是為了更好地描述語言的底層行為邏輯才存在的,但并不存在于實際的 js 代碼中。ref 是怎么來的
從上訴函數(shù)調(diào)用中可以知道,ref 是解釋執(zhí)行 MemberExpression 的結(jié)果。
下面詳細看下 MemberExpression 的解析過程:
產(chǎn)生式 CallExpression : MemberExpression Arguments 按照下面的過程執(zhí)行 :
令 baseReference 為解釋執(zhí)行 MemberExpression 的結(jié)果。
令 baseValue 為 GetValue(baseReference)。
令 propertyNameReference 為解釋執(zhí)行 Expression 的結(jié)果。
令 propertyNameValue 為 GetValue(propertyNameReference)。
調(diào)用 CheckObjectCoercible(baseValue)。
令 propertyNameString 為 ToString(propertyNameValue)。
如果正在執(zhí)行中的語法產(chǎn)生式包含在嚴格模式代碼當(dāng)中,令 strict 為 true,否則令 strict 為 false。
返回一個值類型的引用,其基值為 baseValue 且其引用名為 propertyNameString,嚴格模式標記為 strict。
從上訴信息中分析可以知道:
解釋執(zhí)行 MemberExpression 的結(jié)果是一個引用規(guī)范類型(Reference)
這個引用規(guī)范類型包含三部分信息:
baseValue
propertyNameString
strict
thisValue 的值取決于 baseValue
baseValue 是調(diào)用 GetValue 獲得的。
GetValue 得到的基值是 undefined、Object、Boolean、String、Number、環(huán)境記錄項中的任意一個(詳見:引用規(guī)范類型),而不是引用規(guī)范類型。
GetValue 詳細過程見:GetValueref 什么時候是 Reference,什么時候不是 Reference
一般來說,ref 是MemberExpression解析的結(jié)果,都將是 Reference。
但是,如果 MemberExpression 是其函數(shù)表達式的一部分,則可能將改變最終解析結(jié)果的類型。
而改變解析結(jié)果類型的主要原因取決于是否調(diào)用了 GetValue 方法,如果調(diào)用了 GetValue ,函數(shù)中間值 ref 將是 Object 類型。
那么哪些表達式不使用 GetValue 呢?
標識符引用 : 標識符執(zhí)行的結(jié)果總是一個 Reference 類型的值。
群組表達式:本算法不在執(zhí)行 Expression 后使用 GetValue。這主要的目的是讓 delete 與 typeof 運算符可以作用在被括號括起來的表達式。
成員表達式
調(diào)用表達式
更多表達式詳見:表達式GetBase(ref) 和 ImplicitThisValue 是如何產(chǎn)生結(jié)果的
GetBase:返回引用值ref的基值部分
ImplicitThisValue : 聲明式環(huán)境記錄項永遠將 undefined 作為其 ImplicitThisValue 返回。
this被賦了什么值其實在 this何時被賦值 部分已經(jīng)介紹了 this被賦了什么值。下面總結(jié)三種賦值過程:
第一種this賦值過程:var v = 1; var obj = { v: 2, fn: function(){ console.log(this.v); } } obj.fn(); // 2 (obj.fn)(); // 2
調(diào)用表達式解析 obj.fn ,返回引用規(guī)范類型,baseValue 為 obj。
函數(shù)調(diào)用,由于 1 過程返回的為引用規(guī)范類型,且為屬性引用,調(diào)用 GetBase 將 baseValue (obj) 作為返回值,返回給 thisValue。
進入函數(shù)代碼,thisArg 為 obj,將其賦值給 this。
第二種this賦值過程:如果對步驟1中,baseValue 為 obj 有疑問,詳見屬性訪問, 產(chǎn)生式 MemberExpression : MemberExpression [ Expression ] 執(zhí)行過程。
相當(dāng)于有兩個過程:解析obj,baseValue 為聲明式環(huán)境記錄項。
解析obj.fn,baseValue 為 obj。
function foo(){ console.log(this); } foo(); // Window
調(diào)用表達式解析 foo, 返回引用規(guī)范類型,baseValue 為聲明式環(huán)境記錄項(函數(shù)聲明時綁定)。
函數(shù)調(diào)用,由于 1 過程返回的為引用規(guī)范類型,且不為屬性引用,調(diào)用 ImplicitThisValue 方法,返回 undefined 。
進入函數(shù)代碼,thisArg 為 undefined,非嚴格模式下將 this 賦值為全局對象。
第三種this賦值過程:var v = 1; var obj = { v: 2, fn: function(){ console.log(this.v); } } var fn2 = obj.fn; fn2(); // 1 (obj.fn, obj.fn)(); // 1
調(diào)用表達式解析 fn2,(obj.fn, obj.fn) ,由于賦值表達式、逗號表達式都使用了 GetValue 方法,返回函數(shù)(Object 類型)。
函數(shù)調(diào)用,由于 1 過程返回的不是引用規(guī)范類型,所以 thisValue 為 undefined`。
進入函數(shù)代碼,thisArg 為 undefined,非嚴格模式下將 this 賦值為全局對象。
內(nèi)置函數(shù)如何使用this的內(nèi)置函數(shù)修改 this 是通過給 func 的 [[Call]] 內(nèi)置方法傳遞 thisArg 來實現(xiàn)的。
內(nèi)置方法Function.prototype.apply (thisArg, argArray)
Function.prototype.call (thisArg [ , arg1 [ , arg2, … ] ] )
Function.prototype.bind (thisArg [, arg1 [, arg2, …] ] )
Array.prototype.every ( callbackfn [ , thisArg ] )
Array.prototype.some ( callbackfn [ , thisArg ] )
Array.prototype.forEach ( callbackfn [ , thisArg ] )
Array.prototype.map ( callbackfn [ , thisArg ] )
Array.prototype.filter ( callbackfn [ , thisArg ] )
new 運算符 使用內(nèi)置方法 [[Construct]] 實現(xiàn)this指定
內(nèi)置方法模擬實現(xiàn)使用成員表達式模擬內(nèi)置方法[[Call]]的效果:
applyFunction.prototype.apply_ = function (context, arr) { var context = Object(context) || window; var result; // 臨時記錄需要調(diào)用的function context.fn = this; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i < len; i++) { args.push("arr[" + i + "]"); } // 使用成員表達式指定context.fn執(zhí)行時this為context result = eval("context.fn(" + args + ")") } delete context.fn return result; }call
Function.prototype.call_ = function (context) { var context = context || window; context.fn = this; var args = []; // 獲取參數(shù)列表 for(var i = 1, len = arguments.length; i < len; i++) { args.push("arguments[" + i + "]"); } // 使用成員表達式指定context.fn執(zhí)行時this為context var result = eval("context.fn(" + args +")"); delete context.fn return result; }bind
Function.prototype.bind_ = function (context) { // 記錄bind的函數(shù) var self = this; var args = []; // 獲取綁定的參數(shù)列表 for(var i = 1, len = arguments.length; i < len; i++) { args.push("arguments[" + i + "]"); } // 創(chuàng)建新函數(shù) var fbound = function () { // 獲取未綁定的參數(shù)列表 var bindArgs = Array.prototype.slice.call(arguments); // fbound被當(dāng)做構(gòu)造函數(shù)使用,this指向?qū)嵗?。否則,指向 context self.apply(this instanceof self ? this : context, args.concat(bindArgs)); } // 維護原型關(guān)系 fbound.prototype = self.prototype || new Function().prototype ; return fbound; }參考文檔
【1】冴羽的深入理解系列
【2】ES5 Wiki
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102001.html
摘要:最近工作中重構(gòu)了抽獎轉(zhuǎn)盤,給大家提供一個開發(fā)轉(zhuǎn)盤抽獎的思路需求轉(zhuǎn)盤根據(jù)獎品數(shù)量不同而有變化目錄結(jié)構(gòu)由于業(yè)務(wù)需要所以開發(fā)了兩個版本抽獎,和,不過部分只能替換圖片,沒有功能邏輯。 最近工作中重構(gòu)了抽獎轉(zhuǎn)盤,給大家提供一個開發(fā)轉(zhuǎn)盤抽獎的思路 需求 1、轉(zhuǎn)盤根據(jù)獎品數(shù)量不同而有變化 2、canvas 目錄結(jié)構(gòu) showImg(https://segmentfault.com/img/bVbwL...
摘要:構(gòu)建二叉樹進行數(shù)值數(shù)組的去重及優(yōu)化常見兩層循環(huán)實現(xiàn)數(shù)組去重構(gòu)建二叉樹實現(xiàn)去重僅適用于數(shù)值類型的數(shù)組將先前遍歷過的元素,構(gòu)建成二叉樹,樹中每個結(jié)點都滿足左子結(jié)點的值當(dāng)前結(jié)點的值右子結(jié)點的值這樣優(yōu)化了判斷元素是否之前出現(xiàn)過的過程若元素比當(dāng)前結(jié)點 構(gòu)建二叉樹進行數(shù)值數(shù)組的去重及優(yōu)化 常見兩層循環(huán)實現(xiàn)數(shù)組去重 let arr = [11, 12, 13, 9, 8, 7, 0, 1, 2, 2...
摘要:手勢解鎖界面一些對安全要求比較高的少不了鎖屏頁面,而手勢解鎖對于用戶來說使用方便,對于程序員來說小有挑戰(zhàn),怎么有棄之不用的道理。 ionic 2+ 手勢解鎖界面 一些對安全要求比較高的app少不了鎖屏頁面,而手勢解鎖對于用戶來說使用方便,對于程序員來說小有挑戰(zhàn),怎么有棄之不用的道理。 效果圖 效果圖處理短,方便大家閱讀showImg(https://segmentfault.co...
摘要:五子棋人機大戰(zhàn)創(chuàng)建實例是否結(jié)束我電腦所有棋子已經(jīng)落下的棋子贏法總數(shù)所有贏法統(tǒng)計我的贏法統(tǒng)計電腦贏法統(tǒng)計初始化初始化生成棋盤棋盤初始化鼠標移動聚焦功能實現(xiàn)算法初始化落子功能實現(xiàn)生成棋盤初始化生成不是的倍數(shù)棋盤列初始化棋盤棋盤初始化畫棋盤畫 JS+canvas五子棋人機大戰(zhàn) 1. 創(chuàng)建實例 function Gobang () { this.over = false; // 是否結(jié)...
摘要:二叉樹二叉樹是一種樹形結(jié)構(gòu),它的特點是每個節(jié)點最多只有兩個分支節(jié)點,一棵二叉樹通常由根節(jié)點,分支節(jié)點,葉子節(jié)點組成。 二叉樹 二叉樹(Binary Tree)是一種樹形結(jié)構(gòu),它的特點是每個節(jié)點最多只有兩個分支節(jié)點,一棵二叉樹通常由根節(jié)點,分支節(jié)點,葉子節(jié)點組成。而每個分支節(jié)點也常常被稱作為一棵子樹。 showImg(https://segmentfault.com/img/bVbmEd...
閱讀 1980·2021-11-24 10:45
閱讀 1468·2021-11-18 13:15
閱讀 4562·2021-09-22 15:47
閱讀 3941·2021-09-09 11:36
閱讀 2018·2019-08-30 15:44
閱讀 3097·2019-08-29 13:05
閱讀 2510·2019-08-29 12:54
閱讀 2003·2019-08-26 13:47