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