首先,必须搞清楚在JS里面,函数的几种调用方式:
普通函数调用
作为方法来调用
作为构造函数来调用
使用apply/call方法来调用
Function.prototype.bind方法
es6箭头函数
但是不管函数是按哪种方法来调用的,请记住一点:谁调用这个函数或方法,this关键字就指向谁。
接下来就分情况来讨论下这些不同的情况:
普通函数调用 1 2 3 4 5 6 7 8 1 function person ( ) {2 this .name="xl" ;3 console .log(this );4 console .log(this .name);5 }6 7 person(); 8
在这段代码中person
函数作为普通函数调用,实际上person
是作为全局对象window
的一个方法来进行调用的,即window.person()
; 所以这个地方是window
对象调用了person
方法,那么person
函数当中的this
即指window
,同时window
还拥有了另外一个属性name
,值为xl
.
1 2 3 4 5 1 var name="xl" ;2 function person ( ) {3 console .log(this .name);4 }5 person();
同样这个地方person
作为window
的方法来调用,在代码的一开始定义了一个全局变量name
,值为xl
,它相当于window
的一个属性,即window.name="xl"
,又因为在调用person
的时候this
是指向window
的,因此这里会输出xl
.
作为方法来调用 在上面的代码中,普通函数的调用即是作为window
对象的方法进行调用。显然this
关键字指向了window
对象.
再来看下其他的形式
1 2 3 4 5 6 7 8 9 10 11 12 13 var name="XL" ; var person={ name:"xl" , showName:function ( ) { console .log(this .name); } } person.showName(); var showNameA=person.showName; showNameA();
再换种形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 var personA={ name:"xl" , showName:function ( ) { console .log(this .name); } } var personB={ name:"XL" , sayName:personA.showName } personB.sayName();
作为构造函数来调用 1 2 3 4 5 6 7 8 9 10 11 1 function Person (name ) { 2 this .name=name; 3 } 4 var personA=Person("xl" ); 5 console .log(personA.name); 6 console .log(window .name); 7 8 9 var personB=new Person("xl" ); 10 console .log(personB.name);11
new操作符 1 2 3 4 5 6 7 8 9 10 function person (name ) { var o={}; o.__proto__=Person.prototype; Person.call(o,name); return o; } var personB=person("xl" ); console .log(personB.name);
这段代码涉及到了proto 及prototype的概念,如果有需要了解,请点击链接
在person
里面首先创建一个空对象o,将o的proto指向Person.prototype完成对原型的属性和方法的继承
Person.call(o,name)
这里即函数Person
作为apply/call
调用(具体内容下方),将Person
对象里的this
改为o,即完成了o.name=name
操作
返回对象o。
1 2 因此`person("xl")` 返回了一个继承了`Person.prototype` 对象上的属性和方法,以及拥有`name` 属性为"xl" 的对象,并将它赋给变量`personB` . 所以`console.log(personB.name)` 会输出"xl"
call/apply方法的调用 在JS里函数也是对象,因此函数也有方法。从Function.prototype上继承到Function.prototype.call/Function.prototype.apply
方法call/apply
方法最大的作用就是能改变this
关键字的指向.
1 Obj.method.apply(AnotherObj,arguments );
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 1 var name="XL" ; 2 var Person={ 3 name:"xl" , 4 showName:function ( ) { 5 console .log(this .name); 6 } 7 } 8 Person.showName.call(); 9 10 11 funtion FruitA(n1,n2){12 this .n1=n1;13 this .n2=n2;14 this .change=function (x,y ) {15 this .n1=x;16 this .n2=y;17 }18 }19 20 var fruitA=new FruitA("cheery" ,"banana" );21 var FruitB={22 n1:"apple" ,23 n2:"orange" 24 };25 fruitA.change.call(FruitB,"pear" ,"peach" );26 27 console .log(FruitB.n1); 28 console .log(FruitB.n2);
FruitB
调用fruitA
的change
方法,将fruitA
中的this
绑定到对象FruitB
上。
Function.prototype.bind()方法 1 2 3 4 5 6 7 8 9 10 11 12 var name="XL" ;function Person (name ) { this .name=name; this .sayName=function ( ) { setTimeout(function ( ) { console .log("my name is " +this .name); },50 ) } } var person=new Person("xl" );person.sayName()
那么如何才能输出"my name is xl"
呢?
1 2 3 4 5 6 7 8 9 10 11 var name="XL" ;function Person (name ) { this .name=name; this .sayName=function ( ) { setTimeout(function ( ) { console .log("my name is " +this .name); }.bind(this ),50 ) } } var person=new Person("xl" );person.sayName();
这里setTimeout(function(){console.log(this.name)}.bind(this),50);
,匿名函数使用bind(this)
方法后创建了新的函数,这个新的函数不管在什么地方执行,this
都指向的Person
,而非window
,因此最后的输出为”my name is xl”而不是”my name is XL”
另外几个需要注意的地方:setTimeout/setInterval/匿名函数执行
的时候,this
默认指向window对象
,除非手动改变this的指向。在《javascript高级程序设计》当中,写到:“超时调用的代码(setTimeout
)都是在全局作用域中执行的,因此函数中的this的值,在非严格模式下是指向window对象,在严格模式下是指向undefined”。本文都是在非严格模式下的情况。
1 2 3 4 5 6 7 8 9 10 11 12 var name="XL" ; function Person ( ) { this .name="xl" ; this .showName=function ( ) { console .log(this .name); } setTimeout(this .showName,50 ); } var person=new Person();
修改上面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 var name="XL" ; 2 function Person ( ) { 3 this .name="xl" ; 4 var that=this ; 5 this .showName=function ( ) { 6 console .log(that.name); 7 } 8 setTimeout(this .showName,50 ) 9 } 10 var person=new Person(); 11 12 13 14
匿名函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 1 var name="XL" ; 2 var person={ 3 name:"xl" , 4 showName:function ( ) { 5 console .log(this .name); 6 } 7 sayName:function ( ) { 8 (function (callback ) { 9 callback(); 10 })(this .showName)11 }12 }13 person.sayName(); 14 var name="XL" ;15 var person={16 name:"xl" ,17 showName:function ( ) {18 console .log(this .name);19 }20 sayName:function ( ) {21 var that=this ;22 (function (callback ) {23 callback();24 })(that.showName)25 }26 }27 person.sayName() ; 28
Eval函数 该函数执行的时候,this绑定到当前作用域的对象上
1 2 3 4 5 6 7 8 9 10 11 12 var name="XL" ;var person={ name:"xl" , showName:function ( ) { eval ("console.log(this.name)" ); } } person.showName(); var a=person.showName;a();
箭头函数 es6
里面this
指向固定化,始终指向外部对象,因为箭头函数没有this
,因此它自身不能进行new
实例化,同时也不能使用call, apply, bind
等方法来改变this
的指向
1 2 3 4 5 6 7 8 9 10 11 function Timer ( ) { this .seconds = 0 ; setInterval( () => this .seconds ++, 1000 ); } var timer = new Timer(); setTimeout( () => console .log(timer.seconds), 3100 );
评论加载中