JavaScript报错TypeError: Illegal invocation,但未调用任何函数
为什么用Object.create(DOM元素)创建的对象访问属性会报错?
我完全懂你的困惑——明明只是访问个属性,却抛出了和函数调用相关的错误,而且你根本没调用任何函数对吧?这事儿的根源其实和DOM元素的特殊性,以及Object.create()的工作逻辑有关。
问题出在哪?
- DOM元素可不是普通的JavaScript对象,它们属于宿主对象——是浏览器提供的特殊对象,内部属性的实现逻辑和原生JS对象完全不一样。
- 当你用
Object.create(a)时,是把DOM元素a设成了新对象b的原型。但DOM元素的很多属性(比如baseURI)并不是像普通对象那样直接存储的值,而是通过getter函数来返回结果的。 - 这些getter函数在设计的时候,默认是绑定在DOM元素实例本身上执行的。当你访问
b.baseURI时,JS引擎会顺着原型链找到a上的getter,然后在b的上下文(也就是this指向b)里执行这个getter。但b只是个普通JS对象,不是合法的DOM元素,浏览器就会抛出错误——看起来像是函数调用的问题,但本质是getter执行时的上下文不对。
怎么解决?
根据你的需求,有几种不同的处理方式:
- 如果只是想把DOM元素的属性复制到一个普通对象上,可以用
Object.assign:var a = document.getElementById("something"); var b = Object.assign({}, a); console.log(b.baseURI); // 这样就能正常获取属性值了 - 如果需要复制包括不可枚举在内的所有属性,可以用
Object.getOwnPropertyDescriptors:var a = document.getElementById("something"); var b = Object.create(null); Object.defineProperties(b, Object.getOwnPropertyDescriptors(a)); console.log(b.baseURI); - 如果你的目标是创建一个DOM元素的副本(而不是普通对象),那应该用DOM自带的
cloneNode方法:var a = document.getElementById("something"); var b = a.cloneNode(true); // true表示深复制,会复制元素的子节点 console.log(b.baseURI);
小提醒
DOM对象的原型链和行为和普通JS对象差异很大,别用操作普通对象的思路去折腾它们。用浏览器提供的DOM API来处理DOM元素,才是最稳妥的做法。
内容的提问来源于stack exchange,提问作者D. Pardal
相关产品推荐
相关产品推荐

