1、JS中函數(shù)的幾種調(diào)用方式
(1)普通函數(shù)調(diào)用
(2)作為對象方法調(diào)用
(3)作為構(gòu)造函數(shù)調(diào)用
(4)apply/call方法調(diào)用
(5)Function.ptototype.bind()方法
(6)ES6箭頭函數(shù)
總之:無論誰調(diào)用這個函數(shù)或方法this就指向誰
2伤柄、調(diào)用方式實(shí)例與解析
(1)普通函數(shù)調(diào)用
function person(){
this.name="hello";
console.log(this); // window
console.log(this.name); // hello
}
person();
解析:person函數(shù)作為普通函數(shù)調(diào)用累榜,實(shí)際上person是作為window全局對象的一個方法進(jìn)行調(diào)用的逻恐,即window.person()遭居,所以window對象調(diào)用了person方法刽辙,那么person函數(shù)中的this就指向window但狭,同事window還擁有了另外一個屬性name披诗,值為hello
(2)作為對象方法調(diào)用
var name = "HELLO";
var person = {
name:"hello",
showName:function(){
console.log(this.name);
}
}
foo.showName(); //這里是person對象調(diào)用showName方法,很顯然this關(guān)鍵字是指向person對象的立磁,因此輸出hello
var showNameA = person.showName;
showNameA(); //這里將person.showName方法賦給showNameA變量呈队,此時showNameA變量相當(dāng)于window對象的一個屬性,因此showNameA()執(zhí)行的時候相當(dāng)于window.showNameA()唱歧,即window對象調(diào)用showNameA這個方法宪摧,所以this關(guān)鍵字指向window,因此輸出HELLO
var personA = {
name: "hello",
showName: function(){
console.log(this.name); // HELLO
}
}
var personB = {
name: "HELLO",
sayName: personA.showName
}
personB.sayName(); //雖然showName方法是在personA這個對象中定義颅崩,但是調(diào)用的時候卻是在personB這個對象中調(diào)用几于,因此this對象指向personB,因此輸出HELLO
(3)作為構(gòu)造函數(shù)調(diào)用
function Person(name){
this.name = name;
}
var personA = Person("hello");
console.log(window.name); //此處沒有進(jìn)行new操作沿后,相當(dāng)于window對象調(diào)用Person("hello")方法沿彭,此時this指向window對象,并進(jìn)行賦值操作window.name = "hello"
var personB = new Person("hello");
console.log(personB.name); // hello
function person(name){//new操作符(實(shí)例化對象)的內(nèi)部過程
var o = {};
o.__proto__ = Person.prototype; //原型繼承
Person.call(o,name);
return o;
}
var personB = person("hello");
console.log(personB.name); //hello
解析:在person里面首先創(chuàng)建一個空對象o尖滚,將o的proto指向Person.prototype完成對原型的屬性和方法的繼承喉刘;Person.call(o,name)這里即函數(shù)Person作為apply/call調(diào)用(具體內(nèi)容下方)瞧柔,將Person對象里的this改為o,即完成了o.name=name操作睦裳;返回對象o造锅;因此 person("hello") 返回了一個繼承了Person.prototype 對象上的屬性和方法,以及擁有 name 屬性為"hello"的對象推沸,并將它賦給變量personB备绽,所以 console.log(personB.name) 會輸出 hello
(4)apply/call方法調(diào)用:在JS里函數(shù)也是對象,因此函數(shù)也有方法鬓催。從Function.prototype上繼承了Function.prototype.call/Function.prototype.apply方法肺素,call/apply方法最大的作用就是能改變this關(guān)鍵字的指向
var name="HELLO";
var Person={
name: "hello",
showName: function(){
console.log(this.name);
}
}
Person.showName.call(); //call方法里面的第一個參數(shù)為空,默認(rèn)指向window宇驾。雖然showName方法定義在Person對象里面倍靡,但是使用call方法后,將showName方法里面的this指向了window课舍。因此輸出"HELLO";
function FruitA(n1, n2){
this.n1 = n1;
this.n2 = n2;
this.change = function(x,y){
this.n1 = x;
this.n2 = y;
}
}
var fruitA = new FruitA("cheery","banana");
var FruitB = {
n1: "apple",
n2: "orange"
};
fruitA.change.call(FruitB,"pear","peach");
console.log(FruitB.n1); //FruitB調(diào)用fruitA的change方法塌西,將fruitA中的this綁定到對象FruitB上,因此輸出pear
console.log(FruitB.n2); //FruitB調(diào)用fruitA的change方法筝尾,將fruitA中的this綁定到對象FruitB上捡需,因此輸出peach
(5)Function.ptototype.bind()方法
var name = "HELLO";
function Person(name){
this.name = name;
this.sayName = function(){
setTimeout(function(){
console.log("my name is "+this.name);
},50)
}
}
var person = new Person("hello");
person.sayName() //setTimeout()定時函數(shù)相當(dāng)于window.setTimeout(),由window這個全局對象對調(diào)用筹淫,因此this的指向?yàn)閣indow,站辉,則this.name則為 HELLO
var name = "HELLO";
function Person(name){
this.name = name;
this.sayName = function(){
setTimeout(function(){
console.log("my name is "+this.name);
}.bind(this),50) //注意這個地方使用的bind()方法,綁定setTimeout里面的匿名函數(shù)的this一直指向Person對象
}
}
var person = new Person("xl");
person.sayName(); //setTimeout(function(){console.log(this.name)}.bind(this),50);损姜,匿名函數(shù)使用bind(this)方法后創(chuàng)建了新的函數(shù)饰剥,這個新的函數(shù)不管在什么地方執(zhí)行,this都指向的Person摧阅,而非window汰蓉,因此最后的輸出為"my name is hello"而不是"my name is HELLO"
解析:setTimeout/setInterval 匿名函數(shù)執(zhí)行的時候,this默認(rèn)指向window對象棒卷,除非手動改變this的指向顾孽。在《javascript高級程序設(shè)計(jì)》當(dāng)中,寫到:“超時調(diào)用的代碼(setTimeout)都是在全局作用域中執(zhí)行的娇跟,因此函數(shù)中的this的值岩齿,在非嚴(yán)格模式下是指向window對象,在嚴(yán)格模式下是指向undefined”苞俘。
var name = "HELLO";
function Person(){
this.name = "hello";
this.showName = function(){
console.log(this.name);
}
setTimeout(this.showName,50);
}
var person = new Person(); //在setTimeout(this.showName,50)語句中,會延時執(zhí)行this.showName方法龄章,this.showName方法即構(gòu)造函數(shù)Person()里面定義的方法吃谣。50ms后乞封,執(zhí)行this.showName方法,this.showName里面的this此時便指向了window對象岗憋,因此輸出 HELLO
var name = "HELLO";
function Person(){
this.name = "hello";
var that = this;
this.showName = function(){
console.log(that.name);
}
setTimeout(this.showName,50)
}
var person=new Person(); //在Person函數(shù)當(dāng)中將this賦值給that肃晚,即讓that保存Person對象,因此在setTimeout(this.showName,50)執(zhí)行過程當(dāng)中console.log(that.name)會輸出Person對象的屬性hello
var name = "HELLO";
var person = {
name: "hello",
showName: function(){
console.log(this.name);
},
sayName: function(){
(function(callback){
callback();
})(this.showName)
}
}
person.sayName(); //匿名函數(shù)的執(zhí)行同樣在默認(rèn)情況下this是指向window的仔戈,除非手動改變this的綁定對象关串,因此輸出 HELLO
(6)Eval函數(shù):該函數(shù)執(zhí)行的時候,this綁定到當(dāng)前作用域的對象上
var name = "HELLO";
var person = {
name: "hello",
showName: function(){
eval("console.log(this.name)");
}
}
person.showName(); //hello
var a = person.showName;
a(); // HELLO
(7)箭頭函數(shù):ES6里面this指向固定化监徘,始終指向外部對象晋修,因?yàn)榧^函數(shù)沒有this,因此它自身不能進(jìn)行new實(shí)例化凰盔,同時也不能使用call, apply, bind等方法來改變this的指向
function Timer() {
this.seconds = 0;
setInterval( () => this.seconds ++, 1000);
}
var timer = new Timer();
setTimeout( () => console.log(timer.seconds), 3100); //構(gòu)造函數(shù)內(nèi)部的setInterval()內(nèi)的回調(diào)函數(shù)墓卦,this始終指向?qū)嵗膶ο螅@取實(shí)例化對象的seconds的屬性户敬,每1s這個屬性的值都會增加1落剪,因此最后輸出 3