`
zhouyrt
  • 浏览: 1162157 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

各JS引擎对Function.prototype.apply第二个参数的实现差异

阅读更多

每个function都有个apply方法,该方法有两个作用:
1,改变function的执行上下文(第一个参数非null,undefined等)
2,执行/调用该function (见:http://snandy.iteye.com/blog/420000

apply方法第二个参数一般认为被实现为数组,见经典的《javascript权威指南-第五版》中章节8.6.4,145页一句:

《javascript权威指南》 写道
"apply()方法和call()方法相似,只不过要传递给函数的参数是由数组指定的:"

 

和641页关于Function.apply中提到args为一个数组。

权威指南中这个说法不太严谨,甚至自相矛盾。因为apply的第二个参数可以是arguments,而arguments并非数组。关于arguments非数组在权威指南章节8.2.2中提到。下面分别测试下:

function fun1(a){alert(a)}

fun1.apply(null,[1,2,3]);//第二个参数传数组

function fun2(){
	fun1.apply(null,arguments);//这里使用的是arguments,而arguments并非数组
}
fun2('test');
 

 

所有浏览器中测试均没有报错,两次弹出信息框,第一次传给apply的是数组,第二次的则是arguments对象,而arguments并非数组。它是一个伪数组(Array like)。

另外,arguments并非Arguments的实例,或者说arguments的构造器不是Arguments,这点让人有点疑惑。是什么自己可以测试一下。

 

做一个错误的测试,传给apply的第二个参数是一个普通对象。实际上,如果第二个参数不是数组或arguments,部分浏览器相关开发工具会报错。如:

function fun(){
	alert(arguments[0]);
}

fun.apply(null,{name:'john'});

 

IE8 : 缺少 Array 或 arguments 对象
Firefox3.6.3 : second argument to Function.prototype.apply must be an array
Chrome5.0.375.29 dev :  Function.prototype.apply: Arguments list has wrong type
Safari4.0.3 :  Result of expression '.apply' [[object Object]] is not a valid argument for 'Function.prototype.apply'.

 

其中,IE明确的提示要求apply的第二个参数是Array或arguments,Firefox/Chrome/Safari虽然没有明确,实际上它们都允许第二个参数可以是arguments。到这里,似乎所有浏览器都达成了默契---apply的第二个参数实现为数组,arguments。

 

arguments是一个伪数组(Array like),从而很自然的想到其它的伪数组(HTMLCollection,NodeList等 )如是否也可以作为apply的参数呢?

<!DOCTYPE HTML>
<HTML>
 <HEAD>
  <script>
	window.onload = function(){		
		var divs = document.getElementsByTagName('div');
		var chis = document.body.children;

		function fun(){
			alert(arguments[0]);
		}

		fun.apply(null,divs);
		//fun.apply(null,chis);
	}
  </script>
 </HEAD>
 <BODY>
	<p>First Child</p>
	<div id="d1"><div>
	<div id="d2"><div>
 </BODY>
</HTML>
 

 

以上分别测试divs和chis,IE/Firefox/Chrome/Safari均报错,失败了。令人惊喜的是,Opera竟然通过了。即Opera中apply的第二个参数不仅允许数组,arguments,还可以传这些伪数组。

 

IE/Firefox/Chrome/Safari中允许传数组和arguments,那能否伪造一个伪数组欺骗apply呢?如下:

var obj = {0:'zero',1:'one',length:2}
function fun(){
	alert(arguments[0]);
}
fun.apply(null,obj);

 

除Opera,其它浏览器还是不能通过。

 

继续,这次我们把对象的constructor指定为Array

var obj = {0:'zero',1:'one',length:2,constructor:Array}
function fun(){
	alert(arguments[0]);
}
fun.apply(null,obj);

 

即使obj看起来已经很象一个数组了,但除了Opera正常运行,仍然欺骗不了IE/Firefox/Chrome/Safari,看来只有Opera与众不同。

 

 

 

分享到:
评论
1 楼 ciznx 2012-03-08  
好文章,分析得细致入理。

相关推荐

    完美解决IE低版本不支持call与apply的问题

    `apply` 方法与 `call` 类似,也用于改变 `this` 的值,但它的第二个参数是一个数组或类数组对象,包含传递给函数的所有参数。基本语法如下: ```javascript func.apply(thisArg, [arg1, arg2, ..., argN]); ``` ...

    js代码-test call and apply

    `apply`接收两个参数:第一个是`this`的值,第二个是参数数组或者类数组对象。基本语法如下: ```javascript func.apply(thisArg, [argsArray]); ``` - `thisArg`:必需,将在函数内部作为`this`值的对象。 - `...

    web前端工程师__面试大全

    - **apply()**方法的第一个参数同样是指定的`this`对象,但第二个参数是一个数组或类数组对象,其中包含要传递给函数的所有参数。 **2. 示例对比** 假设我们有一个对象`obj`和一个函数`func`,函数`func`接受两个...

    一些手写JavaScript常用的函数汇总

    - **apply函数实现**:与`call`类似,`apply`方法接受两个参数,第一个参数是`this`的值,第二个参数是一个数组,这个数组包含了函数需要的参数。这里提供了一个简单的`myApply`实现。 ```javascript Function....

    去哪儿网2014年校园招聘技术笔试题

    **解析**: 此题考查的是如何找出两个字符串之间的差异,即找到第一个字符串不匹配第二个字符串的起始位置,并返回该位置之后的字符串。这个问题可以通过遍历两个字符串并逐字符比较来解决。 **示例代码** ...

    前端面试题整理

    - `call`:用于改变函数内部的`this`指向,第一个参数是新的`this`值,其余参数依次传递给函数。 - `apply`:同样用于改变函数内部的`this`指向,但其参数以数组的形式传递。 #### 五、JavaScript代码解析 **示例...

    对JavaScript的eval&#40;&#41;中使用函数的进一步讨论

    ")`和`eval("(function X(){})")`两者都会返回`undefined`,并且第二个还会声明一个变量`X`,因为在JScript中,它认为这是一个具名函数声明语句。 在SpiderMonkey中,由于它支持函数表达式,上述两行代码都被解释为...

    JavaScript中具名函数的多种调用方式总结

    第二种调用方式是使用call方法。在ECMAScript 3之后,Function对象增加了一个call方法,它允许调用一个函数,并且可以明确指定函数体内的this值,以及为函数提供参数。在没有参数的情况下,可以传入null或者...

Global site tag (gtag.js) - Google Analytics