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 varglobal 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