上一篇随笔讲解了构造函数、原型及原型链相关的知识,今天让我们一起来探讨另一个问题:this。
一 this 的指向
1, 函数预编译过程中,this指向window
我们在讲解函数预编译过程的时候提到,函数在将要被执行时,会在[[scope]]属性中插入一个AO对象,AO对象包含了函数的形参、函数内声明的变量及子函数。实际上AO对象还有两个属性:this和arguments。
arguments用来存储接下来函数执行过程中接受到的实参列表。this用来接受函数中没有被声明就被赋值的变量(未声明访问将报错)。可以简单的理解为JavaScript的一种容错机制。这里的this就指向window,所以我们才说未声明的变量将被升级成window的属性。
1 function test(a,b){ 2 c = 10; 3 console.log(arguments); 4 console.log(c === window.c); 5 } 6 test(1,2); 7 //Arguments{0:1,1:2;length:2,...} 类数组 8 //true
2, 全局作用域里this指向window。
JavaScript的执行环境就是window,所以在全局作用域里window === this。
1 console.log(this === window);//true
3, obj.method() 方法中的this指向obj
如果有对象调用方法,那么方法内的this将指向调用对象。
如果没有对象调用方法,即函数(方法)空执行,这里面的this还是将指向window。
1 var name = 'ru'; 2 var obj = { 3 name:'ren', 4 method:function(){ 5 console.log(this.name); 6 } 7 }; 8 obj.method();//'ren' 9 var test = obj.method; 10 test();//'ru'
二 call/apply/bind
可以改变函数内部this的指向。
1, call(myThisarg,agr1,arg2,...)
call()方法使用一个指定的对象替代函数内原本的this。函数原有的参数依次添加在其后。如果第一个参数是null或者undefined,那么函数内部的this将不被改变。
1 function Person(name,age,sex){ 2 this.name = name; 3 this.age = age; 4 this.sex = sex; 5 } 6 /* 7 function Student(name,age,sex,classes,num){ 8 this.name = name; 9 this.age = age; 10 this.sex = sex; 11 this.classes = classes; 12 this.num = num; 13 } 14 这种写法当然能够实现我们的功能,但Student构造函数的功能完美涵盖了Person,我们是否可以在创建Student实例时调用Person以简化代码呢?当然 15 */ 16 function Student(name,age,sex,classes,num){ 17 Person.call(this,name,age,sex); 18 this.classes = classes; 19 this.num = num; 20 } 21 var student = new Student(1,2,3,4,5); 22 console.log(student);//{name:1,age:2,sex:3,classes:4,num:5}
1 var person = { 2 name : 'ren', 3 say : function(){ 4 console.log(this.name); 5 } 6 }; 7 var animal = { 8 name : 'dog' 9 }; 10 person.say();//'ren' 11 animal.say();//Typerror:animal.say is not a function 12 //我们怎么才能让dog也能学ren一样说话呢? 13 person.say.call(animal);//'dog'
2, apply()
apply()方法的功能和call()完全一样。只是apply以数组形式接受原函数的参数。
1 var arr = [50,-1,-20,2,99,38]; 2 //我们怎么拿到arr数组中最大或最小的那个数呢?你可能第一反应当然是数组的sort方法。但今天我们有一个使用更简单的方法! 3 Math.max.apply(null,arr);//99 4 Math.min.apply(null,arr);//-20 5 //因为这里不涉及操作this的问题,所以第一个参数为null
3, bind()
bind()方法使用方式和call()完全一样,但功能上有所差别。bind执行会返回一个函数,这个函数内部的this已被改变,等待在合适的时候被调用。
call和apply是在函数执行时改变其this的指向,而bind是先改变函数内的this指向,然后返回一个新的函数。
1 var person = { 2 name : 'ren', 3 say : function (){ 4 console.log(this.name); 5 } 6 }; 7 var animal = { 8 name : 'dog' 9 }; 10 person.say.call(animal);//'dog' 立即返回执行结果 11 var test = person.say.bind(animal); //返回一个函数 12 console.log(test);//fn 13 test();//'dog'
三 callee、caller
1,callee
arguments的一个属性,指向函数本身。
一般用在立即执行函数的递归调用。因为立即执行函数会忽略函数名,所以在递归调用时可以用callee来解决。
1 var factorial = (function(n){ 2 if(n == 1){ 3 return 1; 4 }else{ 5 return n * arguments.callee(n - 1); 6 } 7 }(50));
2,caller
函数的一个属性,指向执行时的父函数。全局执行时,fn.caller == null。好像并没有什么卵用?
另外,这两个属性在严格模式下还不能使用。
四 ES5严格模式
关于ES5的严格模式这里就不详细讲解了,有兴趣的同学可以去这里: