js apply/call/caller/callee/bind使用方法與區別分析

microsoft_kk發表於2014-07-23

一、call 方法
呼叫一個物件的一個方法,以另一個物件替換當前物件(其實就是更改物件的內部指標,即改變物件的this指向的內容)。
Js程式碼
call([thisObj[,arg1[, arg2[, [,.argN]]]]])
引數
thisObj
可選項。將被用作當前物件的物件。
arg1, arg2, , argN
可選項。將被傳遞方法引數序列。
說明
call 方法可以用來代替另一個物件呼叫一個方法。call 方法可將一個函式的物件上下文從初始的上下文改變為由 thisObj 指定的新物件。如果沒有提供 thisObj 引數,那麼 Global 物件被用作 thisObj。
Js程式碼
複製程式碼 程式碼如下:

<input type="text" id="myText" value="input text"> Code
function Obj(){this.value="物件!";}
var value="global 變數";
function Fun1(){alert(this.value);}
window.Fun1(); //global 變數
Fun1.call(window); //global 變數
Fun1.call(document.getElementById('myText')); //input text
Fun1.call(new Obj()); //物件!

Js程式碼
Code
複製程式碼 程式碼如下:

var first_object = {
num: 42
};
var second_object = {
num: 24
};
function multiply(mult) {
return this.num * mult;
}
multiply.call(first_object, 5); // returns 42 * 5
multiply.call(second_object, 5); // returns 24 * 5

二、apply方法
apply方法的第一個引數也是要傳入給當前物件的物件,即函式內部的this。後面的引數都是傳遞給當前物件的引數。
對於apply和call兩者在作用上是相同的,但兩者在引數上有區別的。對於第一個引數意義都一樣,但對第二個引數:apply傳入的是一個引數陣列,也就是將多個引數組合成為一個陣列傳入,而call則作為call的引數傳入(從第二個引數開始)。
如 func.call(func1,var1,var2,var3)對應的apply寫法為:func.apply(func1, [var1,var2,var3])同時使用apply的好處是可以直接將當前函式的arguments物件作為apply的第二個引數傳入。
Js程式碼
複製程式碼 程式碼如下:

var func=new function(){this.a="func"}
var myfunc=function(x,y){
var a="myfunc";
alert(this.a);
alert(x + y);
}
myfunc.call(func,"var"," fun");// "func" "var fun"
myfunc.apply(func,["var"," fun"]);// "func" "var fun"

三、caller 屬性
返回一個對函式的引用,即呼叫了當前函式的函式體。
functionName.caller :functionName 物件是所執行函式的名稱。
說明:
對 於函式來說,caller 屬性只有在函式執行時才有定義。 如果函式是由 JScript 程式的頂層呼叫的,那麼 caller 包含的就是 null 。如果在字串上下文中使用 caller 屬性,那麼結果和 functionName.toString 一樣,也就是說,顯示的是函式的反編譯文字。
Js程式碼
複製程式碼 程式碼如下:

function CallLevel(){
if (CallLevel.caller == null)
alert("CallLevel was called from the top level.");
else
alert("CallLevel was called by another function:\n"+CallLevel.caller);
}
function funCaller(){
CallLevel();
}
CallLevel();
funCaller()

四、callee屬性
返回正被執行的 Function 物件,也就是所指定的 Function 物件的正文。
[function.]arguments.callee:可選項 function 引數是當前正在執行的 Function 物件的名稱。
說明:
callee 屬性的初始值就是正被執行的 Function 物件。
callee 屬性是 arguments 物件的一個成員,它表示對函式物件本身的引用,這有利於匿
函式的遞迴或者保證函式的封裝性,例如下邊示例的遞迴計算1到n的自然數之和。而該屬性
僅當相關函式正在執行時才可用。還有需要注意的是callee擁有length屬性,這個屬性有時
用於驗證還是比較好的。arguments.length是實參長度,arguments.callee.length是
形參長度,由此可以判斷呼叫時形參長度是否和實參長度一致。
Js程式碼
複製程式碼 程式碼如下:

//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)
}

五、bind
Js程式碼
複製程式碼 程式碼如下:

var first_object = {
num: 42
};
var second_object = {
num: 24
};
function multiply(mult) {
return this.num * mult;
}
Function.prototype.bind = function(obj) {
var method = this,
temp = function() {
return method.apply(obj, arguments);
};
return temp;
}
var first_multiply = multiply.bind(first_object);
first_multiply(5); // returns 42 * 5
var second_multiply = multiply.bind(second_object);
second_multiply(5); // returns 24 * 5

六、JS閉包(Closure)
所謂“閉包”,指的是一個擁有許多變數和繫結了這些變數的環境的表示式(通常是一個函式),因而這些變數也是該表示式的一部分。
關 於閉包,最簡單的描述就是 ECMAScript 允許使用內部函式--即函式定義和函式表示式位於另一個函式的函式體內。而且,這些內部函式可以訪問它們所在的外部函式中宣告的所有區域性變數、引數和宣告 的其他內部函式。當其中一個這樣的內部函式在包含它們的外部函式之外被呼叫時,就會形成閉包。也就是說,內部函式會在外部函式返回後被執行。而當這個內部 函式執行時,它仍然必需訪問其外部函式的區域性變數、引數以及其他內部函式。這些區域性變數、引數和函式宣告(最初時)的值是外部函式返回時的值,但也會受到 內部函式的影響。
簡而言之,閉包的作用就是在out function執行完並返回後,閉包使得Javascript的垃圾回收機制GC不會收回out function所佔用的資源,因為out function的內部函式inner function的執行需要依賴out function中的變數。
閉包的兩個特點:
1、作為一個函式變數的一個引用 - 當函式返回時,其處於啟用狀態。
2、一個閉包就是當一個函式返回時,一個沒有釋放資源的棧區。
例1:
Html程式碼
複製程式碼 程式碼如下:

<script type="text/javascript">
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gAlertNumber = function() { alert(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}
</script>
<button onclick="setupSomeGlobals()">生成 - setupSomeGlobals()</button>
<button onclick="gAlertNumber()">輸出值 - gAlertNumber()</button>
<button onclick="gIncreaseNumber()">增加 - gIncreaseNumber()</button>
<button onclick="gSetNumber(5)">賦值5 - gSetNumber(5)</button>

例2:
Html程式碼
複製程式碼 程式碼如下:

<script type="text/javascript">
function newClosure(someNum, someRef) {
// Local variables that end up within closure
var num = someNum;
var anArray = [1,2,3];
var ref = someRef;
return function(x) {
num += x;
anArray.push(num);
alert('num: ' + num +
' nanArray ' + anArray.toString() +
' nref.someVar ' + ref.someVar);
}
}
var closure1 = newClosure(40, {someVar:' never-online'})
var closure2 = newClosure(99, {someVar:' BlueDestiny'})
closure1(4)
closure2(3)
</script>

例3:
Js程式碼
複製程式碼 程式碼如下:

<script language="javascript">
/* 宣告一個全域性變數 - getImgInPositionedDivHtml - 並將一次呼叫一個外部函式表示式返回的內部函式賦給它。
這個內部函式會返回一個用於表示絕對定位的 DIV 元素包圍著一個 IMG 元素 的 HTML 字串,這樣一來,
所有可變的屬性值都由呼叫該函式時的引數提供:
*/
var getImgInPositionedDivHtml = (function(){
/* 外部函式表示式的區域性變數 - buffAr - 儲存著緩衝陣列。這個陣列只會被建立一次,生成的陣列例項對內部函式而言永遠是可用的
因此,可供每次呼叫這個內部函式時使用。
其中的空字串用作資料佔位符,相應的資料
將由內部函式插入到這個陣列中:
*/
var buffAr = [
'<div id="',
'', //index 1, DIV ID 屬性
'" style="position:absolute;top:',
'', //index 3, DIV 頂部位置
'px;left:',
'', //index 5, DIV 左端位置
'px;width:',
'', //index 7, DIV 寬度
'px;height:',
'', //index 9, DIV 高度
'px;overflow:hidden;\"><img src=\"',
'', //index 11, IMG URL
'\" width=\"',
'', //index 13, IMG 寬度
'\" height=\"',
'', //index 15, IMG 調蓄
'\" alt=\"',
'', //index 17, IMG alt 文字內容
'\"><\/div>'
];
/* 返回作為對函式表示式求值後結果的內部函式物件。
這個內部函式就是每次呼叫執行的函式
- getImgInPositionedDivHtml( ... ) -
*/
return (function(url, id, width, height, top, left, altText){
/* 將不同的引數插入到緩衝陣列相應的位置:
*/
buffAr[1] = id;
buffAr[3] = top;
buffAr[5] = left;
buffAr[13] = (buffAr[7] = width);
buffAr[15] = (buffAr[9] = height);
buffAr[11] = url;
buffAr[17] = altText;
/* 返回通過使用空字串(相當於將陣列元素連線起來)
連線陣列每個元素後形成的字串:
*/
return buffAr.join('');
}); //:內部函式表示式結束。
})();//自呼叫
alert(getImgInPositionedDivHtml);//顯示返回的函式
alert(getImgInPositionedDivHtml("img.gif","img",100,50,0,0,"Test"));
</script>

說 明:其中的關鍵技巧在於通過執行一個單行(in-line)函式表示式建立一個額外的執行環境,而將該函式表示式返回的內部函式作為在外部程式碼中使用的函 數。此時,緩衝陣列被定義為函式表示式的一個區域性變數。這個函式表示式只需執行一次,而陣列也只需建立一次,就可以供依賴它的函式重複使用。
七、原型鏈
ECMAScript 為 Object 型別定義了一個內部 [[prototype]] 屬性。這個屬性不能通過指令碼直接訪問,但在屬性訪問器解析過程中,則需要用到這個內部[[prototype]] 屬性所引用的物件鏈--即原型鏈。可以通過一個公共的 prototype 屬性,來對與內部的 [[prototype]] 屬性對應的原型物件進行賦值或定義。
例1:
Js程式碼
複製程式碼 程式碼如下:

<script language="javascript">
function NumObject(formalParameter){
this.testNumber = formalParameter;
}
function StrObject(formalParameter){
this.testString = formalParameter;
}
//用 NumObject類的例項替換了所有與 StrObject類的例項相關聯的原型。
StrObject.prototype =new NumObject(6);
var objRef = new StrObject( "String_Value" );
//當某個屬性訪問器嘗試讀取由 objectRef 所引用的物件的屬性值時,整個原型鏈都會被搜尋。
//不論是在物件或物件的原型中,讀取命名屬性值的時候只返回首先找到的屬性值。而當為物件的命名屬性賦值時,如果物件自身不存在該屬性則建立相應的屬性。
alert(objRef.testString);//output "String_Value"
alert(objRef.testNumber);//output "6"
alert(objRef.toString);
//StrObject 的例項擁有一個原型鏈。該鏈中的第一個物件是在建立後被指定給 StrObject 建構函式的 prototype 屬性的 NumObject 的一個例項。NumObject 的例項也有一個原型,即與 Object.prototype 所引用的物件對應的預設的 Object 物件的原型。最後, Object.prototype 有一個值為 null 的原型,因此這條原型鏈到此結束。
objRef.testNumber = 3;//物件自身不存在該屬性則建立相應的屬性
alert(objRef.testNumber);//自身有了屬性,屬性訪問器不會再進一步搜尋原型鏈
alert(NumObject.prototype.isPrototypeOf(objRef));// output "true"
</script>

相關文章