JavaScript-JS中this的指向
本文将主要总结JS中this的指向
隐式绑定的场景:
- 全局上下文
- 直接调用函数
- 对象.方法的形式调用
- DOM事件绑定(特殊)
- new构造函数绑定
- 箭头函数
1. 全局上下文
全局上下文默认this指向window, 严格模式下指向undefined。
2. 直接调用函数
比如:
let obj = {
a: function() {
console.log(this);
}
}
let func = obj.a;
func();
这种情况是直接调用。this相当于全局上下文的情况(默认this指向window, 严格模式下指向undefined。)。
3. 对象.方法的形式调用
还是刚刚的例子,我如果这样写:
obj.a();
这就是对象.方法的情况,this指向这个对象
4. DOM事件绑定
onclick和addEventerListener中 this 默认指向绑定事件的元素。
IE比较奇异,使用attachEvent,里面的this默认指向window。
5. new+构造函数
此时构造函数中的this指向实例对象。
6. 箭头函数?
箭头函数没有this, 因此也不能绑定。里面的this会指向当前最近的非箭头函数的this,找不到就是window(严格模式是undefined)。比如:
let obj = {
a: function() {
letdo = () => {
console.log(this);
}
do();
}
}
obj.a(); // 找到最近的非箭头函数a,a现在绑定着obj, 因此箭头函数中的this是obj
显式绑定的场景:
call, apply, bind都是为了调用函数,然后改变函数中的this的指向。
它们允许你在调用函数时为函数指定上下文
1.call
- call用于显式地设置函数的上下文,call方法将对象绑定到this。
- call 需要分别传参
- call会立即执行函数
function show(title) {
alert(`${title+this.name}`);
}
let lisi = {
name: '李四'
};
let wangwu = {
name: '王五'
};
show.call(lisi, '后盾人'); //重点
2.apply
apply与call用于显式地设置函数的上下文,两个方法作用一样都是将对象绑定到this,只是在传递参数上有所不同。
因为,我们可以看到,call方法必须一个一个传递参数,这样有些恼人。如果我们可以把整个数组作为第二个参数并让 JavaScript 为我们自动展开就好了。
所以,这个时候apply,就派上用场了
apply 用数组传参
call 需要分别传参
与 bind 不同 call/apply 会立即执行函数
- apply用于显式地设置函数的上下文,apply方法将对象绑定到this。
- apply 需要分别传参
- apply会立即执行函数
function show(title) {
alert(`${title+this.name}`);
}
let lisi = {
name: '李四'
};
let wangwu = {
name: '王五'
};
show.apply(lisi, ['后盾人']); //重点
也就是说,apply和call如果不传参数的话,只传想要改变的this的指向,那他们用法,效果都是一样的。
func().call(self) //意思是将func()这个函数中的this指向self
func().apply(self) //意思是将func()这个函数中的this指向self
小栗子:
找数组中的数值最大值
let arr = [1, 3, 2, 8];
console.log(Math.max(arr)); //NaN
console.log(Math.max.call(Math, arr)); //NaN, 因为call除了要传的this以外,后面的参数必须用逗号隔开,现在传的是数组
console.log(Math.max.apply(Math, arr)); //8, apply后的参数传的是数组
console.log(Math.max(...arr)); //8, 当然我们用展开语法也是完全可以的
3.bind
bind()是将函数绑定到某个对象,比如 a.bind(hd) 可以理解为将a函数绑定到hd对象上即 hd.a()。
其实,.bind
和 .call
完全相同,除了不会立刻调用函数,而是返回一个能以后调用的新函数。
与 call/apply 不同bind不会立即执行
bind 是复制函数行为,会返回新函数
bind是复制函数行为
let a = function() {};
let b = a;
console.log(a === b); //true
//bind是新复制函数
let c = a.bind();
console.log(a == c); //false
然后呢,因为调用bind方法并不会立即执行该函数,所以就给了我们两次传参的机会;
绑定参数注意事项
function func(a, b) {
return this.f + a + b;
}
//使用bind会生成新函数
let newFunc = func.bind({ f: 1 }, 3);
//1+3+2 参数2赋值给b即 a=3,b=2
console.log(newFunc(2)); //注意如果在前面bind的时候给某些参数赋值过了,在这里就不需要赋值了,现在赋了也没用
优先级: new > call、apply、bind > 对象.方法 > 直接调用。
参考: