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