立柱盖

JavaScript中“this”的温和解释

已更新 2020年10月24日

1.这其中的奥秘

关键字对我来说一直是个谜。

从Java、PHP或其他背景标准语言,是类方法中当前对象的实例。不能在方法外使用,并且这样简单的方法不会造成混淆。

在JavaScript中,情况有所不同:是函数调用(也称为执行)的上下文。该语言有4种函数调用类型:

  • 函数调用:alert(“你好,世界!”)
  • 方法调用:console.log(“你好,世界!”)
  • 构造函数调用:新RegExp('\\d')
  • 间接调用:alert.call(未定义,“Hello World!”)

每个调用类型都以其方式定义上下文,因此行为与开发人员预期的不同。

JavaScript中的奥秘

此外严格模式也会影响执行上下文。

理解的关键关键字可以清楚地查看函数调用及其对上下文的影响。

本文重点介绍调用解释,函数调用如何影响并展示了识别.

开始之前,让我们熟悉几个术语:

  • 调用函数的代码是执行生成函数体的代码,或者简单地打电话函数。例如解析Int功能调用解析Int('15').
  • 上下文的值在函数体中。
  • 范围函数的是函数体中可访问的变量和函数的集合。

2.函数调用

函数调用当计算为函数对象的表达式后跟左括号时执行(,以逗号分隔的参数表达式列表和右括号)。例如解析Int('18').

函数调用的一个简单示例:


函数hello(name){
return'Hello'+name+'!';
}
//函数调用
const消息=hello('World');

hello(“世界”)是函数调用:你好表达式求值为函数对象,后跟一对带“世界”参数。

函数调用表达式不能是属性访问器 对象myFunc(),这将创建方法调用。例如[1,5].连接(',')函数调用,但方法调用。请记住他们之间的区别。

一个更高级的示例是IIFE公司(立即调用的函数表达式):


//IIFE公司
const消息=(函数(名称){
return'Hello'+name+'!';
})(“世界”);

IIFE也是一个函数调用:第一对括号(函数(名称){…})是计算为函数对象的表达式,后跟一对带“世界”参数:(“世界”).

2.1. 这是在函数调用中

全局对象在函数调用中。

全局对象由执行环境确定。在浏览器中,全局对象是窗口对象。

这是JavaScript函数调用

在函数调用中,执行上下文是全局对象。

让我们检查以下函数中的上下文:


函数和(a,b){
console.log(这个===窗口);//=>真实的
this.myNumber=20;//将“myNumber”属性添加到全局对象
返回a+b;
}
//sum()作为函数调用
//sum()中的this是一个全局对象(窗口)
总和(15,16);//=>31
window.myNumber;//=>20

当时总和(15,16)被调用时,JavaScript会自动设置作为全局对象(窗口在浏览器中)。

何时在任何函数作用域(最上面的作用域:全局执行上下文)之外使用,它也等于全局对象:


console.log(这个===窗口);//=>真实的
this.myString='你好,世界!';
console.log(window.myString);//=>'你好,世界!”


<!-- 在html文件中-->
<script type=“text/javascript”>
console.log(这个===窗口);//=>真实的
</script>

2.2. 这是一个函数调用,严格模式

未定义在严格模式下的函数调用中

严格模式可启动ECMAScript 5.1版,这是JavaScript的受限变体。它提供了更好的安全性和更强的错误检查。

要启用严格模式,请放置指令'使用严格'在函数体的顶部。

启用后,严格模式会影响执行上下文成为未定义在常规函数调用中。执行上下文是全局对象,与上述情况相反2.1.

这是在JavaScript函数调用中,严格模式

在严格模式下调用的函数示例:


函数乘法(a,b){
'使用严格';//启用严格模式
console.log(此===未定义);//=>真实的
返回a*b;
}
//启用严格模式的multiply()函数调用
//multiply()中的this未定义
乘法(2,5);//=>10

何时乘法(2,5)在严格模式下作为函数调用,未定义.

严格模式不仅在当前范围内有效,而且在内部范围内也有效(对于内部声明的所有函数):


函数execute(){
'使用严格';
函数concat(str1,str2){
//也启用了严格模式
console.log(此===未定义);//=>真实的
返回str1+str2;
}
//concat()在严格模式下作为函数调用
//concat()中的此未定义
concat('Hello','World!');//=>“你好,世界!”
}
执行();

'使用严格'坐在执行主体,在其范围内启用严格模式。因为凹面(concat)在中声明执行范围,它继承了严格模式。还有祈祷concat(“你好”,“世界!”)制造成为未定义.

单个JavaScript文件可以包含严格模式和非严格模式。因此,对于相同的调用类型,在单个脚本中可能有不同的上下文行为:


函数nonStrictSum(a,b){
//非限制模式
console.log(这个===窗口);//=>真实的
返回a+b;
}
函数strictSum(a,b){
'使用严格';
//已启用严格模式
console.log(此===未定义);//=>真实的
返回a+b;
}
//nonStrictSum()在非限制模式下作为函数调用
//nonStrictSum()中的这个是窗口对象
非严格总和(5,6);//=>11
//strictSum()在严格模式下作为函数调用
//strictSum()中的此未定义
严格总和(8,12);//=>20

2.3。陷阱:这是内在功能

⚠️ 函数调用的一个常见陷阱是认为在内部函数中与在外部函数中相同。

👍 内部函数(箭头函数除外)的上下文仅取决于其自身的调用类型,而不取决于外部函数的上下文。

制造具有所需的值,使用间接调用(使用.call().apply(),请参阅5)或创建绑定函数(使用.bind(),请参阅6).

以下示例计算两个数字的总和:


常数={
数量A:5,
数量B:10,
sum:函数(){
console.log(这===数字);//=>真实的
函数calculate(){
//这是窗口或在严格模式下未定义
console.log(这===数字);//=>
返回this.numberA+this.number B;
}
return calculate();
}
};
numbers.sum();//=>NaN或在严格模式下抛出TypeError

⚠️numbers.sum()是对对象的方法调用(请参见三。)因此等于数字.计算()函数在内部定义总和(),所以您可能希望作为数字调用时的对象计算()也是。

计算()是函数调用(但是方法调用),因此这里是全局对象窗口(案例2.1.)或未定义严格模式下(案例2.2.). 即使外部功能numbers.sum()上下文为数字物体,它在这里没有影响。

的调用结果numbers.sum()NaN公司(或抛出错误TypeError:无法读取未定义的属性“numberA”严格模式下)。绝对不是预期的结果5 + 10 = 15都是因为计算()未正确调用。

👍为了解决这个问题,计算()函数必须使用与numbers.sum()方法,以访问此编号A此编号B属性。

一种解决方案是手动更改计算()打电话给想要的人calculate.call(this)(函数的间接调用,请参阅第节5):


常数={
数量A:5,
数量B:10,
sum:函数(){
console.log(这===数字);//=>真实的
函数calculate(){
console.log(这===数字);//=>真实的
返回this.numberA+this.number B;
}
//使用.call()方法修改上下文
return calculate.call(this);
}
};
numbers.sum();//=>15

calculate.call(this)执行计算()函数,但还将上下文修改为指定为第一个参数的值。

现在此编号A+此编号B与相同编号A+编号B。该函数返回预期结果5 + 10 = 15.

另一个稍微好一点的解决方案是使用箭头功能:


常数={
数量A:5,
数量B:10,
sum:函数(){
console.log(这===数字);//=>真实的
常数计算=()=>{
console.log(这===数字);//=>真实的
返回this.numberA+this.number B;
}
return calculate();
}
};
numbers.sum();//=>15

箭头函数解析词汇上,或者换句话说,使用的值numbers.sum()方法。

3.方法调用

A类方法是存储在对象属性中的函数。例如:


const myObject={
//helloMethod是一种方法
helloMethod:function(){
return“你好,世界!”;
}
};
const消息=myObject.helloMethod();

hello方法是一种方法myObject(我的对象)。使用属性访问器myObject.hello方法以访问该方法。

方法调用当表达式的形式为属性访问器求值为函数对象的后接左括号(,以逗号分隔的参数表达式列表和右括号).

回顾前面的示例,myObject.helloMethod()是的方法调用hello方法在对象上myObject(我的对象).

方法调用的更多示例包括:[1,2].加入(',')/\s/.test(“美丽的世界”).

了解两者之间的差异函数调用(参见第节2)和方法调用很重要!

方法调用需要属性访问器调用函数的窗体(对象myFunc()对象['myFunc']()),而函数调用不(我的函数()).


const words=['Hello','World'];
words.join(',');//方法调用
常量对象={
我的方法(){
return new Date().toString();
}
};
obj.myMethod();//方法调用
const func=obj.myMethod;
func();//函数调用
parseFloat('16.6');//函数调用
是NaN(0);//函数调用

3.1. 这是在方法调用中

拥有该方法的对象在方法调用中

在对象上调用方法时,是拥有该方法的对象。

这是JavaScript方法调用

让我们使用递增数字的方法创建一个对象:


常数计算={
数字:0,
增量(){
console.log(这===计算);//=>真实的
this.num+=1;
返回this.num;
}
};
//方法调用。这是calc
计算增量();//=>1
计算增量();//=>2

打电话计算增量()使上下文增量函数为计算对象。所以使用this.num这个增加数字属性效果很好。

让我们来看另一个案例。JavaScript对象从其原型。在对象上调用继承的方法时,调用的上下文仍然是对象本身:


const myDog=对象创建({
sayName(){
console.log(this===myDog);//=>真实的
返回this.name;
}
});
myDog.name='Milo';
//方法调用。这是我的狗
myDog.sayName();//=>'米洛

对象.create()创建新对象我的狗并从第一个参数设置其原型。我的狗对象继承说出姓名方法。

何时myDog.sayName()执行,我的狗是调用的上下文。

2015年ECMAScript语法,方法调用上下文也是实例本身:


类行星{
建造师(姓名){
this.name=名称;
}
获取名称(){
console.log(此===接地);//=>真实的
返回this.name;
}
}
const earth=新行星(“地球”);
//方法调用。背景是地球
earth.getName();//=>'地球'

3.2. 陷阱:将方法与其对象分离

⚠️ 可以将方法从对象提取到单独的变量中仅const=myObj.myMethod。单独调用方法时单独(),与原始对象分离,您可能会认为是对象myObject(我的对象)在此基础上定义方法。

👍 正确地说,如果在没有对象的情况下调用方法,则会发生函数调用,其中是全局对象窗口未定义在严格模式下(请参见2.12.2).

绑定函数仅const=myObj.myMethod.bind(myObj)(使用.bind(),请参阅6)通过绑定修复上下文拥有该方法的对象。

以下示例定义宠物构造函数并生成其实例:我的猫.然后设置超时()1秒日志后我的猫对象信息:


功能宠物(类型,腿){
this.type=类型;
this.legs=腿;
this.logInfo=函数(){
控制台.log(this===myCat);//=>
console.log(`${this.type}有${this.legs}腿`);
}
}
const myCat=新宠物(“猫”,4);
//日志“未定义的具有未定义的腿”
//或在严格模式下抛出TypeError
setTimeout(myCat.logInfo,1000);

⚠️ 你可能会认为setTimeout(myCat.logInfo,1000)将调用myCat.log信息(),它应该记录有关我的猫对象。

不幸的是,当作为参数传递时,该方法与其对象分离:设置超时(myCat.logInfo)以下情况等效:


setTimeout(myCat.logInfo);
//等于:
const extractedLogInfo=myCat.logInfo;
setTimeout(extractedLogInfo);

当分开时logInfo(日志信息)作为函数调用,是全局对象或未定义严格模式下(但 我的猫对象)。因此,对象信息没有正确记录。

👍 函数与对象的边界使用.bind()方法(请参见6). 如果分隔的方法与绑定我的猫对象,上下文问题得到解决:


功能宠物(类型,腿){
this.type=类型;
this.legs=腿;
this.logInfo=函数(){
控制台.log(this===myCat);//=>真实的
console.log(`${this.type}有${this.legs}腿`);
};
}
const myCat=新宠物(“猫”,4);
//创建绑定函数
const boundLogInfo=myCat.logInfo.bind(myCat);
//日志“猫有四条腿”
setTimeout(boundLogInfo,1000);

myCat.logInfo.bind(myCat)返回一个执行方式与logInfo(日志信息),但有作为我的猫甚至在函数调用中。

另一种解决方案是定义日志信息()方法作为箭头函数,它绑定词汇上:


功能宠物(类型,腿){
this.type=类型;
this.legs=腿;
this.logInfo=()=>{
控制台.log(this===myCat);//=>真实的
console.log(`${this.type}有${this.legs}腿`);
};
}
const myCat=新宠物(“猫”,4);
//日志“猫有四条腿”
setTimeout(myCat.logInfo,1000);

如果您想使用类和绑定对于方法中的类实例,使用箭头函数作为类属性:


班级宠物{
构造函数(类型,支腿){
this.type=类型;
this.legs=腿;
}
logInfo=()=>{
控制台.log(this===myCat);//=>真实的
console.log(`${this.type}有${this.legs}腿`);
}
}
const myCat=新宠物(“猫”,4);
//日志“猫有四条腿”
setTimeout(myCat.logInfo,1000);

4.构造函数调用

构造函数调用在以下情况下执行新的关键字后面是计算为函数对象的表达式,即左括号(,以逗号分隔的参数表达式列表和右括号).

施工调用示例:新宠物('cat',4),新RegExp('\\d').

本例声明了一个函数国家,然后将其作为构造函数调用:


功能国家(姓名,旅行){
this.name=名称?name:'英国';
this.traveled=布尔值(移动);//转换为布尔值
}
Country.prototype.travel=函数(){
this.traveled=真;
};
//构造函数调用
const france=新国家('france',false);
//构造函数调用
const unitedKingdom=新国家;
法国.travel();//前往法国

新国家('France',false)是的构造函数调用国家功能。此调用创建一个新对象,该对象名称属性为“法国”.

如果调用构造函数时没有参数,则可以省略括号对:新国家/地区.

从ECMAScript 2015开始,JavaScript允许使用语法:


class城市{
建造师(姓名,旅行){
this.name=名称;
this.traveled=假;
}
行程(){
this.traveled=真;
}
}
//构造函数调用
const paris=新城市('paris',false);
巴黎旅游();

新城(“巴黎”)是构造函数调用。对象的初始化由类中的一个特殊方法处理:建造师,其中有作为新创建的对象。

构造函数的作用是初始化实例。构造函数调用创建一个新的空对象,该对象继承构造函数原型的属性。

当属性访问器myObject.my函数前面是新的关键字,JavaScript执行构造函数调用,但是不是方法调用.

例如新建myObject.myFunction():首先使用属性访问器提取函数extractedFunction=myObject.myFunction,然后作为构造函数调用以创建新对象:新建extractedFunction().

4.1. 这是在构造函数调用中

新创建的对象在构造函数调用中

构造函数调用的上下文是新创建的对象。构造函数使用来自构造函数参数的数据初始化对象,为属性设置初始值,附加事件处理程序等。

这是JavaScript构造函数调用

让我们检查以下示例中的上下文:


函数Foo(){
//这是fooInstance
this.property='默认值';
}
//构造函数调用
const fooInstance=新Foo();
fooInstance.property;//=>'默认值'

新Foo()正在上下文所在的位置进行构造函数调用fooInstance.内部对象已初始化:此属性指定了默认值。

使用时会发生相同的情况语法(在ES2015中可用),只有初始化发生在建造师方法:


类条形图{
构造函数(){
//这是barInstance
this.property='默认值';
}
}
//构造函数调用
const barInstance=新Bar();
barInstance.properties;//=>'默认值'

新Bar()执行时,JavaScript创建一个空对象,并将其作为构造函数()方法。现在可以使用向对象添加属性关键词:this.property='默认值'.

4.2. 陷阱:忘记新事物

有些JavaScript函数不仅在作为构造函数调用时创建实例,而且在作为函数调用时也创建实例。例如注册Exp:


const reg1=新RegExp('\\w+');
const reg2=RegExp('\\w+');
RegExp的reg1实例;//=>真实的
RegExp的reg2实例;//=>真实的
reg1.source===reg2.source;//=>真实的

执行时新RegExp('\\w+')RegExp('\\w+'),JavaScript创建等效的正则表达式对象。

⚠️ 使用函数调用创建对象是一个潜在的问题(不包括工厂模式),因为当新的缺少关键字。

以下示例说明了该问题:


功能车辆(类型,车轮计数){
this.type=类型;
this.wheelsCount=车轮计数;
返回此;
}
//函数调用
const car=车辆(“汽车”,4);
汽车类型;//=>'汽车'
汽车车轮计数//=>4
car===window//=>真

车辆是一个设置类型车轮计数上下文对象上的属性。执行时车辆(“汽车”,4)一个物体汽车返回,它具有正确的属性:汽车类型“汽车”汽车车轮计数4.

您可能认为它很适合创建和初始化新对象。

然而,窗口函数调用中的对象(请参见2.1.),因此车辆(“汽车”,4)在上设置属性窗口对象。这是一个错误。不会创建新对象。

👍 确保使用新的运算符(如果需要构造函数调用):


功能车辆(类型,车轮计数){
if(!(本例车辆){
抛出错误(“错误:调用不正确”);
}
this.type=类型;
this.wheelsCount=车轮计数;
返回此;
}
//构造函数调用
const car=新车(“car”,4);
car.type//=>“汽车”
汽车车轮计数//=>4
汽车实例Vehicle//=>true
//函数调用。引发错误。
const brokenCar=车辆(“破损车辆”,3);

新车(“汽车”,4)工作良好:创建并初始化一个新对象是因为新的关键字出现在构造函数调用中。

构造函数中添加了验证:这辆车,以确保执行上下文是正确的对象类型-无论何时车辆(“破损汽车”,3)执行时没有新的引发异常:错误:调用错误.

5.间接调用

间接调用在使用调用函数时执行myFun.call()myFun.apply()方法。

JavaScript中的函数是一级对象,这意味着函数就是一个对象。函数对象的类型为功能.

方法列表函数对象具有,.call().apply()用于调用具有可配置上下文的函数。

myFunction.call(thisArg、arg1、arg2…)接受第一个参数此Arg作为调用的上下文和参数列表arg1、args2。。。作为参数传递给被调用函数的。

myFunction.apply(thisArg,[arg1,arg2,…])接受第一个参数此Arg作为调用的上下文和参数数组[arg1,args,…]作为参数传递给被调用函数的。

以下示例演示了间接调用:


函数和(数字1,数字2){
返回number1+number2;
}
sum.call(未定义,10,2);//=>12
sum.apply(未定义,[10,2]);//=>12

sum.call()sum.apply()两者都使用调用函数102论据。

5.1. 这是间接调用

第一个参数属于.call().apply()在间接调用中

在间接调用中,是作为第一个参数传递给.call().apply().

这是JavaScript间接调用

以下示例显示了间接调用上下文:


constrabbit={name:‘白兔’};
函数concatName(字符串){
console.log(这===兔子);//=>真实的
返回字符串+this.name;
}
//间接调用
concatName.call(rabbit,'Hello');//=>'你好,白兔
concatName.apply(rabbit,['Bye']);//=>'再见白兔

当函数应该在特定上下文中执行时,间接调用非常有用。例如,要解决函数调用的上下文问题,其中总是窗口未定义在严格模式下(请参见2.3.). 它可以用于模拟对对象的方法调用(请参阅前面的代码示例)。

另一个实际示例是在ES5中创建类的层次结构以调用父构造函数:


函数Runner(name){
console.log(兔子的这个实例);//=>真实的
this.name=名称;
}
功能兔子(name,countLegs){
console.log(兔子的这个实例);//=>真实的
//间接调用。调用父构造函数。
Runner.call(this,name);
this.countLegs=计数支腿;
}
const-myRabbit=新兔子(“白色兔子”,4);
我的兔子;//{name:'白兔',countLegs:4}

Runner.call(这个名字)里面兔子间接调用父函数来初始化对象。

6.绑定函数

绑定函数是其上下文和/或参数绑定到特定值的函数。使用创建绑定函数.bind()方法。原始函数和绑定函数共享相同的代码和范围,但执行时的上下文和参数不同。

myFunc.bind(thisArg[,arg1,arg2,…)接受第一个参数此Arg作为上下文和可选参数列表arg1、arg2。。。绑定到。.bind()返回上下文绑定到的新函数此Arg和参数arg1、arg2。。。.

以下代码创建了一个绑定函数,然后调用它:


函数乘法(数字){
'使用严格';
返回这个*数字;
}
//使用上下文创建绑定函数
const double=multiply.bind(2);
//调用绑定函数
双(3);//=>6
双(10);//=>20

乘法绑定(2)返回新的函数对象双重的,与数字绑定2.双重的具有相同的代码和范围。

与…相反.apply().call()方法(请参见5),它立即调用函数.bind()方法只返回一个应该稍后使用预定义的值。

6.1. 这在一个绑定函数中

第一个参数属于myFunc.bind(thisArg)调用绑定函数时

的作用.bind()是创建一个新函数,该调用将上下文作为第一个参数传递给.bind()它是一种强大的技术,允许使用预定义的值。

这是JavaScript绑定函数调用

让我们看看如何配置绑定函数的:


常数={
数组:[3,5,10],
获取数字(){
返回this.array;
}
};
//创建绑定函数
const boundGetNumbers=numbers.getNumbers.bind(numbers);
boundGetNumbers();//=>[3, 5, 10]
//从对象提取方法
const simpleGetNumbers=数字.getNumbers;
simpleGetNumbers();//=>未定义或在严格模式下抛出错误

numbers.getNumbers.bind(数字)返回函数绑定GetNumbers绑定到哪个上下文数字.然后绑定GetNumbers()使用调用作为数字并返回正确的数组对象。

功能数字.get数字提取到变量中简单获取数字没有约束力。稍后的函数调用simpleGetNumbers()作为窗口未定义严格模式,但不是数字对象(请参见3.2. 陷阱). 在这种情况下simpleGetNumbers()将无法正确返回数组。

6.2. 紧密的上下文绑定

.bind()制作永久上下文链接并将始终保留它。绑定函数在使用时无法更改其链接上下文.call().apply()在不同的背景下,甚至反弹都没有任何影响。

只有绑定函数的构造函数调用才能更改已绑定的上下文,但这不是您通常会做的事情(构造函数调用必须使用有规律的,非绑定函数)。

以下示例创建了一个绑定函数,然后尝试更改其已预定义的上下文:


函数getThis(){
'使用严格';
返回此;
}
const one=getThis.bind(1);
一();//=>1
一个.调用(2);//=>1
一、应用(2);//=>1
one.bind(2)();//=>1
新建一个();//=>对象

仅限新建()更改绑定函数的上下文。其他类型的调用总是有等于1.

7.箭头功能

箭头函数旨在以较短的形式声明函数,并且词法上绑定上下文。

它可以使用以下方式:


const hello=(name)=>{
return'Hello'+name;
};
你好(“世界”);//=>你好世界'
//只保留偶数
[1,2,5,6].筛选器(项=>项%2===0);//=>[2,6]

箭头函数有简单的语法,没有verbose关键字功能。当arrow函数只有1条语句时,甚至可以省略返回关键字。

箭头功能是匿名的,但它无法推断名称。它没有词法函数名(这对于递归、分离事件处理程序非常有用)。

此外,它也没有提供论据对象,而不是常规函数。失踪者论据使用ES2015修复静止参数:


const-sumArguments=(…args)=>{
console.log(参数类型);//=>'未定义'
return args.reduce((result,item)=>result+item);
};
sumArguments.name//=>“”
sumArguments(5,5,6);//=>16

7.1. 此为箭头函数

封闭上下文箭头函数的定义位置

arrow函数不创建自己的执行上下文,但接受从定义它的外部函数。换句话说,箭头函数解析词汇上。

这是JavaScript中的箭头函数调用

以下示例显示上下文透明度属性:


类点{
构造函数(x,y){
这个.x=x;
this.y=y;
}
日志(){
console.log(this===myPoint);//=>真实的
设置超时(()=>{
console.log(this===myPoint);//=>真实的
console.log(this.x+':'+this.y);//=>'95:165英寸
}, 1000);
}
}
const myPoint=新点(95165);
myPoint.log();

设置超时()使用相同的上下文调用arrow函数(我的观点对象)作为日志()方法。如图所示,箭头函数从定义它的函数“继承”上下文。

本例中的正则函数创建自己的上下文(窗口未定义严格模式下)。因此,要使相同的代码正确地与函数表达式一起工作,需要手动绑定上下文:setTimeout(function(){…}.bind(this))这很冗长,使用箭头函数是一种更简洁的解决方案。

如果箭头函数是在最顶层的作用域中定义的(在任何函数之外),则上下文始终是全局对象(窗口在浏览器中):


const获取上下文=()=>{
console.log(这个===窗口);//=>真实的
返回此;
};
console.log(getContext()===窗口);//=>真实的

箭头函数与词法绑定 一劳永逸.即使使用上下文修改方法也无法修改:


常数=[1,2];
(函数(){
常数获取=()=>{
console.log(这===数字);//=>真实的
返回此;
};
console.log(这===数字);//=>真实的
获取();//=>[1, 2]
//尝试手动更改箭头功能上下文
get.call([0]);//=>[1, 2]
获取应用程序([0]);//=>[1, 2]
get.bind([0])();//=>[1, 2]
}).call(数字);

无论箭头如何工作获取()被称为,它总是保持词汇上下文数字.与其他上下文的间接调用get.call([0]).get.apply([0]),正在重新绑定get.bind([0])()没有效果。

箭头函数不能用作构造函数。将其作为构造函数调用新建get()抛出错误:TypeError:get不是构造函数.

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

⚠️ 您可能希望使用箭头函数来声明对象上的方法。很公平:他们的声明与函数表达式:(参数)=>{…}而不是函数(参数){..}.

这个例子定义了一个方法格式()在一个类上周期使用箭头功能:


功能周期(小时、分钟){
this.hours=小时;
this.minutes=分钟;
}
Period.prototype.format=()=>{
console.log(这个===窗口);//=>真实的
返回this.hours+“hours and”+this.minutes+“minutes”;
};
const walkPeriod=新周期(2,30);
walkPeriod.format();//=>'未定义小时和未定义分钟

格式是一个箭头函数,在全局上下文(最顶部范围)中定义,它具有作为窗口对象。

即使格式作为对象上的方法执行walkPeriod.format(),窗口保留为调用的上下文。之所以会发生这种情况,是因为arrow函数有一个静态上下文,它不会因不同的调用类型而改变。

该方法返回'未定义小时和未定义分钟',这不是预期的结果。

👍 函数表达式解决了这个问题,因为正则函数会改变上下文取决于调用:


功能周期(小时、分钟){
this.hours=小时;
this.minutes=分钟;
}
Period.prototype.format=函数(){
console.log(this===walkPeriod);//=>真实的
返回this.hours+“hours and”+this.minutes+“minutes”;
};
const walkPeriod=新周期(2,30);
walkPeriod.format();//=>'2小时30分钟

walkPeriod.format()是对对象的方法调用(请参见3.1.)根据上下文walkPeriod(步行周期)对象。这个小时计算结果为2这个小坚果30,因此该方法返回正确的结果:“2小时30分钟”.

8.结论

因为函数调用对,从现在开始不要问问自己:

在哪里取自?

但是问问自己:

如何调用*`函数*?

对于箭头功能,请自问:

是什么在箭头函数所在的外部函数内定义?

这种心态是正确的这样你就不会头疼了。

如果您有一个有趣的上下文陷阱示例,或者只是在案例中遇到了困难,请在下面写下评论,让我们一起讨论!

喜欢这个帖子吗?请分享!

德米特里·帕夫林

关于德米特里·帕夫林

软件开发人员,有时是作家。我的日常生活包括(但不限于)喝咖啡、编码、写作、克服无聊😉. 住在阳光明媚的巴塞罗那。🇪🇸