this在javascript中和在其他面向对象语言中是不同的,this在javascrript相对比较灵活,this在javascript中跟定义的位置是没有关系的,而是跟调用的方式和调用的位置是有关系的,并且,this在javascript中是在运行过程中动态绑定的。
this的4种绑定规则
1.默认绑定:独立函数的调用
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
//案例一:
function foo(){
console.log(this);
}
foo();//这里是独立调用,foo函数中打印的this会指向window
//案例二
function foo1(){
console.log(this);
foo(2);
}
function foo2(){
console.log(this);
foo3();
}
function foo3(){
console.log(this);
}
foo1();//这里虽然在函数实现了相互之间的调用,但是仍然还是独立的函数调用,this仍然指向window
//案例三:
function foo4(func){
func();
}
var obj = {
name:'obj',
foo5(){
console.log(this)
}
}
foo4(obj.foo5);//这里虽然将一个函数的作为参数传入了另一个函数中去,但是对于调用的函数foo4来讲,仍然是独立的函数调用,this仍然会指向window
总结:在javascript中,只要函数是独立调用的,那么this必定会指向window
2.隐式绑定
隐式绑定简单理解就是通过对象进行调用函数,隐式调用有一个条件就是必须在调用的对象内部有一个函数的引用(比如一个属性),如果没有这个引用的存在,那么在代码被执行时会报一个找不到该函数的错误,也正是这通过这个函数的引用,间接的将this绑定到了这个对象上去。
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
//案例一:
function foo(){
console.log(this);
}
var obj = {
name:'obj',
foo:foo
}
obj.foo();//这里obj对象中有foo的引用,obj又调用了foo函数,符合隐式绑定,这时,this也就指向了obj对象
//案例二:
function foo2(){
console.log(this);
}
var obj1 = {
name:'obj1',
foo2:foo2
}
var obj2 = {
name:'obj2',
obj1:obj1
}
obj2.obj1.foo2();//这里obj2中有obj1的引用,obj1中又有foo2的引用,虽然是obj2调用了obj1,调用了foo2,但是实际上仍然是obj1调用了foo,所以this会指向到obj1,而不是obj2
//案例三:
function foo3(){
console.log(this);
}
var obj3 = {
name:'obj3',
foo3:foo3
}
var foo4 = obj3.foo3;
foo4();//这里可以简单理解为是将obj3.foo3这个函数的调用赋值给了foo4,之后foo4的调用实际就是独立的函数调用,this会指向window
总结:隐式绑定的情况下,看是谁发起的函数调用,谁发起,this就指向谁
3.显示绑定
显示绑定就是通过javascript的内置函数call/apply/bind进行调用函数,显示绑定不需要像隐式绑定一样必须在函数内部有一个引用,显示绑定会对函数进行强制调用。
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
//call和apply
function foo(num1,num2){
console.log(num1 + num2,this);
}
foo.call('aaa',1,2);//这里使用了call进行显示绑定,this会指向aaa
foo.apply('bbb',[1,2]);//这里使用了apply进行显示绑定,this会指向bbb
//补充:call和apply实现发效果和作用是一样的,唯一的区别就是在传参时,call和apply的第一个参数都是一个对象,代码执行以后会将this绑定到这个对象上去,后面的参数是函数执行时需要传入的参数,call是通过普通传参的方式将形参的实参传入,apply是将形参的实参通过一个数组的方式进行传入。
//bind bind可以将this总是绑定到一个对象上去,而不是像call和apply一样,使用一次绑定一次
function foo1(){
console.log(this);
}
var obj = {
name:'obj'
}
var foo2 = foo1.bind(obj);
foo2();//这里的foo虽然是独立的函数调用,但是之前是将foo1通过bind绑定到了obj上,之后再赋值给foo2上,所以最终的this会指向obj,而不是window
总结:如果在函数调用过程中出现了call、apply、bind等内置函数的使用,一定是会显示绑定this的
4.new绑定
使用new关键字来调用函数,是创建了一个新的对象,这个新的对象会被执行prototype,新对象会绑定到函数调用的this上去,this也是在这个步骤中完成的绑定的,如果函数没有返回其他对象,表达式会返回这个新的对象的。
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
//案例:
function Foo(name){
this.name = name;
console.log(this);
}
var f = new Foo(foo);//使用了new关键字来调用函数
console.log(f);//这里的this会打印出f对象
总结:如果在函数的调用中使用了new关键字,那么就去看函数通过使用new关键字创建了那个对象,创建了谁,this就会指向谁
以上就是this的4中绑定规则的介绍。
绑定优先级
上面说到了this有4中绑定规则,那么如果2种或者2种以上的规则同时出现时,this的绑定会执行那一种规则呢?这就是this绑定的优先级了。
优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
注意:new绑定不能和call、apply同时进行使用,也就不存在所谓的优先级问题了,但是new绑定可以和bind进行同时使用,但是new绑定的优先级要高于bind。
特殊绑定
1.忽略显示绑定
如果在显示绑定时,传入了一个null和undefined,那么会自动忽略这个显示绑定,使用默认绑定的规则。
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
//在使用call、apply、bind时,如果传入了null和undefined,会自动将this绑定成全局对象,也就是使用默认绑定的规则
function foo(){
console.log(this);
}
var obj = {
name:'obj
}
foo.call(obj);//显示绑定,this指向了obj对象
foo.call(null);//显示绑定传入null,忽略显示绑定,使用默认绑定,this指向了window
foo.call(undefined);//显示绑定传入了undefined,忽略显示绑定,使用默认绑定,this指向了window
foo.apply(obj);//显示绑定,this指向了obj对象
foo.apply(null);//显示绑定传入null,忽略显示绑定,使用默认绑定,this指向了window
foo.apply(undefined);//显示绑定传入了undefined,忽略显示绑定,使用默认绑定,this指向了window
var foo1 = foo.bind(null);
foo1();//显示绑定传入null,忽略显示绑定,使用默认绑定,this指向了window
2.间接函数的引用
如果创建了一个函数的简介引用,那么这种情况也会使用默认绑定规则
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
function foo(){
console.log(this);
}
var obj = {
name:'obj,
foo:foo
}
var obj1 = {
name:'obj1
}
obj.foo();//隐式绑定,this指向了obj
(obj1.foo = obj.foo)();//函数间接引用,使用默认绑定规则,this指向了window
箭头函数的this
在箭头函数中是不会绑定this的,无论是那种绑定方式都是不会生效的,箭头函数中的this会向上层作用域中进行查找,使用上层作用域的this。
这里需要主要强调的是箭头函数是不能和new同时进行使用的
//说明:以下代码的测试环境均为Chrome浏览器,node环境的打印结果还请大家自行测试,主要的区别在于window对象
var obj = {
data:[],
getData:() => {
setTimeout(() => {
console.log(this);//这里的箭头函数是没有this的,它会向上层作用域中进行查找,也就是window
})
}
}
这里对javascript中的this指向问题做了一个简单介绍,如果有不足或者错误的地方,还请码友指出,我也及时更正和学习!