This的指向解析
JavaScript中的this有时候比较难以理解,需要总结一下规律;并详细说明apply、call、bind的区别。
# This的指向问题
this 的绑定规则总共有一下几种形式:
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
- 箭头函数绑定
优先级:new 绑定 -> 显式绑定 -> 隐式绑定 -> 默认绑定
# 默认绑定
在全局环境下直接定义的函数中调用this,会指向全局对象。
// 非严格模式
function a(){
console.log(this) // window | 全局对象
}
2
3
4
但是严格模式下,只能绑定到undefined。
// 严格模式
'use strict'
function a(){
console.log(this) // undefined
}
2
3
4
5
# 隐式绑定
函数调用时会指向上下为对象。
function _b(){
console.log(this, 'b');
}
var obj = {
a: '123',
b: function(){
console.log(this, 'obj.b'); // obj // 隐式绑定,上下文对象是obj
_b() // window // 隐式绑定丢失,查找到上一级(例子里刚好是全局对象)
},
c: null,
d: _b
}
obj.c = _b
console.log(obj.b()); // window
console.log(obj.c()); // obj
console.log(obj.d()); // obj
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
例子中打印的log如下:
this.html:23 {a: '123', b: *ƒ, c: *ƒ*, d: *ƒ*}'obj.b' this.html:18Window {window: Window, self: Window, document: document, name: '', location: Location, …}'b' this.html:30 undefined this.html:18{a: '123', b: *ƒ*, c: *ƒ*, d: *ƒ*}* 'b' this.html:31 undefined this.html:18 {a: '123', b: *ƒ, c: *ƒ*, d: *ƒ*}* 'b' this.html:32 undefined
c、d的两种赋值方法都指向obj,只有b的调用会指向window,是因为b在函数内部调用_b时 this指向丢失,于是链式得向上一级的上下文环境中取this,获得到了window
# 显示绑定
apply、call、bind可以显示的修改函数的this,具体的区别在于参数传递形式,实际功能一致
# new 绑定
使用new来调用函数,执行的步骤如下:
function d(val){
this.val = val
console.log(this); // d
}
var d1 = new d()
2
3
4
5
- 创建一个对象
- 这个对象会执行prototype的设置
- 新的对象会绑定到函数调用的this
- 如果函数没有返回对象,那么new表达式中函数调用会返回这个新函数
下面再举一个例子来说明函数有了返回对象时的this指向被覆盖的情景
function f(){
this.val = 'f'
}
f.prototype.log = function(){
console.log(this.val, 'f.log');
}
function e(e){
this.val = 'e'
return new f()
}
e.prototype.log = function(){
console.log(this.val, 'log');
}
var e1 = new e()
console.log(e1.log()); // f f.log
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 箭头函数绑定
箭头函数会丢失this,this就会向上层查询this指向,直到全局变量
var g = {
a: '1',
b: function(){
console.log(this.a, 'b');
},
c(){
console.log(this.a, 'c');
},
d: ()=>{
console.log(this, this.a, 'd');
}
}
g.b() // 1
g.c() // 1
g.d() // window undefined
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# apply与call与bind
apply、call、bind同样都是用来修改函数中的this指向的,区别在于:
apply、call 不同点是: 第一个参数一致,call的第2到N个参数相当于apply的第二个参数(数组,包含了call的第2到N个参数);相同点是:返回的是函数的执行结果
bind、call不同点是:bind返回函数,call返回执行结果;相同点是传参形式一样
var _obj = {
val: 'aaa',
a: function(){
var str = [...arguments].reduce((sum, e)=>{
return sum + ' - ' + e
})
return this.val + '~' + str;
}
}
var data = {
val: 'bbb'
}
var __a = _obj.a('111')
var __b = _obj.a.call(data, '2', '3', '4')
var __c = _obj.a.apply(data, ['2', '3', '4'])
var __d = _obj.a.bind(data, '2', '3', '4')
console.log(__a, __b, __c, __d, __d());
/*
aaa~111 bbb~2 - 3 - 4 bbb~2 - 3 - 4 ƒ (){
var str = [...arguments].reduce((sum, e)=>{
return sum + ' - ' + e
})
return this.val + '~' + str;
} bbb~2 - 3 - 4
*/
// 即 __b == __c == __d()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28