JavaScript 中的 call与apply

February 2nd, 2010 | Author: JavaChen | Categories: JavaScript | Tags: ,

call 方法

call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。

obj1.method1.call(obj2,argument1,argument2)
如上,call的作用就是把obj1的方法放到obj2上使用,后面的argument1..这些做为参数传入.
举一个具体的例子:
function add(a,b)
{
alert(a+b);
}
function sub(a,b)
{
alert(a-b);
}
add.call(sub,3,1);

这个例子中的意思就是用 add 来替换 sub,add.call(sub,3,1) == add(3,1) ,所以运行结果为:alert(4); // 注意:js 中的函数其实是对象,函数名是对 Function 对象的引用。

关于call,最简单的解释就是:把隐藏的第一个参数显示化。因为通常一个方法x的调用,会有一个额外的隐藏参数,就是x所属的对象,如果没有所属,则为 global(如window)对象,并在函数内可以用this关键字访问之。

用call来实现继承:

function Class1()
{
this.showTxt = function(txt)
{
alert(txt);
}
}

function Class2()
{
Class1.call(this);
}

var c2 = new Class2();
c2.showTxt(“cc”);
这样 Class2 就继承Class1了,Class1.call(this) 的 意思就是使用 Class1 对象代替this对象,那么 Class2 中不就有Class1 的所有属性和方法了吗,c2 对象就能够直接调用Class1 的方法以及属性了,执行结果就是:alert(“cc”)

研究一下Ext的扩展插件机制就可以看到call方法的使用,下面是扩展Ext form的一个例子

Ext.namespace('Ext.ux.form');
Ext.ux.form.StaticTextField = Ext.extend(Ext.form.Field, {
	//设置组件默认常见的HTML元素为div
	defaultAutoCreate : {tag: "div"},
	value : '',
	onRender : function(ct, position){
        // 让Ext.ux.form.StaticTextField的父类的onRender调用本类的onRender方法,也就是覆盖父类的onRender方法
        Ext.ux.form.StaticTextField.superclass.onRender.call(this, ct, position);
        //将需要显示的文本作为子元素插入到组件中
        Ext.DomHelper.append(this.el, {
        	tag : 'div',
			style :'height:100%;width:100%;',
        	html : this.value
        });
    },
    isDirty : function() {
    	//因为是静态文本所以不存在被改变的情况
        return false;
    },
    isValid : function(){
    	//因为是静态文本所以不需要进行验证
        return true;
    }
});
Ext.reg('xstatictextfield', Ext.ux.form.StaticTextField);

让Ext.ux.form.StaticTextField的父类的onRender调用本类的onRender方法,也就是覆盖父类的onRender方法

apply方法
apply([thisObj[,argArray]])
参数
thisObj 可选项。将被用作当前对象的对象。
argArray 可选项。将被传递给该函数的参数数组。
说明
如果 argArray 不是一个有效的数组或者不是 arguments 对象,那么将导致一个 TypeError。
如果没有提供 argArray 和 thisObj 任何一个参数,那么 Global 对象将被用作 thisObj, 并且无法被传递任何参数。

两者的区别:
两者实现的功能是完全一样的,只是参数传递方式不一样,call是将各个参数以逗号(,)隔开,而apply是将所有参数组成一个数组进行传递

apply方法实例:

function foo(x, y) {
	this.x = x;    //共有变量
	var y = "Just a y"; //私有变量
}
 
function bar(foo) {
	return function() {
		var temp = {};
		foo.apply(temp, arguments);
		alert("temp.foo:" + temp.foo + "\n" + "temp.x:" + temp.x + "\n"
				+ "temp.y:" + temp.y);// 注意看这一行的执行结果
	}
}
 
var func = bar(foo);
func("In foo now", "y");

试验结果表明:apply以后,temp对象就继承了foo类的公有变量了。也就是说,这一句 foo.apply(temp, arguments);实际上就是把foo()函数当成temp对象的构造函数来调用了。一旦构造完毕,temp对象就具有了成员this.x,但是没有y成员

使用apply方法,实际上是将arguments或传入的数组内的公有属性复制到第一个参数内,即拷贝,参照上例,也就是说系统把foo类拥有的所有公有变量复制给了temp对象(这里是指Object,Function,Array和包装型对象“四类”对象)。

同样看看Ext的apply代码,就能明白JavaScript中apply的实现方式了,也说明了为什么apply要求传入的是数组和arguments。

Ext.apply = function(o, c, defaults){
    // no "this" reference for friendly out of scope calls
    if (defaults) Ext.apply(o, defaults);
    if(o && c && typeof c == 'object'){
        for(var p in c){
            o[p] = c[p];
        }
    }
    return o;
};
No comments yet.
:wink: :twisted: :roll: :oops: :mrgreen: :lol: :idea: :evil: :cry: :arrow: :?: :-| :-x :-o :-P :-D :-? :) :( :!: 8-O 8)