js中函数执行的两种方式:一是通过调用运算符’()’,二是通过调用call或apply来动态执行。

一、动态方法调用中指定this对象

开发中我们往往需要在对象B中调用对象A的方法,这个时候就用到了apply()和call(),它们的第一个参数就是用于指定this对象,如果为null,则表明传入默认的宿主对象。

function foo(){
	alert(this.name);
}
function MyObject(){
	this.name = 'MyObject';
}
MyObject.prototype.do = function(){
	foo.apply(this);
}

var obj = new MyObject();
//弹出'MyObject',这里的this就是obj
obj.do(); 

二、栈的可见与修改

function fun1(v1){
	var v1 = 100;
}
function fun2(name){
	fun1.apply(this,arguments);
	alert(name);
}
//传入参数未被修改,仍然弹出'myName'
fun2('myName');

这是因为,fun1.apply()被调用时,arguments被做了一次复制:值数据被复制,引用数据被创建引用。因此fun1与fun2中的arguments虽然看起来是相同的,其实是被隔离的两套数据。但是,如果把fun1改成下面这样:

function fun1(){
	//显示true
	alert(arguments.callee.caller === fun2);
}

所以外层的函数对于内部被调用的函数依然是可见的,尽管arguments在call() || apply()时是通过复制加以隔离的,但是调用栈对于被调用函数仍然可见,被调用函数仍然可以访问栈上的arguments,如:

function fun1(name){
	arguments.callee.caller.arguments[0] = 100;
	//alert(arguments.callee.caller === fun2)
}
function fun2(name){
	fun1.apply(this,arguments);
	alert(name);
}
//显示传入的参数被改为 100
fun2('myName');

在fun1中,我们通过callee与caller访问到栈上的函数的参数,并修改了fun2中的形式参数name,然而fun2并不知道形参已经被修改,因此这是极其危险的! 小盆友们都知道,类Arguments和Array通常是共享一个父类的——这是因为它们都有一个需要自维护的length属性。因此,我们也可以把Array原型中的方法apply到arguments实例上,例如:

[].slice.call(arguments,1);

但这也增加了调用栈上的风险:我们不但可以修改arguments中某些参数的值,也可修改arguments传入值的个数。例如:

function fun3(name){
	[].push.call(arguments.callee.caller.arguments,100);
}
function fun4(name){
	fun3();
  	//显示 2
	console.dir(arguments.length);
}

fun4('myName');
posted on 2015-04-03 10:01  狂流  阅读(206)  评论(0编辑  收藏  举报