一、前言
之前学过this、call、apply怎么去用,但是理解地不够深刻,离开了案例,实际操作起来就出问题了。当然了,写这篇文章只是个人在使用过程中产生的理解。
二、正文
1、什么是this,call,apply?
this是函数内部的属性,也就是说this只能在函数运行的时候才能确定,只能在函数内部访问。
指向的对象是调用当前函数的对象(谁调用我这个函数,我就是谁)。
如何更好判断this具体指向谁?
- 第一种情况:函数调用时,函数名前面有对象,这时this指向函数名前面这个对象
var obj = {
name:"lodash",
age:13,
foo:function(){
console.log(this);
}
}
obj.foo();//{name:"lodash",age:"13",foo:[Function:foo]}
因为是对象obj调用了这个函数foo,所以这时this就是obj,进而打印出来整个obj对象。
- 第二种情况:函数名前面没有对象,这时this指向全局对象(window/global)
var a = 6;
var obj = {name:"bb"};
function foo(){
console.log(this);//一大堆东西,巴拉巴拉
consoele.log(this.a);//6
console.log(this.obj);//{name:"bb"}
console.log(this.foo);//[Function:foo]
}
foo ();
对于全局对象不太理解的,可以将全局对象看做一个网页最大的对象,这个对象包含了网页全部的内容,例如上面代码定义的a,obj,foo都是全局对象里的成员。所以全局对象可以像对象的调用一样调用a,obj,foo。
- 第三种情况:函数名后面有call/apply,这是this指向传参的第一个参数。
var obj = {
name:"lala",
age:5,
foo:function(){
console.log(this);
}
}
var obj2 = {
name:"baba",
age:"30"
}
obj.foo();//{name:"lala",age:"5"}
obj.foo.call(obj2);//{name:"baba",age:30}
obj.foo.apply(obj2);//{name:"baba",age:30}
这里的call,apply是指定调用函数的对象方法。(告诉this现在谁调用这个函数),传入的参数第一个作为手动指定调用函数的对象,这里只给一个参数,其实可以有多个参数,但不管有几个参数,第一个参数永远都是指定调用函数的对象。
2、疑问
看完上面,小伙伴可能会有些疑问
不是说好的函数名前面有对象,this就指向前面这个对象嘛?怎么后面有call就变了?
这里只能告诉你,一旦函数名后面有call/apply,这个优先指向第一个参数,如果没有传参,就指向全局对象。
3、总结
判断this指向优先级:
有call/apply就指向第一个参数(没有参数就指向全局对象)->函数名前面有对象就指向这个对象->即没对象又没call/apply就指向全局对象。
4、拓展
1、一个代码看懂this
var name = "全局对象";
var obj ={
name:"对象1",
age:15
}
var obj2 = {
name:"对象2",
age:22
}
function foo(){
console.log(this.name);
}
Object.prototype.foo = foo;//这里是为了让对象能调用函数foo,这个知识点是函数的原型链继承
foo();//全局对象
obj.foo();//对象1
obj2.foo();//对象2
foo.call();//全局对象
foo.call(obj);//对象1
foo.call(obj2);//对象2
obj.foo.call();//全局对象
obj2.foo.call();//全局对象
obj.foo.call(obj2);//对象2
obj2.foo.call(obj);//对象1
2、call与apply简单区分
它们两用法总体差不多,区别在于传参上
call传参是指定对象后面接实参列表
apply传参是指定对象后面接参数数组
var obj ={
name:"对象1",
age:15
}
var obj2 = {
name:"对象2",
age:22
}
function foo(a,b,c){
console.log(this,a,b,c);
}
Object.prototype.foo = foo;
foo.call(obj,1,3,4,5);//{name:"对象1",age:15} 1 3 4
foo.apply(obj2,[1,3,4,6]);//{name:"对象2",age:22} 1 3 4
由此可见参数传的第一个不能看做参数,而是指定的对象,而对象后面接的第一个才是真正的第一个参数。