每一個 JavaScript 程序猿,包括我本身,都一直在努力瞭解 this
關鍵字在代碼中的真正身份。javascript
我設計了一個通用算法,能夠幫你在任何狀況下肯定 this
關鍵字的值。雖然我儘量的使算法容易看懂,但仍是建議你多看幾遍並理解相關術語。前端
另外還用了幾個例子展現怎樣用這個算法一步一步的對 this
進行評估,最後你本身親自試一試。java
把算法定義爲 ThisValueOfFunction(func, invocationType)
,返回值爲在以 invocationtype
方式調用函數 func
時的 this
值:程序員
ThisValueOfFunction(func, invocationType):面試
若是 func
是一個箭頭函數,那麼算法
func
是在最外面的做用域 中定義的,那麼返回 globalObject
不然segmentfault
SuffeFunc
是 Func
的外部函數ThisValueOfFunction(outerFunc, outerInvocationType)
若是 func
是 originFunc
函數的綁定函數,那麼瀏覽器
thisArg
是 Func = OriginFunc.bind(thisarg)
的參數thisArg
若是 func
是 someclass
類 中的 constructor()
方法,那麼服務器
instance
是 instance = new SomeClass()
的實例instance
若是 func
是一個常規函數,那麼微信
若是 invocationtype
是做爲構造函數,那麼
newObject
是新構造的對象 newObject = new func()
newObject
若是 invocationtype
是間接調用的,那麼
thisArg
是 func.call(thisArg)
或 func.apply(thisArg)
的參數thisArg
若是 invocationtype
是 方法,那麼
object
是在 object.func()
上調用 func
的對象object
若是 invocationtype
是常規的,那麼
undefined
globalObject
這個算法使用了大量的 JavaScript 術語。若是你不熟悉某些東西,先看下面的解釋。
箭頭函數
箭頭函數是使用粗箭頭語法 =>
定義的函數。 箭頭函數示例:
const sum = (number1, number2) => { return number1 + number2; }
綁定函數
綁定函數是經過在函數上調用方法 myFunc.bind(thisArg, arg1, ..., argN)
建立的函數。 綁定函數的示例:
function originalFunction() { // ... } const boundFunction = originalFunction.bind({ prop: 'Value' });
常規函數
常規函數是用 function
關鍵字或在對象上定義的簡單 JavaScript 函數。 常規函數的示例:
function regularFunction(who) { return `Hello, ${who}!`; } const object = { anotherRegularFunction(who) { return `Good bye, ${who}!` } };
constructor()
constructor() 是 class
內部的一種特殊方法,用於初始化類實例。
class SomeClass() { constructor(prop) { this.prop = prop; } }
最外部的做用域
最外部的做用域是沒有外部做用域的最頂級做用域。
// 最外部的做用域 let a = 1; function someFunction() { // someFunction() 的做用域 // 這裏不是最外部的做用域 let b = 1; }
外部函數
外部函數在其做用域內包含另外一個函數。
// outerFunction() 是 myFunction() 的外部函數 function outerFunction() { function myFunction() { //... } }
全局對象是在全局做用域內始終存在的對象。 window
是瀏覽器環境中的全局對象,在 Node 環境中是 global
。
調用
函數的調用只是使用一些參數來調用該函數。
function sum(number1, number2) { return number1 + number2; } sum(1, 3); // 調用 sum.call({}, 3, 4); // 調用 sum.apply({}, 5, 9); // 調用 const obj = { method() { return 'Some method'; } }; obj.method(); // 調用 class SomeClass { constructor(prop) { this.prop = prop; } } const instance = new SomeClass('Value'); // 調用
構造函數調用
使用 new
關鍵字調用函數或類時,將發生構造函數調用。
function MyCat(name) { this.name = name; } const fluffy = new MyCat('Fluffy'); // 構造函數調用 class MyDog { constructor(name) { this.name = name; } } const rex = new MyDog('Rex'); // 構造函數調用
間接調用
使用 func.call(thisArg, ...)
或 func.apply(thisArg, ...)
方法調用函數時,會發生間接調用。
function sum(number1, number2) { return number1 + number2; } sum.call({}, 1, 2); // 間接調用 sum.apply({}, 3, 5); // 間接調用
方法調用
當在屬性訪問器表達式 object.method()
中調用函數時,將發生方法調用。
const object = { greeting(who) { return `Hello, ${who}!` } }; object.greeting('World'); // 方法調用 object['greeting']('World'); // 方法調用
常規調用
只用函數參數變量調用 func(...)
時,會發生常規調用。
function sum(number1, number2) { return number1 + number2; } sum(1, 4); // 常規調用
use strict
指令來啓用嚴格模式。const myFunc = () => { console.log(this); // logs `window`}; myFunc();
ThisValueOfFunction(myFunc, 「常規的」)
myfunc
是箭頭函數:從而在算法中匹配狀況 1。同時 myFunc
在最外面的做用域內定義,匹配狀況 1.1。
算法 1.1 中返回 globalObject
意思是 myFunc
中的 this
值爲全局對象 window
(在瀏覽器環境中)。
const object = { method() { console.log(this); // logs { method() {...} } } }; object.method();
ThisValueOfFunction(object.method, 「做爲方法調用」)
method()
同時是 object
的屬性,是常規函數。與算法的狀況 4 匹配。
object.method()
是一種方法調用,由於是屬性訪問的,送一所以與 4.3 匹配。
而後,根據 4.3,method()
方法中的 this
等於方法的擁有者 (object.method()
) — object
。
function MyCat(name) { this.name = name; const getName = () => { console.log(this); // logs { name: 'Fluffy', getName() {...} } return this.name; } this.getName = getName; } const fluffy = new MyCat('Fluffy'); fluffy.getName();
ThisValueOfFunction(getName, 「做爲方法調用」)
getName()
是一個箭頭函數,因此符合算法的狀況 1;由於 mycat
是 getName()
的外部函數,而後與 1.2 匹配。
分支 1.2.2 :this
在 getName()
箭頭函數內部的值等於外部函數的值 MyCat
。
因此讓咱們在 MyCat
函數上運行算法 ThisValueOfFunction(MyCat, "作爲構造函數")
。
ThisValueOfFunction(MyCat, 「做爲構造函數」)
MyCat
是常規函數,因此跳轉到算法的分支 4。
由於 MyCat
作爲構造函數調用 new MyCat('Fluffy')
,符合分支 4.1。最後根據 4.1.1 和 4.1.2,this
在 MyCat
中等於構造的對象:fluffy
。
而後,返回箭頭函數後符合 1.2.2,在 getname()
中的 this
等於 mycat
的 this
,最終結果爲 fluffy
。
要理解這個算法,最好本身親自試試。下面是 3 個練習。
const myRegularFunc = function() { console.log(this); // logs ???}; myRegularFunc();
如何肯定 myRegularFunc()
中的 this
值?寫出你的判斷步驟。
class MyCat { constructor(name) { this.name = name; console.log(this); // logs ??? } } const myCat = new MyCat('Lucy');
如何肯定 new MyCat('Lucy')
中的 this
值?寫出你的判斷步驟。
const object = { name: 'Batman', getName() { const arrow = () => { console.log(this); // logs ??? return this.name; }; return arrow(); }; } object.getName();
如何肯定 arrow()
中的 this
值?寫出你的判斷步驟。