JavaScript的类型错误:Illegal invocation
今天写一个十分简单的页面,要获取页面中某一DOM,用了如下的写法:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> new document </title>
<meta name="generator" content="editplus" />
<meta name="author" content="" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<div id="demo">demo</div>
<script type='text/javascript'>1:
2: var d = document.getElementById;3:
4: var s = d("demo").innerHTML;5:
6: alert(s);
7:
</script>
</body>
</html>
昨一看好像也没什么问题,在IE6~8下运行也没有任何的问题,可是在其它浏览器下就报错了,报了这样一个错:
Uncaught TypeError: Illegal invocation 未捕获的类型错误:非法调用
当时没太注意,当时没仔细去弄清楚是怎么回事,然后在微博中发一条消息,稍作了一下记录。等吃完饭回来看到有一条回复,是taibo转播的,说明了原因:call/apply 上下文非法时,会抛出此异常,IE9也遵守此规范。后面是可以避免报此错误的一个example,我一看没太注意,之后他又发了一个ref
Calling a Method with a Function Pointer without ".call" or ".bind"
看完以后才真正缓过神来,上面的在IE9和非IE(例如Chrome)浏览器下的写法如同:
<script type='text/javascript'>
var d = document.getElementById;
var s = d.call(window, "demo").innerHTML
alert(s);
</script>
这样写显然会导致调用错误,因为id为demo的DOM元素应该是在document对象中,而不是在其它对象中。改为调用document就可以得到想要的结果:
<script type='text/javascript'>
var d = document.getElementById;
var s = d.call(document, "demo").innerHTML
alert(s);
</script>
但是在我的IE6下却报错了,然后我开始找原因了…
我试图去循环迭代出d中的所有属性
var d = document.getElementById;
for (var p in d){}
但得到的情况很糟糕,不知道是我的系统问题,还是确实存在这个问题,运行后直接出现了“"0x7e2cf10c" 指令引用的 "0x00000000" 内存。该内存不能为 "read"。 ”
之后我尝试着使用typeof,想看看它到底是个什么东东,按我的预期,它应该是一个函数,只有函数才能被调用“()”
var d = document.getElementById;
alert(typeof d);
在我的IE6中得到的结果让我很失望“object”,好在Chrome下得到的是 “function”。也许你觉得上面的typeof可能不准,那使用下面的方法应该是没什么异议了吧
alert(Object.prototype.toString.call(d)); 结果依旧表明:ie下为object而Chrome则为function
最为神奇的是在我的ie6下,d是没有toString方法的,我想它应该是一个很“干净”的对象吧(没有toString、valueOf方法),而Chrome是符合预期的
更重要的是它不Function的实例,更不是Object的实例,而在Chrome下也符合预期。
var d = document.getElementById;
alert(d instanceof Function);
alert(d instanceof Object);
它到底如何实现的呢?如果是Global对象下的方法,如parseInt、isNaN等,它应该可以被删除,很明显的它属于docuemnt对象下的方法而不是Global下的方法。至于它到底怎么实现的,我暂时还不太清楚,只是觉得一个对象 object(要么它的typeof有问题),也可以被调用,这个就很神奇了。
()在javascript虽然有多义性,但无非下面几种:
1、函数声明时的函数列表 例:function fnName(arg1, arg2) {};
2、和一些语句一起使用用来限定的作用,例:for()、while()等:
3、和new一起使用,用来传递参数--在不传递参数的情况下()可以省略,但并不建议 例:var obj1 = new FunName();
4、计算一个表达式,提升运算的优先级 例:var a = 5 * (3 + 4)
5、正则表达中用作捕获的分组之用
6、函数调用符
上面的问题让我困惑的是,一个object如何被调用,这里的()肯定是函数调用符,那么document.getElementById应该是一个函数才对,而实际得到的结果却不是(还是这个测试类型得到的结果有问题?!)
需要说明一下情况的是,最初写的页面不是在宿舍写的,用的浏览器是IE8,而我回宿舍之后本本上装的是IE6(为了测试之用,虽然我很痛恨ie6),其它浏览器暂时没全部测试过。