JS中this的各个环境下指向(二)

8月15日学习笔记

JS中this在各个环境下的指向

绑定函数

绑定函数是与对象连接的函数。通常使用.bind() 方法从原始函数创建。
原始函数和绑定函数共享相同的代码和作用域,但执行时上下文不同。

方法foo.bind(thisArg[,arg1[,arg2[,...]]]) 接受第一个参数thisArg作为绑定函数执行其上下文,并且它接受一组可选的参数arg1,arg2,... 作为被调用函数的参数。它返回了一个绑定thisArg 的新函数:

1
2
3
4
5
6
7
function foo(name){
'use strict'
return this *name;
}
const double=foo.bind(2);
console.log(double(3));//=>6
console.log(double(10));//=>20

在这段代码中bind(2) 返回了一个新的函数对象double ,double 绑定了数字2foodouble 具有相同的代码和作用域。
.apply().call() 方法相反,它不会立即调用该函数,.call() 方法只返回一个新函数,在之后被调用,this 已经被提前设置好了。

绑定函数中的this

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

.bind() 的作是创建一个新函数,调用该函数时,将上下文作为传递给.bind() 的第一个参数。这种方法可以创建一个定义了this 值的函数。

在绑定函数设置this :

1
2
3
4
5
6
7
8
const nums={
array:[3,4,5],
getNums : function(){
return this.array;
}
};
const bindGetNums=nums.getNums.bind(nums);
console.log(bindGetNums());//=>array:[3,4,5]

nums.getNums.bind(nums) 返回绑定nums 对象的bindGetNums 函数。
bindGetNums() 调用时的this是nums对象,能够返回正确的数组对象。

而如果将nums.getNums 提取到变量而不绑定。则在之后的函数调用中的this是window而不是nums对象。在这个情况下,不会正确返回数组

紧密的上下文绑定

.bind()创建一个永久的上下文链接,并始终保持它。一个绑定函数不能通过.call() 或者.apply() 来改变其上下文,甚至再次绑定也无用。只有绑定函数的构造函数调用才能更改已经绑定的上下文,但是不推荐这样使用(构造函数必须使用常规的非绑定函数)

例如:

1
2
3
4
5
6
7
8
9
10
11
function getThis(){
'use strict'
return this;
}
const one=getThis.bind(1);
console.log(one());

console.log(one.call(2)); //=>1
console.log(one.apply(2));//=>1
console.log(one.bind(2)());//=>1
console.log(new one());//=>getThis {}

只有new one() 改变了绑定函数的上下文,其他方式的调用中this 总等于1

###

箭头函数

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

它可以这样使用:

1
2
3
4
5
6
const hello=(name)=>{
return 'hello'+name;
}
console.log(hello('xiaomin')); //=>helloxiaomin
const a=[1,2,5,6].filter(item=>item%2===0);
console.log(a);//=>[2,6]

箭头函数没有function 关键字。当箭头函数只有一条语句时,可以省略return 关键字。
箭头函数是匿名的 ,这意味着name 属性是一个空字符串'' 。这样它就没有词法上的函数名,同时,跟常规函数相反,它也不提供arguments 对象。但是这在ES6中通过rest parameters 修复了:

1
2
3
4
5
6
const sum=(...args)=>{
console.log(typeof arguments); //=>'Object'
return args.reduce((result,item)=>result+item);
}
console.log(sum.name); //=>sum
console.log(sum(5,5,6));//=>16

箭头函数中的this

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

箭头函数不会创建自己的执行上下文,而是从定义它的外部函数中获取this 。也就是箭头函数这种词汇上绑定this
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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() 方法相同的上下文调用箭头函数。正如所见,箭头函数从定义它的函数继承上下文。

如果在这个例子中尝试使用常规函数,它创建自己的上下文(window或严格模式下的undefined )。因此,要使用相同的代码正确的使用函数表达式,需要手动绑定上下文:setTimeOut(function(){...}.bind(this) 。这很冗长,使用箭头函数是一种更简洁更短的解决方案。

如果箭头函数在其他函数之外定义,那么它的上下文就是全局变量(window 或者严格模式下的undefined

箭头函数一劳永逸的与词汇上下文绑定。即使修改上下文,this 也不能被改变:

1
2
3
4
5
6
7
8
9
10
11
12
13
const nums =[1,2];
(function(){
const get=()=>{
console.log(this === nums); //=>true
return this;
};
console.log(this===nums);//=>true
console.log(get());//=>[1,2]
get.call([0]);//=>[1,2]
get.apply([0]);//=>[1,2]
//bind
get.bind([0]);//=>[1,2]
}).call(nums);

无论如何调用箭头函数get ,它总是保留词汇上下文nums 。用其他上下文的隐式调用(通过get.call([0])get.apply([0]) 或者通过重新绑定都不会起作用

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

你可能希望使用箭头函数来声明一个对象上的方法。箭头函数的定义相比于函数表达式短得多:(param)=>{…} instead of function(param){…}

例如,用箭头函数在Period类上定义了format() 方法:

1
2
3
4
5
6
7
8
9
10
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(); //=>"undefinedhours and undefinedminutes"

由于format()是一个箭头函数,并且在全局上下文中定义,因此this指向window对象。
即使format() 作为方法在一个对象上被调用,window 仍然是这次调用的上下文,之所以这样是因为箭头函数有静态的上下文,不会随着调用方式的改变而改变。
函数表达式解决了这个问题,因为常规函数能够通过实际调用改变其上下文。

1
2
3
4
5
6
7
8
9
10
function Period(hours,minutes){
this.hours=hours;
this.minutes=minutes;
}
Period.prototype.format=function(){
console.log(this===window); //=>false
return this.hours+'hours and ' +this.minutes+'minutes';
};
const WalkPeriod =new Period(2,30);
WalkPeriod.format();//=>"2hours and 30minutes"

WalkPeriod.format() 是一个对象上的方法调用,它的上下文是WalkPeriod对象。这个方法返回了正确的结果

总结:

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

this从何而来

而是要知道:

函数是如何被调用的?

对于箭头函数:

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

原文地址