最开始在学习javascript的时候,对caller,callee,call,apply也是很迷惑的,一直搞不懂,慢慢地经过一些学习,对他们也有了一点的了解,本文是最初学习时收藏保存的一些文章和练习记录,搜索一些资料,经过整理的一个简单笔记的记录,供收藏学习使用,如有什么错误或者不对的地方请大家指出,谢谢!

一、caller

返回一个对函数的引用,该函数调用了当前函数functionName.caller  ,functionName 对象是所执行函数的名称。

对于函数来说,caller 属性只有在函数执行时才有定义。 如果函数是由 Javascript 程序的顶层调用的,那么 caller 包含的就是 null,如果在字符串上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本。 

那么如何理解caller呢, 先来举个简单的例子:

// caller demo
function callerDemo() { 
    if (callerDemo.caller) { 
        var a= callerDemo.caller.toString(); 
        alert(a); 
    } else { 
        alert("this is a top function"); 
    } 
} 
function handleCaller() { 
    callerDemo(); 
} 
  

上面的例子,可以看出,它就是返回一个调用数据的引用。(指向请求调用的函数) 也由此可以看出,当在这样的情况下,onclick触发事件的时候总是带着匿名函数的。

二、callee 

返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。

 [function.]arguments.callee

可选项 function 参数是当前正在执行的 Function 对象的名称。

说明 : callee 属性的初始值就是正被执行的 Function 对象。

下面通过例子来看看:

function calleeDemo() { 
    alert(arguments.callee); 
} 
function calleeLengthDemo(arg1, arg2) { 
    if (arguments.length==arguments.callee.length) { 
        window.alert("验证形参和实参长度正确!"); 
        return; 
    } else { 
        alert("实参长度:" +arguments.length); 
        alert("形参长度: " +arguments.callee.length); 
    } 
} 

从上面的例子可以看出,callee可以用来打在执行函数,也就是指向被调用的函数。上面的例子就说明calee可以打印其本身,当然还有其它的一些用途。还有需要注意的是callee拥有length属性,这个属性有时候用于验证还是比较好的。而length属性中arguments.length是实参长度,arguments.callee.length是形参长度,由此可以判断调用时形参长度是否和实参长度一致。

由于callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,这有利于匿名函数的递归或者保证函数的封装性,例如下边示例的递归计算1到n的自然数之和。而该属性仅当相关函数正在执行时才可用。

 //递归计算
 var sum = function(n){
   if (n <= 0) {                       
       return 1;
   }else{
      return n +arguments.callee(n - 1)
   }
}
//比较一般的递归函数:
 var sum = function(n){
    if (1==n) return 1;
    else return n + sum (n-1);

调用:alert(sum(100));

其中函数内部包含了对sum自身的引用,函数名仅仅是一个变量名,在函数内部调用sum即相当于调用

一个全局变量,不能很好的体现出是调用自身,这时使用callee会是一个比较好的方法。

三、call 和 apply

call 和 apply 都是为了改变某个函数运行时的 context 即上下文而存在的,简单点说,call和apply就是为了动态改变函数体内部 this 的指向而出现的

call 和 apply 的定义

call 定义:调用一个对象的一个方法,以另一个对象替换当前对象。

apply的定义:应用某一对象的一个方法,用另一个对象替换当前对象。

call 和 apply 的语法、相同点和作用

call语法:objcall(thisObj, arg1,arg2)

apply语法:obj.apply(thisObj, [arg1, arg2])

两者的作用是完全一致的,都是把obj(即this)绑定到thisObj,这时候thisObj具备了obj的属性和方法。或者说thisObj集成了obj的属性和方法。

call 和 apply 的区别

不同点:只是他们接受参数的方式不太一样(第二个参数),call 需要把参数按顺序传递进去,而 apply 则是把参数放在数组里。(也就是说apply传入的是一个参数数组,也就是将多个参数组合成为一个数组传入)

最简单的示例代码如下:

function funA(name,age,pay) { 
	console.log("我的姓名是:"+name+",我的年龄是:"+age+",我的薪水是:"+pay*13);
} 
function funB(pay,num){
    console.log("我的薪水是:"+(pay-num))
};
funA.call(funB,"真的",38,15000); //我的姓名是:真的,我的年龄是:38,我的薪水是:195000
funB.apply(funA,[15000,3000]);      //我的薪水是:12000

从上面例子可以看出,apply接受的是数组参数,call接受的是连续参数

下面再来看看call, apply的一些具体应用:

// simple call demo 
function simpleCallDemo(arg) { 
    window.alert(arg); 
} 
function handleSPC(arg) { 
    simpleCallDemo.call(this, arg); 
} 
// simple apply demo 
function simpleApplyDemo(arg) { 
    window.alert(arg); 
} 
function handleSPA(arg) { 
    simpleApplyDemo.apply(this, arguments); 
} 

从上面简单的例子可以看出,call和apply可以把当前的参数传递给另外一个函数的参数中,从而调用另一个函数的应用。有的时候这是一个很实用的方法,当然,用call或是apply(是参数或是数组),看实际情况而定了。 

下面来看另一个应用:

call和apply还有一个技巧在里面,就是用call和apply应用另一个函数(类)以后,当前的函数(类)就具备了另一个函数(类)的方法或者是属性,这也可以称之为”继承”。具体看下面示例代码。 :

// inherit 
function base() { 
    this.member = "never-online"; 
    this.method = function() { 
        window.alert(this.member); 
    } 
} 
function extend() { 
    base.call(this); 
    window.alert(member); 
    window.alert(this.method); 
} 

上面的例子可以看出,通过call之后,extend可以继承到base的方法和属性。

再看看一个apply的应用

// advanced apply demo 
function adApplyDemo(x) { 
    return ("this is never-online, BlueDestiny '" + x + "' demo"); 
} 
function handleAdApplyDemo(obj, fname, before) { 
  var oldFunc = obj[fname]; 
  obj[fname] = function() { 
    return oldFunc.apply(this, before(arguments)); 
  }; 
} 
function hellowordFunc(args) { 
  args[0] = "hello " + args[0]; 
  return args; 
} 
function applyBefore() { 
    alert(adApplyDemo("world")); 
} 
function applyAfter() { 
    handleAdApplyDemo(this, "adApplyDemo", hellowordFunc); 
    alert(adApplyDemo("world")); // Hello world! 
} 

 需要注意的是,要先点”原始的adApplyDemo(‘world’)”按钮,如果先点”应用后的adApplyDemo(‘world’)”按扭,会先应用了apply方法,这样原始的值将会被改变。或许有的朋友没有发现有什么特别的,我在这里指明一下,当点击左边的按扭时,只有”this is never-online, BlueDestiny ‘world’ demo”, 当点击右边的按扭后,会现结果是”this is never-online, BlueDestiny ‘hello world’ demo”,再点点左边的按扭,看看结果又会是什么呢?自己试试看:D,已经改写了函数adApplyDemo。这个例子则说明了call和apply的”真正”作用了。 

四、call 和 apply的常见应用场景

1、类(伪)数组使用数组方法

有时,通过document.getElementsByTagName选择到的节点,是一种类似array的数组。但它不能应用Array下的push,pop等方法。这时我们可以通过:

var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

这样domNodes就可以应用Array下的所有方法了。

2、获取数组中的最大值和最小
var  numbers = [2, 400 , 300, -200];  
var maxNumbers = Math.max.apply(Math, numbers),   //400
     minNumbers = Math.min.call(Math,2, 400 , 300, -200);  //-200
3、验证是否是数组(前提是toString()方法没有被重写过)

function isArray(obj){  
    returnObject.prototype.toString.call(obj) === '[object Array]' ;
}

4、数组之间的追加

var array1 = [20 , "lao" , {name:"yuan"} , -34];  
var array2 = ["dao" , 111 , 110];  
Array.prototype.push.apply(array1, array2);  
console.log(array1); //array1 值为[20, "lao", {name:"yuan"}, -34, "dao", 111, 110]

参考:


全面理解javascript的caller,callee,call,apply概念(修改版)

Javascript – 全面理解 caller,callee,call,apply (由于原链接找不到,本链接是以前保存的原版html,放在本站的连接)

深入浅出妙用Javascript中apply、call、bind