在提到上述的概念之前,首先想說說javascript中函數的隱含參數:arguments
Arguments
該對象代表正在執行的函數和調用它的函數的參數。
[function.]arguments[n]
參數function :選項。當前正在執行的 Function 對象的名字。 n :選項。要傳遞給 Function 對象的從0開始的參數值索引。
說明
Arguments是進行函數調用時,除了指定的參數外,還另外創建的一個隱藏對象。Arguments是一個類似數組但不是數組的對象,說它類似數組是因為其具有數組一樣的訪問性質及方式,可以由arguments[n]來訪問對應的單個參數的值,并擁有數組長度屬性length。還有就是arguments對象存儲的是實際傳遞給函數的參數,而不局限于函數聲明所定義的參數列表,而且不能顯式創建 arguments 對象。arguments 對象只有函數開始時才可用。下邊例子詳細說明了這些性質:
function ?ArgTest(a,?b){
??? var ?i,?s? = ? " The?ArgTest?function?expected? " ;
??? var ?numargs? = ?arguments.length;????? // ?獲取被傳遞參數的數值。
??? var ?expargs? = ?ArgTest.length;??????? // ?獲取期望參數的數值。
??? if ?(expargs? < ? 2 )
??????s? += ?expargs? + ? " ?argument.? " ;
??? else
??????s? += ?expargs? + ? " ?arguments.? " ;
??? if ?(numargs? < ? 2 )
??????s? += ?numargs? + ? " ?was?passed. " ;
??? else
??????s? += ?numargs? + ? " ?were?passed. " ;
???s? += ? " \n\n "
??? for ?(i? = 0 ?;?i? < ?numargs;?i ++ ){?????? // ?獲取參數內容。
???s? += ? " ??Arg? " ? + ?i? + ? " ?=? " ? + ?arguments[i]? + ? " \n " ;
???}
??? return (s);?????????????????????????? // ?返回參數列表。
}
在此添加了一個說明arguments不是數組(Array類)的代碼:
alert( new ?Array().selfvalue);
function ?testAguments(){
????alert(arguments.selfvalue);
}
運行代碼你會發現第一個alert顯示1,這表示數組對象擁有selfvalue屬性,值為1,而當你調用函數testAguments時,你會發現顯示的是“undefined”,說明了不是arguments的屬性,即arguments并不是一個數組對象。
?caller
? 返回一個對函數的引用,該函數調用了當前函數。
? functionName.caller
? functionName
對象是所執行函數的名稱。
說明
對于函數來說,caller屬性只有在函數執行時才有定義。如果函數是由頂層調用的,那么 caller包含的就是 null 。如果在字符串上下文中使用 caller屬性,那么結果和 functionName.toString 一樣,也就是說,顯示的是函數的反編譯文本。
下面的例子說明了 caller 屬性的用法:
function ?callerDemo()?{
???? if ?(callerDemo.caller)?{
???????? var ?a = ?callerDemo.caller.toString();
????????alert(a);
????}? else ?{
????????alert( " this?is?a?top?function " );
????}
}
function ?handleCaller()?{
????callerDemo();
}
callee
??? 返回正被執行的 Function 對象,也就是所指定的 Function 對象的正文。
[function.]arguments.callee
可選項 function參數是當前正在執行的 Function對象的名稱。
說明
callee 屬性的初始值就是正被執行的 Function 對象。
callee
屬性是 arguments 對象的一個成員,它表示對函數對象本身的引用,這有利于匿名
函數的遞歸或者保證函數的封裝性,例如下邊示例的遞歸計算1到n的自然數之和。而該屬性
僅當相關函數正在執行時才可用。還有需要注意的是callee擁有length屬性,這個屬性有時候
用于驗證還是比較好的。arguments.length是實參長度,arguments.callee.length是
形參長度,由此可以判斷調用時形參長度是否和實參長度一致。
示例
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);
????}
}
// 遞歸計算
var ?sum? = ? function (n){
?? if ?(n? <= ? 0 )????????????????????????
?? return ? 1 ;
?? else
???? return ?n?+arguments.callee(n? - ? 1 )
}
比較一般的遞歸函數:
???? if ?( 1 == n)? return ? 1 ;
else ? return ?n? + ?sum?(n - 1 );
調用時:alert(sum(100));
其中函數內部包含了對sum自身的引用,函數名僅僅是一個變量名,在函數內部調用sum即相當于調用
一個全局變量,不能很好的體現出是調用自身,這時使用callee會是一個比較好的方法。
apply and call
?? 它們的作用都是將函數綁定到另外一個對象上去運行,兩者僅在定義參數方式有所區別:
??? apply
(thisArg,argArray);
??? call
(thisArg[,arg1,arg2…] ]);
即所有函數內部的 this 指針都會被賦值為 thisArg ,這可實現將函數作為另外一個對象的方法運行的目的
apply
的說明
如果 argArray不是一個有效的數組或者不是 arguments對象,那么將導致一個 TypeError。
如果沒有提供 argArray和
thisArg
任何一個參數,那么 Global 對象將被用作
thisArg
,
并且無法被傳遞任何參數。
call的說明
call
方法可將一個函數的對象上下文從初始的上下文改變為由
thisArg
指定的新對象。
如果沒有提供
thisArg
參數,那么 Global 對象被用作
thisArg
相關技巧 :
應用
call
和
apply
還有一個技巧在里面,就是用
call
和
apply
應用另一個函數(類)以后,當前的
函數(類)就具備了另一個函數(類)的方法或者是屬性,這也可以稱之為“繼承”。看下面示例:
function ?base()?{
???? this .member? = ? " ?dnnsun_Member " ;
???? this .method? = ? function ()?{
????????window.alert( this .member);
????}
}
function ?extend()?{
????base.call( this );
????window.alert(member);
????window.alert( this .method);
}
上面的例子可以看出,通過call之后,extend可以繼承到base的方法和屬性。
順便提一下,在
javascript
框架
prototype
里就使用
apply
來創建一個定義類的模式,
其實現代碼如下:
??create:? function ()?{
???? return ? function ()?{
?????? this .initialize.apply( this ,?arguments);
????}
??}
}
解析:從代碼看,該對象僅包含一個方法:Create,其返回一個函數,即類。但這也同時是類的
構造函數,其中調用initialize,而這個方法是在類創建時定義的初始化函數。通過如此途徑,
就可以實現prototype中的類創建模式
示例 :
vehicle.prototype = {
????initialize: function (type){
???????? this .type = type;
????}
????showSelf: function (){
????????alert( " this?vehicle?is? " + ? this .type);
????}
}
var ?moto = new ?vehicle( " Moto " );
moto.showSelf();
更詳細的關于prototype信息請到其官方網站查看。
//下面是一個總合實例
<script>
function Point2D(x, y)
{
?this.x = x;
?this.y = y;
?Point2D.prototype.quadrant = function()
?{
? if (x > 0 && y > 0) return "I";
? else if (x < 0 && y > 0) return "II";
? else if (x < 0 && y < 0) return "III";
? else if (x > 0 && y < 0) return "IV";
? else if (x == 0) return "x-axis";
? else if (y == 0) return "y-axis";
? else throw new Error();
?}
?Point2D.prototype.toVector = function()
?{
? return new Vector2D(x, y);
?}
?Point2D.prototype.distance = function() //求距離
?{
? if (arguments.length == 1 && arguments[0] instanceof Point2D)
? {
?? return this._point_distance.apply(this, arguments);
? }
? else if (arguments.length == 1 && arguments[0] instanceof Vector2D)
? {
?? return this._vector_distance.apply(this, arguments);
? }
? else
? {
?? throw new Error("Argument Error!");
? }
?}
?Point2D.prototype._point_distance = function(p)? //求兩點之間的距離(函數重載)
?{
? return (new Vector2D(p,this)).length();
?}
?Point2D.prototype._vector_distance = function(v)? //求點到向量的距離(函數重載)
?{
? var v1 = new Vector2D(this, v.start);
? var v2 = new Vector2D(this, v.end);
? var area = Math.abs(v1.cross(v2));? //平行四邊形面積 = v1 X v2 = |v1v2|sin(v1,v2)
?
? return area / v.length();?? //平行四邊形面積除以底邊長度即為點到向量的距離
?}
}
function Vector2D()
{
?if (arguments.length == 2 && arguments[0] instanceof Point2D && arguments[1] instanceof Point2D)
?{
? _point_point_Vector2D.apply(this, arguments);
?}
?else if (arguments.length == 2 && !isNaN(arguments[0]) && !isNaN(arguments[1]))
?{
? _double_double_Vector2D.apply(this, arguments);
?}
?else if (arguments.length == 4 && !isNaN(arguments[0]) && !isNaN(arguments[1])
? && !isNaN(arguments[2]) && !isNaN(arguments[3]))
?{
? _double_double_double_double_Vector2D.apply(this, arguments);
?}
?else
?{
? throw new Error("Argument Error!");
?}
}
function _point_point_Vector2D(p1, p2)??
{
?this.start = p1;
?this.end = p2;
?Vector2D.prototype.length = function() //求向量的長度
?{
? return Math.sqrt(this.pond_x() * this.pond_x() + this.pond_y() * this.pond_y());
?}
?Vector2D.prototype.pond_x = function() //x方向分量
?{
? return this.start.x - this.end.x;
?}
?Vector2D.prototype.pond_y = function()
?{
? return this.start.y - this.end.y;
?}
?Vector2D.prototype.cross = function(v)?? //求向量的交積 P1 X P2 = x1y2 - x2y1
?{
? return this.pond_x() * v.pond_y() - v.pond_x() * this.pond_y();
?}
}
function _double_double_Vector2D(x,y) //重載構造函數Vector2D
{
?this.pointPairs = new Array();
?this.pointPairs[0] = new Point2D(0, 0);
?this.pointPairs[1] = new Point2D(x, y);
?_point_point_Vector2D.apply(this, this.pointPairs);
}
function _double_double_double_double_Vector2D(x1, y1, x2, y2)? //重載構造函數Vector2D
{
?this.pointPairs = new Array();
?this.pointPairs[0] = new Point2D(x1, y1);
?this.pointPairs[1] = new Point2D(x2, y2);
?_point_point_Vector2D.apply(this, this.pointPairs);
}
var p1 = new Point2D(0,0);
var p2 = new Point2D(10,10);
var v1 = new Vector2D(p1,p2);? //通過兩個點(p1,p2)的方式來構造向量V1
alert("向量v1長度:"+v1.length());
var v2 = new Vector2D(0,0,5,5);? //通過四個坐標(x1,y1,x2,y2)的方式來構造向量V2
alert("向量v2長度:"+v2.length());
var v3 = new Vector2D(0,10);? //通過指定終點的方式來構造向量V3
alert("向量v3長度:"+v3.length());
alert("向量v1與v2的交積:"+v1.cross(v2));? //求V1 X V2 (因為平行,所以結果為0)
var p3 = new Point2D(10,0);
alert("點p1與p3的距離:"+p1.distance(p3));
alert("點p3與向量v1的距離:"+p3.distance(v1));
</script>