this默认绑定
this默认绑定我们可以理解为函数调用时无任何调用前缀的情景,它无法应对我们后面要介绍的另外四种情况,所以称之为默认绑定,默认绑定时this指向全局对象(非严格模式):在严格模式环境中,默认绑定的this指向undefined
function fn1() {
let fn2 = function () {
console.log(this); //window
fn3();
};
console.log(this); //window
fn2();
};
function fn3() {
console.log(this); //window
};
fn1();
隐式绑定
什么是隐式绑定呢,如果函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上,看个例子:
function fn() {
console.log(this.name);
};
let obj = {
name: '听风是风',
func: fn
};
obj.func() //听风是风
如果函数调用前存在多个对象,this指向距离调用自己最近的对象, 即使那个对象没有那个属性
function fn() {
console.log(this.name);
};
let obj = {
func: fn,
};
let obj1 = {
name: '听风是风',
o: obj
};
obj1.o.func() //undefined
this显式绑定
显式绑定是指我们通过call、apply以及bind方法改变this的行为,相比隐式绑定,我们能清楚的感知 this 指向变化过程。来看个例子:
let obj1 = {
name: '听风是风'
};
let obj2 = {
name: '时间跳跃'
};
let obj3 = {
name: 'echo'
}
var name = '行星飞行';
function fn() {
console.log(this.name);
};
fn(); //行星飞行
fn.call(obj1); //听风是风
fn.apply(obj2); //时间跳跃
fn.bind(obj3)(); //echo
比如在上述代码中,我们分别通过call、apply、bind改变了函数fn的this指向。
在js中,当我们调用一个函数时,我们习惯称之为函数调用,函数处于一个被动的状态;而call与apply让函数从被动变主动,函数能主动选择自己的上下文,所以这种写法我们又称之为函数应用。
注意,如果在使用call之类的方法改变this指向时,指向参数提供的是null或者undefined,那么 this 将指向全局对象。
另外,在js API中部分方法也内置了显式绑定,以forEach为例:
let obj = {
name: '听风是风'
};
[1, 2, 3].forEach(function () {
console.log(this.name);//听风是风*3
}, obj);
call、apply与bind有什么区别?
一、执行方式不同
call、apply与bind都用于改变this绑定,call和apply是改变后页面加载之后就立即执行,是同步代码。bind是异步代码,改变后不会立即执行;而是返回一个新的函数。
二、传参方式不同
call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。
apply使用数组的方式传入的,只要是数组方式就可以使用剩余参数的方式传入。
在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。
三、修改this的性质不同
call、apply只是临时的修改一次,也就是call和apply方法的那一次;当再次调用原函数的时候,它的指向还是原来的指向。
bind是永久修改函数this指向,但是它修改的不是原来的函数;而是返回一个修改过后新的函数,此函数的this永远被改变了,绑定了就无法再次通过bind、apply或 call 修改
new绑定
准确来说,js中的构造函数只是使用new 调用的普通函数,它并不是一个类,最终返回的对象也不是一个实例,只是为了便于理解习惯这么说罢了。
那么new一个函数究竟发生了什么呢,大致分为三步:
1.以构造器的prototype属性为原型,创建新对象;
2.将this(可以理解为上句创建的新对象)和调用参数传给构造器,执行;
3.如果构造器没有手动返回对象,则返回第一步创建的对象
this绑定优先级
this绑定优先级为:
显式绑定 > 隐式绑定 > 默认绑定
new绑定 > 隐式绑定 > 默认绑定
为什么显式绑定不和new绑定比较呢?因为不存在这种绑定同时生效的情景,如果同时写这两种代码会直接抛错,所以大家只用记住上面的规律即可。
箭头函数的this
ES6的箭头函数是另类的存在,为什么要单独说呢,这是因为箭头函数中的this不适用上面介绍的四种绑定规则。
准确来说,箭头函数中没有this,箭头函数的this指向取决于外层作用域中的this,外层作用域或函数的this指向谁,箭头函数中的this便指向谁。有点吃软饭的嫌疑,一点都不硬朗,我们来看个例子:
function fn() {
return () => {
console.log(this.name);
};
}
let obj1 = {
name: '听风是风'
};
let obj2 = {
name: '时间跳跃'
};
let bar = fn.call(obj1); // fn this指向obj1
bar.call(obj2); //听风是风
为啥我们第一次绑定this并返回箭头函数后,再次改变this指向没生效呢?
前面说了,箭头函数的this取决于外层作用域的this,fn函数执行时this指向了obj1,所以箭头函数的this也指向obj1。除此之外,箭头函数this还有一个特性,那就是一旦箭头函数的this绑定成功,也无法被再次修改,有点硬绑定的意思。
当然,箭头函数的this也不是真的无法修改,我们知道箭头函数的this就像作用域继承一样从上层作用域找,因此我们可以修改外层函数this指向达到间接修改箭头函数this的目的。