主要解决一下几个问题:javascript
1.apply和call的区别在哪里java
2.什么状况下用apply,什么状况下用call数组
3.apply的其余巧妙用法(通常在什么状况下可使用apply)网络
apply:方法能劫持另一个对象的方法,继承另一个对象的属性.app
Function.apply(obj,args)方法能接收两个参数函数
obj:这个对象将代替Function类里this对象测试
args:这个是数组,它将做为参数传给Function(args-->arguments)this
call:和apply的意思同样,只不过是参数列表不同.spa
Function.call(obj,[param1[,param2[,…[,paramN]]]])prototype
obj:这个对象将代替Function类里this对象
params:这个是一个参数列表
<script type="text/javascript"> /*定义一我的类*/ function Person(name,age) { this.name=name; this.age=age; } /*定义一个学生类*/ functionStudent(name,age,grade) { Person.apply(this,arguments); this.grade=grade; } //建立一个学生类 var student=new Student("zhangsan",21,"一年级"); //测试 alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade); //你们能够看到测试结果name:zhangsan age:21 grade:一年级 //学生类里面我没有给name和age属性赋值啊,为何又存在这两个属性的值呢,这个就是apply的神奇之处. </script>
分析: Person.apply(this,arguments);
this:在建立对象在这个时候表明的是student
arguments:是一个数组,也就是[“zhangsan”,”21”,”一年级”];
也就是通俗一点讲就是:用student去执行Person这个类里面的内容,在Person这个类里面存在this.name等之类的语句,这样就将属性建立到了student对象里面
在Studen函数里面能够将apply中修改为以下:
Person.call(this,name,age);
这样就ok了
在给对象参数的状况下,若是参数的形式是数组的时候,好比apply示例里面传递了参数arguments,这个参数是数组类型,而且在调用 Person的时候参数的列表是对应一致的(也就是Person和Student的参数列表前两位是一致的) 就能够采用 apply , 若是个人Person的参数列表是这样的(age,name),而Student的参数列表是(name,age,grade),这样就能够用call来 实现了,也就是直接指定参数列表对应值的位置(Person.call(this,age,name,grade));
细心的人可能已经察觉到,在我调用apply方法的时候,第一个参数是对象(this), 第二个参数是一个数组集合, 在调用Person的时候,他须要的不是一个数组,可是为何他给我一个数组我仍然能够将数组解析为一个一个的参数,这 个就是apply的一个巧妙的用处,能够将一个数组默认的转换为一个参数列表([param1,param2,param3] 转换为 param1,param2,param3) 这个若是让咱们用程序来实现将数组的每个项,来装换为参数的列表,可能都得费一会功夫,借助apply的这点特性,因此就有了如下高效率的方法:
a)Math.max 能够实现获得数组中最大的一项
由于Math.max 参数里面不支持Math.max([param1,param2]) 也就是数组
可是它支持Math.max(param1,param2,param3…),因此能够根据刚才apply的那个特色来解决 var max=Math.max.apply(null,array),这样轻易的能够获得一个数组中最大的一项(apply会将一个数组装换为一个参数接一个参数的传递给方法)
这块在调用的时候第一个参数给了一个null,这个是由于没有对象去调用这个方法,我只须要用这个方法帮我运算,获得返回的结果就行,.因此直接传递了一个null过去
b)Math.min 能够实现获得数组中最小的一项
一样和 max是一个思想 var min=Math.min.apply(null,array);
c)Array.prototype.push 能够实现两个数组合并
一样push方法没有提供push一个数组,可是它提供了push(param1,param,…paramN) 因此一样也能够经过apply来装换一下这个数组,即:
vararr1=new Array("1","2","3"); vararr2=new Array("4","5","6"); Array.prototype.push.apply(arr1,arr2);
也能够这样理解,arr1调用了push方法,参数是经过apply将数组装换为参数列表的集合.
一般在什么状况下,可使用apply相似Math.min等之类的特殊用法:
通常在目标函数只须要n个参数列表,而不接收一个数组的形式([param1[,param2[,…[,paramN]]]]),能够经过apply的方式巧妙地解决这个问题!
一、在JavaScript中,arguments对象是比较特别的一个对象,其实是 当前函数的一个内置属性。arguments很是相似Array,但实际上又不是一个Array实例。能够经过以下代码得以证明(固然,实际上,在函数 funcArg中,调用arguments是没必要要写成funcArg.arguments,直接写arguments便可)。
Array.prototype.testArg = "test"; function funcArg() { alert(funcArg.arguments.testArg); alert(funcArg.arguments[0]); } alert(new Array().testArg); // result: "test" 8 funcArg(10); // result: "undefined" "10"
二、arguments对象的长度是由实参个数而不是形参个数决定的。形参是函数内部从新开辟内存空间存储的变量,可是其与arguments对象 内存空间并不重叠。对于arguments和值都存在的状况下,二者值是同步的,可是针对其中一个无值的状况下,对于此无值的情形值不会得以同步。以下代 码能够得以验证。
function f(a, b, c){ alert(arguments.length); // result: "2" a = 100; alert(arguments[0]); // result: "100" arguments[0] = "qqyumidi"; alert(a); // result: "qqyumidi" alert(c); // result: "undefined" c = 2012; alert(arguments[2]); // result: "undefined" } f(1, 2);
三、由JavaScript中函数的声明和调用特性,能够看出JavaScript中函数是不能重载的。
根据其余语言中重载的依据:"函数返回值不一样或形参个数不一样",咱们能够得出上述结论:
第一:Javascript函数的声明是没有返回值类型这一说法的;
第二:JavaScript中形参的个数严格意义上来说只是为了方便在函数中的变量操做,实际上实参已经存储在arguments对象中了。
另外,从JavaScript函数自己深刻理解为何JavaScript中函数是不能重载的:在JavaScript中,函数其实也是对象,函数 名是关于函数的引用,或者说函数名自己就是变量。对于以下所示的函数声明与函数表达式,其实含以上是同样的(在不考虑函数声明与函数表达式区别的前提 下),很是有利于咱们理解JavaScript中函数是不能重载的这一特性。
function f(a){ return a + 10; } function f(a){ return a - 10; } // 在不考虑函数声明与函数表达式区别的前提下,其等价于以下 var f = function(a){ return a + 10; } var f = function(a){ return a - 10; }
四、arguments对象中有一个很是有用的属性:callee。arguments.callee返回此arguments对象所在的当前函数引用。在使用函数递归调用时推荐使用arguments.callee代替函数名自己。
以下:
function count(a){ if(a==1){ return 1; } return a + arguments.callee(--a); } var mm = count(10); alert(mm);
functionName.caller 返回调用者。
看看下面的函数,你们能够复制到VS中执行下
复制代码 代码以下:
function caller() { if (caller.caller) { alert(caller.caller.toString()); } else { alert("函数直接执行"); } } function handleCaller() { caller(); } handleCaller(); caller();
你们会发现第一个alert会弹出调用caller函数的调用者handleCaller,而第二个alert因为没有在其余函数体内调用,因此caller为null,就执行了 alert("函数直接执行");
返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文.
callee是arguments 的一个属性成员,它表示对函数对象自己的引用,这有利于匿名
函数的递归或者保证函数的封装性。 下面一段代码先说明callee的用法,实例代码摘自网上
复制代码 代码以下:
function calleeLengthDemo(arg1, arg2) { alert(arguments.callee.toString()); if (arguments.length == arguments.callee.length) { window.alert("验证形参和实参长度正确!"); return; } else { alert("实参长度:" + arguments.length); alert("形参长度: " + arguments.callee.length); } } calleeLengthDemo(1);
第一个消息框弹出calleeLengthDemo函数自己,这说明callee就是函数自己对象的引用。callee还有个很是有用的应用就是用来判断实际参数跟行参是否一致。上面的代码第一个消息框会弹出实际参数的长度为1,形式参数也就是函数自己的参数长度为2.
callee的应用场景通常用于匿名函数
你们看下下面一段代码 摘自网络
复制代码 代码以下:
var fn=function(n){ if(n>0) return n+fn(n-1); return 0; } alert(fn(10))
函数内部包含了对自身的引用,函数名仅仅是一个变量名,在函数内部调用即至关于调用
一个全局变量,不能很好的体现出是调用自身,这时使用callee会是一个比较好的方法
复制代码 代码以下:
var fn=(function(n){ if(n>0) return n+arguments.callee(n-1); return 0; })(10); alert(fn)
这样就让代码更加简练。又防止了全局变量的污染。
caller的应用场景 主要用于察看函数自己被哪一个函数调用。