1. this 的奥秘

很多时候, JS 中的 this 对付咱们的初学者很随意马虎产生困惑不解。
this 的功能很强大,但须要一定付出才能逐步理解它。

对Java、PHP或其他标准措辞来看,this 表示类方法中当前工具的实例。
大多数情形下,this 不能在方法之外利用,这样就比较不会造成稠浊。

php定义类中thisJavaScript中this毕竟指向什么 React

在J要中情形就有所不同: this表示函数确当前实行高下文,JS 中函数调用紧张有以下几种办法:

函数调用: alert('Hello World!')方法调用: console.log('Hello World!')布局函数: new RegExp('\\d')隐式调用: alert.call(undefined, 'Hello World!')

每种调用类型以自己的办法定义高下文,以是就很随意马虎产生稠浊。

此外,严格模式也会影响实行高下文。

理解this关键是要清楚的知道函数调用及其如何影响高下文。

本文紧张解释函数的调用办法及如何影响 this,并且解释实行高下文的常见陷阱。

在开始之前,先知道几个术语:

调用函数正在实行创建函数体的代码,或者只是调用函数。
例如,parseInt函数调用是parseInt('15')。

函数调用:实行构成函数主体的代码:例如,parseInt函数调用是parseInt('15')。
调用的高下文:指 this 在函数体内的值。
例如,map.set('key', 'value')的调用高下文是 map。
函数的浸染域:是在函数体中可访问的变量、工具和函数的凑集。

2. 函数调用

当一个表达式为函数接着一个(,一些用逗号分隔的参数以及一个)时,函数调用被实行,例如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”。

2.1. 在函数调用中的this

this 在函数调用中是一个全局工具

局工具由实行环境决定。
在浏览器中,this是 window 工具。

image

在函数调用中,实行高下文是全局工具。

再来看看下面函数中的高下文又是什么鬼:

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>

2.2 严格模式下的函数调用 this 又是什么样的

this 在严格模式下的函数调用中为 undefined

严格模式是在 ECMAScript 5.1中引入的,它供应了更好的安全性和更强的缺点检讨。

要启用严格模式,函数头部写入use strict 即可。

启用后,严格模式会影响实行高下文,this 在常规函数调用中值为undefined。
与上述情形2.1相反,实行高下文不再是全局工具。

image

严格模式函数调用示例:

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

2.3 陷阱: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

3. 方法调用

方法是存储在工具属性中的函数。
例如

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); // 函数调用

理解函数调用和方法调用之间的差异有助于精确识别高下文。

3.1 方法调用中 this 是肿么样

在方法调用中,this是拥有这个方法的工具

当调用工具上的方法时,this就变成了工具本身。

image

创建一个工具,该工具有一个递增数字的方法

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'

3.2 陷阱:将方法与其工具分离

方法可以从工具中提取到一个单独的变量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);

4. 布局函数调用

当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()。

4.1. 布局函数中的 this

在布局函数调用中 this 指向新创建的工具

布局函数调用的高下文是新创建的工具。
它利用布局函数的参数初始化新的工具,设定属性的初始值,添加事宜处理函数等等。

image

来看看下面示例中的高下文

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'

4.2. 陷阱: 忘了利用 new

有些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。

给大家推举一个好用的BUG监控工具 Fundebug。

5. 隐式调用

利用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'])。

5.1. 隐式调用中的this

在隐式调用.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)隐式调用了父类的函数来初始化这个工具。

6. 绑定函数

绑定函数是与工具连接的函数。
常日利用.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已经被提前设置好了。

6.1. 绑定函数中的this

在调用绑定函数时,this是.bind()的第一个参数。

.bind()的浸染是创建一个新函数,调用该函数时,将高下文作为通报给.bind()的第一个参数。
它是一种强大的技能,使咱们可以创建一个定义了this值的函数。

image

来看看,如何在如何在绑定函数设置 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()不会精确返回数组。

6.2 紧密的高下文绑定

.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。

7. 箭头函数

箭头函数用于以更短的形式声明函数,并在词法上绑定高下文。
它可以这样利用

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

7.1. 箭头函数中的this

this 定义箭头函数的封闭高下文

箭头函数不会创建自己的实行高下文,而是从定义它的外部函数中获取 this。
换句话说,箭头函数在词汇上绑定 this。

image

下面的例子解释了这个高下文透明的特性:

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。

7.2. 陷阱: 用箭头函数定义方法

你可能希望利用箭头函数来声明一个工具上的方法。
箭头函数的定义比较于函数表达式短得多:(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/gentle-explanation-of-this-in-javascript/

代码支配后可能存在的BUG没法实时知道,事后为理解决这些BUG,花了大量的韶光进行log 调试,这边顺便给大家推举一个好用的BUG监控工具 Fundebug。

总结

为函数调用对this影响最大,从现在开始不要问自己:

this 是从哪里来的?

而是要看看

函数是怎么被调用的?

对付箭头函数,须要想想

在这个箭头函数被定义的地方,this是什么?

这是处理this时的精确想法,它们可以让你免于头痛。

来源:https://dmitripavlutin.com/gentle-explanation-of-this-in-javascript/