javascript 中关于call方法的详解。

     关于javascript中的call方法,网上查了一些资料总是不得详解。总结网上的观点,call有两个妙用:

                      1: 继承。(不太喜欢这种继承方式。)

                      2: 修改函数运行时的this指针。

 

     js中关于call的解释如下:

                    javascript 中关于call方法的详解。

 

                          js关于call的这份文档容易让人迷糊。而《javascript权威指南》对call的描述就比较容易理解了。

 

                         javascript 中关于call方法的详解。          

 

                           

                          注意红色框中的部分,f.call(o)其原理就是先通过 o.m = f 将 f作为o的某个临时属性m存储,然后执行m,执行完毕后将m属性删除。

                          如 function f(){

                                                  var a = "name";

                                                  this.b = "test1";

                                                  this.add = function (){ return "add" }

                                       }

                               function o(){

                                                this. c = "c";  

                                       }

                               f.all(o);

                                                              图  1

 

                             图1中 f.call(o)其实相当于: function o(){ 

                                                                      this.c = "c" ;                                                          

                                                                      var a = "name";

                                                                      this.b = "test1";

                                                                      this.add = function (){ return "add" }

                                                          }

                             说白了,就是把f的方法在o中走一遍,但不做保存。既然不做保存,那么如何通过call实现继承和修改函数运行时的this指针等妙用?关键在于this,对,关键还是在于this的作用域。在之前的文章反复说过,this的作用域不是定义它的函数的作用域,而是执行时的作用域。

                              如下例子:

                                         javascript 中关于call方法的详解。

                                                                            图     2

 

                               如图2, 在执行A.call(this)之前,输出的this 不包含任何属性和方法,之后则继承了A的属性和方法。还是按照上面的方法解释,执行完A.call(this) (this此时代表执行时的作用域,即B)后,B构造函数如下:

                                     function B()

                                       {

                                          console.log(this);                                        

                                          this.test1 = "test1";

                                          this.test2 = "test2";

                                          this.add = function(){ return this.test1 + this.test2 ; }

                                           console.log(this); 

 

                                        }

                                   因为调用A.call(this)的时候,this作用域已经发生改变了,代表的都是B(),哪怕执行A中的方法和属性的时候,this也是代表着执行时的作用域B();所以如此才能实现继承,即将A()中的属性赋值和方法定义在B()中再走一遍。其实此时也修改了函数运行时的this指针。

 

                                    关于call的第二个妙用,修改函数运行时的this指针:

                                        这里采用一位贴吧大神的回答,做了一个测试例子,其实应该也是js中关于each遍历的定义。

                                         javascript 中关于call方法的详解。

                                                                                          图             3

 

                                       如图3,第一个中直接调用fn方法,其中因为没有定义函数作用域,输出的this表示window对象,即全局对象。而第二个中通过fn.call(array[index]  , index , array[index] )将fn方法放到array[index]作用域中执行,输出的this表示的是array[index]对象。

                                      换一个视角:

                                              javascript 中关于call方法的详解。

                                                                            图   4

 

 

                                       如图 4 , 通过each方法中调用call方法可以实现遍历。我们假设array[index]是DOM元素集合,如标签为li的所有集合,假设fn是我们自定义实现的方法,不就是我们用的each遍历DOM元素的方法吗?

                                       javascript 中关于call方法的详解。

                                       javascript 中关于call方法的详解。

                                                                               图               5

 

                                           如图5,简单的代码即实现了遍历DOM元素的each方法。



先看看关于call()的官方解释,“调用一个对象的一个方法,以另一个对象替换当前对象。”,看了这样的解释,或许让你更摸不着头脑了。看例子:

[javascript] view plain copy
  1. var x = "我是全局变量";    //定义全局变量x  
  2. function a(){         //定义函数类结构a    
  3.     this.x = "我是在函数类结构a中声明的哦";      
  4. }  
  5. //定义普通函数,弹出当前指针所包含的变量x的值  
  6. function f(){         
  7.     alert (this.x);  
  8. }  
  9. //返回值为“我是在函数类结构a中声明的哦”  
  10. f.call(new a());  

我的理解是,f.call(new a())就是把函数(其实也是对象)f复制到被调用对象“new a()”下面去解析,事实上和下面这段代码的解析结果一样:

[javascript] view plain copy
  1. function a(){  
  2.   this.x = "我是在函数类结构a中声明的哦";  
  3.   alert(this.x);      
  4. }  
  5. a();  

只不过此时变量X的作用域不同而已,咿…看起来好像有点继承的味道哦,难道不是吗?在上例中,f完全被构造函数a的实力对象继承了,如果说这还不足以说明a.call(b)是一种继承模式,那么再看一个更具有继承味道的用法吧。

[javascript] view plain copy
  1. function f(){      
  2.     this.a ="a";      
  3.     this.b = function(){      
  4.         alert("b");  
  5.     }  
  6. }  
  7. function e(){      
  8.     f.call(this);       
  9. }  
  10. var c = new e();  
  11. alert(c.a);  //弹出a  
  12. c.b();    //弹出b  
在这个例子中,只要会使用浏览器的朋友,都能看得出来e完全继承了f的属性和方法,否则是无法解释的,因为在e中并没有定义属性a和b,那么按常理推断在e的实例对象c中,并不会出现这两个属性。