在刚开始学习JavaScript的时候,经常被js中的this及其相关的方法,例如:bind,apply,call方法迷惑。其实我们只要根据包含this指针的方法的用途加以区分,就能了解this指针所指向的对象:
1、直接函数调用,this指针指向全局环境,即Window对象,例如
2、对象函数调用,this指针指向调用函数的对象本身,例如:
var object={
'name':"vicky",
'sayName':function(){ console.log(this.name)}
};
object.sayName(); //this指针指向object对象,因此输出vicky
3、构造函数调用,this指针指向新创建的对象,例如:
function object(name){
this.name=name;
console.log(this); //由于this指向新创建的对象本身,输出:Object { name: "vikcy" }
console.log(this.name); //输出:"vicky"
}
var myObject=new Object('vicky'); //由于this指向新创建的对象本身
4、间接函数调用,例如call、apply方法,还有个特殊的bind方法
this指针指向全局环境,即Window对象
在全局环境中直接调用函数时,this对象会指向Window对象,例如:
var name='vicky'
function sayName(name){
console.log(this.name);
}
sayName(); //this指向window对象,而因为在全局环境中定义了var name='vicky';所以this.name输出:vicky
window.sayName(); //sayName()效果等同于window.sayName()
this指针指向调用函数的对象本身
var name='Bob';
function sayName(){
console.log(this.name);
}
var object={'name':'vicky'};
object.sayName=sayName; //sayName没有写成sayName(),表示不是执行函数,而是将sayName的指针赋值给object.sayName
object.sayName(); //由于对象函数调用方法,this指向对象本身,所以输出:'vicky'
sayName(); //由于全局环境调用sayName()等同于window.sayName();输出:'Bob'
上面的例子已经很好的说明了在全局环境直接调函数调用和对象函数调用时this指针指向的区别。
this指针指向新创建的对象
function object(name){
this.name=name;
console.log(this); //由于this指向新创建的对象本身,输出:Object { name: "vikcy" }
console.log(this.name); //输出:"vicky"
}
var myObject=new Object('vicky'); //由于this指向新创建的对象本身
开篇的例子已经很好的说明了构造函数调用的时候,this指针指向新创建对象的本身。
但在这里我想拓展一下知识,想跟大家聊聊为什么构造函数调用的时候,this指针为什么指向创建对象的本身,new关键字到底做了什么?
其实在通过new关键词去调用构造函数创建对象的时候,经历了如下的过程:
1、创建新对象,继承构造函数的prototype
2、调用call()方法,接受参数并修改this的指向,指向这个新对象
3、执行构造函数,如果构造函数有返回对象,则这个对象会取代步骤1创建的新对象,如果构造函数没有返回值,则返回步骤1创建的对象。
如果用代码模拟这个过程,可以大致参考:
var newObject=function(func){
var o={}; //创建一个新对象
o.__proto__=func.prototype; //继承构造函数prototype
var k=func.call(o); //调用call方法,修改this指向,指向这个新创建的对象
if(typeof k=== 'object'){
//如果构造函数有返回对象,则取代步骤1创建的对象,返回构造函数所返回的对象
return k;
}else{
//如果构造函数没有返回对象,则直接返回步骤1创建的对象
return o;
}
}
这段模拟的代码有一点缺陷,缺陷就是call方法的时候没有模拟接受参数的步骤,不过不影响我们对new关键词的理解。
现在我们通过实例,来理解下上面的new关键词运作:
function Dog(name){
this.name=name;
console.log(this.name); //输出'泰迪'
console.log(this); //输出:Object { name: "泰迪" }
}
Dog.prototype.sayName=function sayName(){
console.log("my name is "+this.name);
}
var animal=new Dog("泰迪");
animal.sayName(); //输出:'my name is 泰迪'
上面的这段代码,已经能够证实了以下几点:
1、构造函数this指针指向新创建的对象
2、创建新对象,继承构造函数的prototype
3、调用call()方法,接受参数并修改this的指向,指向这个新对象
对于
执行构造函数,如果构造函数有返回对象,则这个对象会取代步骤1创建的新对象,如果构造函数没有返回值,则返回步骤1创建的对象。
这一点,我们在看下下面这个实例:
function Cat(){
console.log(this); //输出:Object { }
console.log("cat");
}
function Dog(name){
this.name=name;
console.log(this.name);
console.log(this); //输出:Object { name: "泰迪" }
return new Cat(); //关键代码,在构造函数返回了一个对象
}
var animal=new Dog("泰迪"); //输出:Object { }
这段代码我们可以证实:
执行构造函数,如果构造函数有返回对象,则这个对象会取代步骤1创建的新对象,如果构造函数没有返回值,则返回步骤1创建的对象。
这一点,我们在Dog的构造函数最后返回了一个Cat的对象,所以animal指针指向的是构造函数中返回的new Cat()的对象实例。
call方法可以动态的设置函数体内的this指向,例如:
function Cat(age,name){
this.name='cat';
console.log(this);
console.log('cat: age:'+age+",name:"+name);
}
var cat=new Cat(4,'Bob'); //输出:Cat {name: "cat"}和cat: age:4,name:Bob
Cat.call(this,3,'Tom'); //由于调用了call方法,输出:this指向了Window和cat: age:3,name:Tom
先看var cat=new Cat(4,'Bob');
这行代码,这里的this指向的是新建的Cat对象,输出的“cat: age:4,name:Bob”也是在对象初始化时候传入的。Cat.call(this,3,'Tom');
这行代码,这里的this指向的是Window对象,因为:我们在全局环境调用了Cat.call(this,3,'Tom');
方法,这里call函数第一个参数(this指针要指向的对象)我们传入了this,而全局环境下的this指向的正好就是window对象,而第三和第四个参数,我们分别定义了age=>3,name=>’Tom’
apply方法和call方法的作用相同,唯一不同的是call方法要将参数一一传入,而apply方法传入的是数组或者arguments对象。arguments对象包含了函数的所有参数。
实例:
function Cat(age,name){
this.name='cat';
console.log(this);
console.log('cat: age:'+age+",name:"+name);
}
var cat=new Cat(4,'Bob'); //输出:Cat {name: "cat"}和cat: age:4,name:Bob
Cat.apply(this,[3,'Tom']); //由于调用了apply方法,输出:this指向了Window和cat: age:3,name:Tom
function getCat(age,name){
Cat.apply(this, arguments); //arguments包含了函数的参数
}
getCat(5,"kitty"); //由于调用了apply方法,输出:this指向了Window和cat: age:5,name:kitty
bind这个方法会创建一个函数的实例,其 this 值会被绑定到传给 bind()函数的值。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
console.log(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //由于调用了 sayColor.bind(o),bind函数返回的函数实例中的this直接绑定了o这个对象,
所以即使在全局环境调用函数objectSayColor,也会输出:"blue"
window.objectSayColor(); //输出:"blue"
所以要了解this指向时,我们应该先弄清楚函数调用的方式,最后问自己这个this指向的对象是从何而来。
我是大麦,如果喜欢我的文章,请点个赞。