js中call與apply用法

call和apply,它們的作用都是將函數綁定到另外一個對象上去運行

兩者的格式和參數定義:

call( thisArg [,arg1,arg2,… ] );       // 參數列表,arg1,arg2,...

apply(thisArg [,argArray] );                 // 參數數組,argArray

上面兩個函數內部的this指針,都會被賦值爲thisArg,這可實現將函數作爲另外一個對象的方法運行的目的

首先我們看call()函數的用法;

call(obj,arg1,arg2,arg3);
apply(obj,[arg1,agr2,agr3]);

我們看最普通的函數;

function main(){
    var str = "hello";
    console.log(str);
}
main();  //hello

如果使用call會怎樣?

function main(){
    var str = "hello";
    console.log(str);
}
main.call();  //hello


效果是完全一樣的,我們再來試試帶參數的;

function main(arg1,arg2){
    var str = arg1+arg2;
    console.log(str);
}
main.call(null,"hello"," world!");  //hello world!
main.apply(null,["hello"," world!"]);  //hello world!


使用第一個參數的情況

var person = {};
person.name = "xiao ming";
function say(arg1,arg2){
    console.log(arg1+"_"+arg2+"_"+this.name);
}
say.call(person,"nihao","wo jiao")  //nihao_wo jiao_xiao ming
say.apply(person,["nihao","wo jiao"])  //nihao_wo jiao_xiao ming

可見this被綁架了!


一、call 的簡單用法

首先,我們先看個簡單的例子(call):

<!doctype html>

<html>
	<head>
		<title> call-apply </title>
	</head>

	<body>
		<input type="text" id="idTxt" value="input text">
		
		<script type="text/javascript">
			var value = "global var";
			
			function mFunc()
			{
				this.value = "member var";
			}
			
			function gFunc()
			{
				alert(this.value);
			}		
													
			window.gFunc();									// show gFunc, global var
			gFunc.call(window);								// show gFunc, global var
			gFunc.call(new mFunc());						// show mFunc, member var
			gFunc.call(document.getElementById('idTxt'));	// show element, input text
		</script>
		
		<script language="javascript">
			var func = new function()
			{
				this.a = "func";
			}
			
			var func2 = function(x)
			{
				var a = "func2";
				alert(this.a);				
				alert(x);
			}
			
			func2.call(func, "func2");						// show func and func2
		</script>
	</body>
</html>

然後,運行結果如下:

global var
global var
member var
input text
func
func2

測試環境:Google Chrome 10.0.648.45

最後,分析結果

1、全局對象window調用函數gFunc,this指向window對象,因此this.value爲global var

2、函數gFunc調用call方法,this默認指向第一個參數window對象,因此this.value也爲global var

3、函數gFunc調用call方法,this默認指向第一個參數new mFunc(),即mFunc的對象,因此this.value爲mFunc的成員變量member var

4、函數gFunc調用call方法,this默認指向第一個參數input text控件,即id=‘idTxt’的控件,因此this.value爲input控件的value值input text

5、函數func2調用call方法,this默認指向第一個參數func函數對象,因此this.value爲this.a,即func

6、函數func2調用call方法,第二個參數屬於函數對象func2的參數,因此alert(x)爲第二個參數func2


二、call 繼承用法與改進

js使用call模擬繼承

測試代碼:

<!doctype html>

<html>
	<head>
		<title> call - apply for inherit </title>
	</head>
	
	<body>
		<script type="text/javascript">
			function baseA()		// base Class A
			{
				this.member = "baseA member";
				this.showSelfA = function()
				{
					window.alert(this.member);
				}
			}
			
			function baseB()		// base Class B
			{
				this.member = "baseB member";
				this.showSelfB = function()
				{
					window.alert(this.member);
				}
			}
			
			function extendAB()		// Inherit Class from A and B
			{
				baseA.call(this);	// call for A
				baseB.call(this);	// call for B
			}
			
			window.onload = function()
			{
				var extend = new extendAB();	
				extend.showSelfA();		// show A
				extend.showSelfB();		// show B
			}
		</script>
	</body>
</html>

運行結果如下:

baseB member

baseB member

測試環境:Google Chrome 10.0.648.45

結果分析:

預期的結果,應該是輸出 baseA member 和 baseB member,但實際輸出卻是 baseB member 和 baseB member

(已在IE9、8、6,Maxthon、Chrome、FF、Opera、Safari、360等瀏覽器測試過,結果都是後者:baseB member)

至此,機器是不會錯的,這就需要我們深入分析

我們可能會很容易想到是this引起的,this兩次都指向了baseB對象,但是推測真是這樣嗎?

爲了探究實質,我們藉助chrome瀏覽器的調試工具,下斷點,進行調試,結果發現:


當調用extend.showSelfA();時,此時的this指向extendAB(並不是我們推測的兩次都指向baseB對象

真實原因是extendAB對象的成員變量member在被baseB.call(this);實例化時,被baseB的成員member覆蓋了,即extendAB的成員member由baseA member賦值成了baseB member

當然,我們也可以對上面baseA代碼稍作修改,來驗證我們調試分析的正確性:

function baseA() // base Class A
{
this.memberA = "baseA member";   // member改成memberA,以區分baseB中的member
this.showSelfA = function()
{
window.alert(this.memberA);    // 顯示memberA
}
}

再次運行chrome等瀏覽器,結果如下:

baseA  member

baseB member

結果和我們的預期相同,同時chrome調試信息也驗證了我們的正確性:




繼承改進(prototype)

以上模擬繼承方法,仔細分析不是最好的。

因爲每次在函數(類)中定義了成員方法,都會導致實例有副本,因此可以藉助prototype原型,進行改進

改進舉例如下:

<!doctype html>

<html>
	<head>
		<title> call - apply for prototype </title>
	</head>
	
	<body>
		<script type="text/javascript">
			var Class = {
				create: function()				// create Function
				{
					return function()
					{
						this.initialize.apply(this, arguments);
					}
				}
			};
			
			var Person = Class.create();		// Create Class Person
			Person.prototype = {				// prototype initialize
				initialize: function(obj1, obj2)
				{
					this.obj1 = obj1;
					this.obj2 = obj2;
				},
				showSelf: function()
				{
					alert("obj: " + this.obj1 + " and " + this.obj2);
				}
			}
			
			// instance Class
			var person = new Person("man", "women");	// two params
			person.showSelf();							// show person
		</script>
	</body>
</html>

運行結果如下:
obj: man and women
發佈了9 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章