阿里雲最近在作活動,低至2折,有興趣能夠看看:
https://promotion.aliyun.com/...
爲了保證的可讀性,本文采用意譯而非直譯。javascript
不少時候, JS 中的 this
對於我們的初學者很容易產生困惑不解。 this
的功能很強大,但須要必定付出才能慢慢理解它。html
對Java、PHP或其餘標準語言來看,this
表示類方法中當前對象的實例。大多數狀況下,this
不能在方法以外使用,這樣就比較不會形成混淆。前端
在J要中狀況就有所不一樣: this
表示函數的當前執行上下文,JS 中函數調用主要有如下幾種方式:java
alert('Hello World!')
console.log('Hello World!')
new RegExp('\\d')
alert.call(undefined, 'Hello World!')
每種調用類型以本身的方式定義上下文,因此就很容易產生混淆。git
此外,嚴格模式也會影響執行上下文。github
理解this
關鍵是要清楚的知道函數調用及其如何影響上下文。正則表達式
本文主要說明函數的調用方式及如何影響 this
,而且說明執行上下文的常見陷阱。數組
在開始以前,先知道幾個術語:瀏覽器
調用函數正在執行建立函數體的代碼,或者只是調用函數。 例如,parseInt函數調用是parseInt('15')。安全
parseInt
函數調用是parseInt('15')
。this
在函數體內的值。 例如,map.set('key', 'value')
的調用上下文是 map
。當一個表達式爲函數接着一個(
,一些用逗號分隔的參數以及一個)
時,函數調用被執行,例如parseInt('18')
。
函數調用表達式不能是屬性方式的調用,如 obj.myFunc()
,這種是建立一個方法調用。再如 [1,5].join(',')
不是函數調用,而是方法調用,這種區別須要記住哈,很重要滴。
函數調用的一個簡單示例:
function hello(name) { return 'Hello ' + name + '!'; } // 函數調用 const message = hello('World'); console.log(message); // => 'Hello World!'
hello('World')
是函數調用: hello
表達式等價於一個函數,跟在它後面的是一對括號以及'World'
參數。
一個更高級的例子是IIFE(當即調用的函數表達式)
const message = (function(name) { return 'Hello ' + name + '!'; })('World'); console.log(message) // => 'Hello World!'
IIFE也是一個函數調用:第一對圓括號(function(name) {...})
是一個表達式,它的計算結果是一個函數對象,後面跟着一對圓括號,圓括號的參數是「World」
。
this 在函數調用中是一個全局對象
局對象由執行環境決定。在瀏覽器中,this
是 window
對象。
在函數調用中,執行上下文是全局對象。
再來看看下面函數中的上下文又是什麼鬼:
function sum(a, b) { console.log(this === window); // => true this.myNumber = 20; // 將'myNumber'屬性添加到全局對象 return a + b; } // sum() is invoked as a function // sum() 中的 `this` 是一個全局對象(window) sum(15, 16); // => 31 window.myNumber; // => 20
在調用sum(15,16)
時,JS 自動將this
設置爲全局對象,在瀏覽器中該對象是window
。
當this
在任何函數做用域(最頂層做用域:全局執行上下文)以外使用,this
表示 window
對象
console.log(this === window); // => true this.myString = 'Hello World!'; console.log(window.myString); // => 'Hello World!' <!-- In an html file --> <script type="text/javascript"> console.log(this === window); // => true </script>
this
在嚴格模式下的函數調用中爲
undefined
嚴格模式是在 ECMAScript 5.1中引入的,它提供了更好的安全性和更強的錯誤檢查。
要啓用嚴格模式,函數頭部寫入use strict
便可。
啓用後,嚴格模式會影響執行上下文,this
在常規函數調用中值爲undefined
。 與上述狀況2.1
相反,執行上下文再也不是全局對象。
嚴格模式函數調用示例:
function multiply(a, b) { 'use strict'; // 啓用嚴格模式 console.log(this === undefined); // => true return a * b; } multiply(2, 5); // => 10
當multiply(2,5)
做爲函數調用時,this
是undefined
。
嚴格模式不只在當前做用域中有效,在內部做用域中也是有效的(對於在內部聲明的全部函數):
function execute() { 'use strict'; // 開啓嚴格模式 function concat(str1, str2) { // 嚴格模式仍然有效 console.log(this === undefined); // => true return str1 + str2; } // concat() 在嚴格模式下做爲函數調用 // this in concat() is undefined concat('Hello', ' World!'); // => "Hello World!" } execute();
'use strict'
被插入到執行體的頂部,在其做用域內啓用嚴格模式。 由於函數concat
是在執行的做用域中聲明的,因此它繼承了嚴格模式。
單個JS文件可能包含嚴格和非嚴格模式。 所以,對於相同的調用類型,能夠在單個腳本中具備不一樣的上下文行爲:
function nonStrictSum(a, b) { // 非嚴格模式 console.log(this === window); // => true return a + b; } function strictSum(a, b) { 'use strict'; // 啓用嚴格模式 console.log(this === undefined); // => true return a + b; } nonStrictSum(5, 6); // => 11 strictSum(8, 12); // => 20
this
在內部函數中的時候函數調用的一個常見陷阱是,認爲this
在內部函數中的狀況與外部函數中的狀況相同。
正確地說,內部函數的上下文只依賴於它的調用類型,而不依賴於外部函數的上下文。
要將 this
設置爲所需的值,能夠經過 .call()
或.apply()
修改內部函數的上下文或使用.bind()
建立綁定函數。
下面的例子是計算兩個數的和:
const numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { console.log(this === numbers); // => false return this.numberA + this.numberB; } return calculate(); } }; numbers.sum(); // => NaN
sum()
是對象上的方法調用,因此sum
中的上下文是numbers
對象。calculate
函數是在sum
中定義的,你可能但願在calculate()
中this
也表示number
對象。
calculate()
是一個函數調用(不是方法調用),它將this
做爲全局對象window
(非嚴格模下)。即便外部函數sum
將上下文做爲number
對象,它在calculate
裏面沒有影響。
sum()
的調用結果是NaN
,不是預期的結果5 + 10 = 15
,這都是由於沒有正確調用calculate
。
爲了解決這個問題,calculate
函數中上下文應該與 sum
中的同樣,以即可以訪問numberA
和numberB
屬性。
一種解決方案是經過調用calculator.call(this)
手動將calculate
上下文更改成所需的上下文。
const numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { console.log(this === numbers); // => true return this.numberA + this.numberB; } // 使用 .call() 方法修改上下文 return calculate.call(this); } }; numbers.sum(); // => 15
call(this)
像往常同樣執行calculate
函數,但 call
會把上下文修改成指定爲第一個參數的值。
如今this.numberA
+ this.numberB
至關於numbers.numberA + numbers.numberB
。 該函數返回預期結果5 + 10 = 15
。
另外一種就是使用箭頭函數
const numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true const calculate = () => { console.log(this === numbers); // => true return this.numberA + this.numberB; } return calculate(); } }; numbers.sum(); // => 15
方法是存儲在對象屬性中的函數。例如
const myObject = { // helloFunction 是一個方法 helloFunction: function() { return 'Hello World!'; } }; const message = myObject.helloFunction();
helloFunction
是myObject
的一個方法,要調用該方法,能夠這樣子調用 :myObject.helloFunction
。
當一個表達式以屬性訪問的形式執行時,執行的是方法調用,它至關於以個函數接着(,一組用逗號分隔的參數以及)。
利用前面的例子,myObject.helloFunction()
是對象myObject
上的一個helloFunction
的方法調用。[1, 2].join(',')
或/\s/.test('beautiful world')
也被認爲是方法調用。
區分函數調用和方法調用很是重要,由於它們是不一樣的類型。主要區別在於方法調用須要一個屬性訪問器形式來調用函數(obj.myFunc()
或obj['myFunc']()
),而函數調用不須要(myFunc()
)。
['Hello', 'World'].join(', '); // 方法調用 ({ ten: function() { return 10; } }).ten(); // 方法調用 const obj = {}; obj.myFunction = function() { return new Date().toString(); }; obj.myFunction(); // 方法調用 const otherFunction = obj.myFunction; otherFunction(); // 函數調用 parseFloat('16.60'); // 函數調用 isNaN(0); // 函數調用
理解函數調用和方法調用之間的區別有助於正確識別上下文。
在方法調用中,
this
是擁有這個方法的對象
當調用對象上的方法時,this
就變成了對象自己。
建立一個對象,該對象有一個遞增數字的方法
const calc = { num: 0, increment: function() { console.log(this === calc); // => true this.num += 1; return this.num; } }; // method invocation. this is calc calc.increment(); // => 1 calc.increment(); // => 2
調用calc.increment()
使increment
函數的上下文成爲calc
對象。因此使用this.num
來增長num
屬性是有效的。
再來看看另外一個例子。JS對象從原型繼承一個方法,當在對象上調用繼承的方法時,調用的上下文仍然是對象自己
const myDog = Object.create({ sayName: function() { console.log(this === myDog); // => true return this.name; } }); myDog.name = 'Milo'; // 方法調用 this 指向 myDog myDog.sayName(); // => 'Milo'
Object.create()
建立一個新對象myDog
,並根據第一個參數設置其原型。myDog
對象繼承sayName
方法。
執行myDog. sayname()
時,myDog
是調用的上下文。
在EC6 class
語法中,方法調用上下文也是實例自己
class Planet { constructor(name) { this.name = name; } getName() { console.log(this === earth); // => true return this.name; } } var earth = new Planet('Earth'); // method invocation. the context is earth earth.getName(); // => 'Earth'
方法能夠從對象中提取到一個單獨的變量const alone = myObj.myMethod
。當方法單獨調用時,與原始對象alone()
分離,你可能認爲當前的this
就是定義方法的對象myObject
。
若是方法在沒有對象的狀況下調用,那麼函數調用就會發生,此時的this
指向全局對象window
嚴格模式下是undefined
。
下面的示例定義了Animal
構造函數並建立了它的一個實例:myCat
。而後setTimout()
在1秒後打印myCat
對象信息
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = function() { console.log(this === myCat); // => false console.log('The ' + this.type + ' has ' + this.legs + ' legs'); } } const myCat = new Animal('Cat', 4); // The undefined has undefined legs setTimeout(myCat.logInfo, 1000);
你可能認爲setTimout
調用myCat.loginfo()
時,它應該打印關於myCat
對象的信息。
不幸的是,方法在做爲參數傳遞時與對象是分離,setTimout(myCat.logInfo)
如下狀況是等效的:
setTimout(myCat.logInfo); // 等價於 const extractedLogInfo = myCat.logInfo; setTimout(extractedLogInfo);
將分離的logInfo
做爲函數調用時,this
是全局 window
,因此對象信息沒有正確地打印。
函數可使用.bind()
方法與對象綁定,就能夠解決 this
指向的問題。
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = function() { console.log(this === myCat); // => true console.log('The ' + this.type + ' has ' + this.legs + ' legs'); }; } const myCat = new Animal('Cat', 4); // logs "The Cat has 4 legs" setTimeout(myCat.logInfo.bind(myCat), 1000);
myCat.logInfo.bind(myCat)
返回一個新函數,它的執行方式與logInfo
徹底相同,可是此時的 this
指向 myCat
,即便在函數調用中也是如此。
另外一種解決方案是將logInfo()
方法定義爲一個箭頭函數:
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = () => { console.log(this === myCat); // => true console.log('The ' + this.type + ' has ' + this.legs + ' legs'); }; } const myCat = new Animal('Cat', 4); // logs "The Cat has 4 legs" setTimeout(myCat.logInfo, 1000);
當new
關鍵詞緊接着函數對象,(,一組逗號分隔的參數以及)時被調用,執行的是構造函數調用如new RegExp('\\d')
。
聲明瞭一個Country
函數,而且將它做爲一個構造函數調用:
function Country(name, traveled) { this.name = name ? name : 'United Kingdom'; this.traveled = Boolean(traveled); } Country.prototype.travel = function() { this.traveled = true; }; // 構造函數調用 const france = new Country('France', false); // 構造函數調用 const unitedKingdom = new Country; france.travel(); // Travel to France
new Country('France', false)
是Country
函數的構造函數調用。它的執行結果是一個name
屬性爲'France'
的新的對象。 若是這個構造函數調用時不須要參數,那麼括號能夠省略:new Country
。
從ES6開始,JS 容許用class
關鍵詞來定義構造函數
class City { constructor(name, traveled) { this.name = name; this.traveled = false; } travel() { this.traveled = true; } } // Constructor invocation const paris = new City('Paris', false); paris.travel();
new City('Paris')
是構造函數調用。這個對象的初始化由這個類中一個特殊的方法constructor
來處理。其中,this
指向新建立的對象。
構造函數建立了一個新的空的對象,它從構造函數的原型繼承了屬性。構造函數的做用就是去初始化這個對象。 可能你已經知道了,在這種類型的調用中,上下文指向新建立的實例。
當屬性訪問myObject.myFunction
前面有一個new
關鍵詞時,JS會執行構造函數調用而不是原來的方法調用。
例如new myObject.myFunction()
:它至關於先用屬性訪問把方法提取出來extractedFunction = myObject.myFunction
,而後利用把它做爲構造函數建立一個新的對象: new extractedFunction()
。
在構造函數調用中 this 指向新建立的對象
構造函數調用的上下文是新建立的對象。它利用構造函數的參數初始化新的對象,設定屬性的初始值,添加事件處理函數等等。
來看看下面示例中的上下文
function Foo () { console.log(this instanceof Foo); // => true this.property = 'Default Value'; } // Constructor invocation const fooInstance = new Foo(); fooInstance.property; // => 'Default Value'
new Foo()
正在進行構造函數調用,其中上下文是fooInstance
。 在Foo
內部初始化對象:this.property
被賦值爲默認值。
一樣的狀況在用class
語法(從ES6起)時也會發生,惟一的區別是初始化在constructor
方法中進行:
class Bar { constructor() { console.log(this instanceof Bar); // => true this.property = 'Default Value'; } } // Constructor invocation const barInstance = new Bar(); barInstance.property; // => 'Default Value'
有些JS函數不是隻在做爲構造函數調用的時候才建立新的對象,做爲函數調用時也會,例如RegExp
:
var reg1 = new RegExp('\\w+'); var reg2 = RegExp('\\w+'); reg1 instanceof RegExp; // => true reg2 instanceof RegExp; // => true reg1.source === reg2.source; // => true
當執行的 new RegExp('\\w+')
和RegExp('\\w+')
時,JS 會建立等價的正則表達式對象。
使用函數調用來建立對象存在一個潛在的問題(不包括工廠模式),由於一些構造函數可能會忽略在缺乏new
關鍵字時初始化對象的邏輯。
下面的例子說明了這個問題:
function Vehicle(type, wheelsCount) { this.type = type; this.wheelsCount = wheelsCount; return this; } // 忘記使用 new const car = Vehicle('Car', 4); car.type; // => 'Car' car.wheelsCount // => 4 car === window // => true
Vehicle
是一個在上下文對象上設置type
和wheelsCount
屬性的函數。
當執行Vehicle('Car', 4)
時,返回一個對象Car
,它具備正確的屬性:Car.type
爲 Car
和Car.wheelsCount
爲4
,你可能認爲它很適合建立和初始化新對象。
然而,在函數調用中,this
是window
對象 ,所以 Vehicle('Car',4)
在 window
對象上設置屬性。 顯然這是錯誤,它並無建立新對象。
當你但願調用構造函數時,確保你使用了new
操做符:
function Vehicle(type, wheelsCount) { if (!(this instanceof Vehicle)) { throw Error('Error: Incorrect invocation'); } this.type = type; this.wheelsCount = wheelsCount; return this; } // Constructor invocation const car = new Vehicle('Car', 4); car.type // => 'Car' car.wheelsCount // => 4 car instanceof Vehicle // => true // Function invocation. Throws an error. const brokenCar = Vehicle('Broken Car', 3);
new Vehicle('Car',4)
運行正常:建立並初始化一個新對象,由於構造函數調用中時使用了new
關鍵字。
在構造函數裏添加了一個驗證this instanceof Vehicle
來確保執行的上下文是正確的對象類型。若是this
不是Vehicle
,那麼就會報錯。這樣,若是執行Vehicle('Broken Car', 3)
(沒有new
),咱們會獲得一個異常:Error: Incorrect invocation
。
使用myFun.call()
或myFun.apply()
方法調用函數時,執行的是隱式調用。
JS中的函數是第一類對象,這意味着函數就是對象,對象的類型爲Function
。從函數對象的方法列表中,.call()
和.apply()
用於調用具備可配置上下文的函數。
.call(thisArg[, arg1[, arg2[, ...]]])
將接受的第一個參數thisArg
做爲調用時的上下文,arg1, arg2, ...
這些則做爲參數傳入被調用的函數。.apply(thisArg, [args])
將接受的第一個參數thisArg
做爲調用時的上下文,而且接受另外一個相似數組的對象[arg1, arg2, ...]
做爲被調用函數的參數傳入。下面是隱式調用的例子
function increment(number) { return ++number; } increment.call(undefined, 10); // => 11 increment.apply(undefined, [10]); // => 11
increment.call()
和increment.apply()
都用參數10
調用了這個自增函數。
二者的區別是.call()
接受一組參數,例如myFunction.call(thisValue, 'value1', 'value2')
。而.apply()
接受的一組參數必須是一個相似數組的對象,例如myFunction.apply(thisValue, ['value1', 'value2']
)。
在隱式調用.call()或.apply()中,this是第一個參數
很明顯,在隱式調用中,this
做爲第一個參數傳遞給.call()
或.apply()
。
var rabbit = { name: 'White Rabbit' }; function concatName(string) { console.log(this === rabbit); // => true return string + this.name; } concatName.call(rabbit, 'Hello '); // => 'Hello White Rabbit' concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'
當應該使用特定上下文執行函數時,隱式調用很是有用。例如爲了解決方法調用時,this
老是window
或嚴格模式下的undefined
的上下文問題。隱式調用能夠用於模擬在一個對象上調用某個方法。
function Runner(name) { console.log(this instanceof Rabbit); // => true this.name = name; } function Rabbit(name, countLegs) { console.log(this instanceof Rabbit); // => true Runner.call(this, name); this.countLegs = countLegs; } const myRabbit = new Rabbit('White Rabbit', 4); myRabbit; // { name: 'White Rabbit', countLegs: 4 }
Rabbit
中的Runner.call(this, name)
隱式調用了父類的函數來初始化這個對象。
綁定函數是與對象鏈接的函數。一般使用.bind()
方法從原始函數建立。原始函數和綁定函數共享相同的代碼和做用域,但執行時上下文不一樣。
方法 myFunc.bind(thisArg[, arg1[, arg2[, ...]]])
接受第一個參數thisArg
做爲綁定函數執行時的上下文,而且它接受一組可選的參數 arg1, arg2, ...
做爲被調用函數的參數。它返回一個綁定了thisArg
的新函數。
function multiply(number) { 'use strict'; return this * number; } const double = multiply.bind(2); double(3); // => 6 double(10); // => 20
bind(2)
返回一個新的函數對象double
,double
綁定了數字2
。multiply
和double
具備相同的代碼和做用域。
與.apply()
和.call()
方法相反,它不會當即調用該函數,.bind()
方法只返回一個新函數,在以後被調用,只是this已經
被提早設置好了。
在調用綁定函數時,this
是.bind()
的第一個參數。
.bind()
的做用是建立一個新函數,調用該函數時,將上下文做爲傳遞給.bind()
的第一個參數。它是一種強大的技術,使我們能夠建立一個定義了this
值的函數。
來看看,如何在如何在綁定函數設置 this
const numbers = { array: [3, 5, 10], getNumbers: function() { return this.array; } }; const boundGetNumbers = numbers.getNumbers.bind(numbers); boundGetNumbers(); // => [3, 5, 10] // Extract method from object const simpleGetNumbers = numbers.getNumbers; simpleGetNumbers(); // => undefined (嚴格模式下報錯)
numbers.getNumbers.bind(numbers)
返回綁定numbers
對象boundGetNumbers
函數。boundGetNumbers()
調用時的this
是number
對象,並可以返回正確的數組對象。
能夠將函數numbers.getNumbers
提取到變量simpleGetNumbers
中而不進行綁定。在以後的函數調用中simpleGetNumbers()
的this
是window
(嚴格模式下爲undefined
),不是number
對象。在這個狀況下,simpleGetNumbers()
不會正確返回數組。
.bind()
建立一個永久的上下文連接,並始終保持它。 一個綁定函數不能經過.call()
或者.apply()
來改變它的上下文,甚至是再次綁定也不會有什麼做用。
只有綁定函數的構造函數調用才能更改已經綁定的上下文,可是很不推薦的作法(構造函數調用必須使用常規的非綁定函數)。
下面示例建立一個綁定函數,而後嘗試更改其已預先定義好的上下文
function getThis() { 'use strict'; return this; } const one = getThis.bind(1); // 綁定函數調用 one(); // => 1 // 使用帶有.apply()和.call()的綁定函數 one.call(2); // => 1 one.apply(2); // => 1 // 再次綁定 one.bind(2)(); // => 1 // 以構造函數的形式調用綁定函數 new one(); // => Object
只有new one()
改變了綁定函數的上下文,其餘方式的調用中this
老是等於1。
箭頭函數用於以更短的形式聲明函數,並在詞法上綁定上下文。它能夠這樣使用
const hello = (name) => { return 'Hello ' + name; }; hello('World'); // => 'Hello World' // Keep only even numbers [1, 2, 5, 6].filter(item => item % 2 === 0); // => [2, 6]
箭頭函數語法簡單,沒有冗長的function
關鍵字。當箭頭函數只有一條語句時,甚至能夠省略return
關鍵字。
箭頭函數是匿名的,這意味着name
屬性是一個空字符串''
。這樣它就沒有詞法上函數名(函數名對於遞歸、分離事件處理程序很是有用)
同時,跟常規函數相反,它也不提供arguments
對象。可是,這在ES6中經過rest parameters
修復了:
const sumArguments = (...args) => { console.log(typeof arguments); // => 'undefined' return args.reduce((result, item) => result + item); }; sumArguments.name // => '' sumArguments(5, 5, 6); // => 16
this 定義箭頭函數的封閉上下文
箭頭函數不會建立本身的執行上下文,而是從定義它的外部函數中獲取 this
。 換句話說,箭頭函數在詞彙上綁定 this
。
下面的例子說明了這個上下文透明的特性:
class Point { constructor(x, y) { this.x = x; this.y = y; } log() { console.log(this === myPoint); // => true setTimeout(()=> { console.log(this === myPoint); // => true console.log(this.x + ':' + this.y); // => '95:165' }, 1000); } } const myPoint = new Point(95, 165); myPoint.log();
setTimeout
使用與log()
方法相同的上下文(myPoint
對象)調用箭頭函數。正如所見,箭頭函數從定義它的函數繼承上下文。
若是在這個例子裏嘗試用常規函數,它建立本身的上下文(window
或嚴格模式下的undefined
)。所以,要使相同的代碼正確地使用函數表達式,須要手動綁定上下文:setTimeout(function(){…}.bind(this))
。這很冗長,使用箭頭函數是一種更簡潔、更短的解決方案。
若是箭頭函數在最頂層的做用域中定義(在任何函數以外),則上下文始終是全局對象(瀏覽器中的 window):
onst getContext = () => { console.log(this === window); // => true return this; }; console.log(getContext() === window); // => true
箭頭函數一勞永逸地與詞彙上下文綁定。 即便修改上下文,this
也不能被改變:
const numbers = [1, 2]; (function() { const get = () => { console.log(this === numbers); // => true return this; }; console.log(this === numbers); // => true get(); // => [1, 2] // Use arrow function with .apply() and .call() get.call([0]); // => [1, 2] get.apply([0]); // => [1, 2] // Bind get.bind([0])(); // => [1, 2] }).call(numbers);
不管如何調用箭頭函數get
,它老是保留詞彙上下文numbers
。 用其餘上下文的隱式調用(經過 get.call([0])
或get.apply([0])
)或者從新綁定(經過.bind()
)都不會起做用。
箭頭函數不能用做構造函數。 將它做爲構造函數調用(new get()
)會拋出一個錯誤:TypeError: get is not a constructor
。
你可能但願使用箭頭函數來聲明一個對象上的方法。箭頭函數的定義相比於函數表達式短得多:(param) => {...} instead of function(param) {..}
。
來看看例子,用箭頭函數在Period類上定義了format()
方法:
function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = () => { console.log(this === window); // => true return this.hours + ' hours and ' + this.minutes + ' minutes'; }; const walkPeriod = new Period(2, 30); walkPeriod.format(); // => 'undefined hours and undefined minutes'
因爲format
是一個箭頭函數,而且在全局上下文(最頂層的做用域)中定義,所以 this
指向window
對象。
即便format
做爲方法在一個對象上被調用如walkPeriod.format()
,window
仍然是此次調用的上下文。之因此會這樣是由於箭頭函數有靜態的上下文,並不會隨着調用方式的改變而改變。
該方法返回'undefined hours和undefined minutes'
,這不是我們想要的結果。
函數表達式解決了這個問題,由於常規函數確實能根據實際調用改變它的上下文:
function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = function() { console.log(this === walkPeriod); // => true return this.hours + ' hours and ' + this.minutes + ' minutes'; }; const walkPeriod = new Period(2, 30); walkPeriod.format(); // => '2 hours and 30 minutes'
walkPeriod.format()
是一個對象上的方法調用,它的上下文是walkPeriod
對象。this.hours
等於2
,this.minutes
等於30
,因此這個方法返回了正確的結果:'2 hours and 30 minutes'
。
原文:https://dmitripavlutin.com/ge...
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
爲函數調用對this
影響最大,從如今開始不要問本身:
this 是從哪裏來的?
而是要看看
函數是怎麼被調用的?
對於箭頭函數,須要想一想
在這個箭頭函數被定義的地方,this是什麼?
這是處理this
時的正確想法,它們可讓你免於頭痛。
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
https://github.com/qq44924588...
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。