在提到上述的概念之前,首先想說說javascript中函數的隱含參數:arguments
Arguments
該對象代表正在執行的函數和調用它的函數的參數。
[function.]arguments[n]
參數function :選項。當前正在執行的 Function 對象的名字。 n :選項。要傳遞給 Function 對象的從0開始的參數值索引。
說明
Arguments是進行函數調用時,除了指定的參數外,還另外創建的一個隱藏對象。Arguments是一個類似數組但不是數組的對象,說它類似數組是因為其具有數組一樣的訪問性質及方式,可以由arguments[n]來訪問對應的單個參數的值,并擁有數組長度屬性length。還有就是arguments對象存儲的是實際傳遞給函數的參數,而不局限于函數聲明所定義的參數列表,而且不能顯式創建 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類)的代碼:
Array.prototype.selfvalue?
=
?
1
;
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 屬性的用法:
//
?caller?demo?{
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是
形參長度,由此可以判斷調用時形參長度是否和實參長度一致。
示例
//
callee可以打印其本身
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
)
}
比較一般的遞歸函數:
var
?sum?
=
?
function
(n){
????
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
來創建一個定義類的模式,
其實現代碼如下:
var
?Class?
=
?{
??create:?
function
()?{
????
return
?
function
()?{
??????
this
.initialize.apply(
this
,?arguments);
????}
??}
}
解析:從代碼看,該對象僅包含一個方法:Create,其返回一個函數,即類。但這也同時是類的
構造函數,其中調用initialize,而這個方法是在類創建時定義的初始化函數。通過如此途徑,
就可以實現prototype中的類創建模式
示例
:
var
?vehicle
=
Class.create();
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>