最开始在学习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
