JSON.parse在循环对象上没有错误

时间:2013-12-04 16:51:53

标签: javascript json firefox try-catch

我正在尝试确定某个对象是否可以进行字符串化。此检查适用于Chrome和Safari,但不适用于FF(25.0.1)。

var good = true;
var myObj = {"param1":11, "param2": "a string", "param3": $("a")}; 
//some cyclic object, specifically I have a jQuery object I got via `$("a")` 
//which returned several anchor tags.

//try to stringify, which supposedly rejects cyclic objects 
try {
    JSON.stringify(myObj);
} catch(error){
    good = false;
}
console.log(good) //returns true.

没有抛出错误......或者我没有正确捕捉它?我从来没有打过电话使用过try ... catch,所以我对它的细微差别的经验是空的。

JSON.stringify(myObj)返回对象的字符串版本,没有很多对象参数,显然无法进行字符串化。根据{{​​3}},它应该是错误的。

谢谢!

1 个答案:

答案 0 :(得分:4)

你正确地捕捉到了这个错误,但是(正如你已经确定的那样)Firefox并没有抛出错误。

这是因为Fiefox没有阻止DOM对象的JSON化,而其他浏览器则这样做:

JSON.stringify(document.getElementById("header"))

在Chrome和Safari中,此行会导致错误(因为在WebKit / Blink中,像兄弟姐妹这样的循环DOM对象直接存在于每个DOM对象上),而在Firefox中则无害地生成字符串“{}”。

这是因为Firefox的DOM对象没有任何自己的可枚举属性:

Object.keys(document.getElementById("header"))
> []

在WebKit / Blink浏览器中,此行提供了一个属性名称数组作为字符串,因为DOM对象具有自己的属性。 JSON.stringify仅捕获对象自己的属性,而不是原型属性。

奖励信息:超过您想知道的DOM

在Firefox中,DOM对象大多没有自己的属性;相反,属性访问被委托给原型链到HTMLElement.prototypeElement.prototypeNode.prototype(或元素的直接原型,如HTMLDivElement.prototypeHTMLAnchorElement.prototype

您可能想知道:如果访问DOM元素上的属性会导致原型访问,那么DOM元素如何具有不同的属性值?并非所有DOM元素都具有或多或少相同的原型链吗?

这里的技巧是原型属性没有,它们是 getter 函数。例如,当您要求firstChild的{​​{1}}时,JavaScript引擎会执行以下步骤:

  1. 在对象本身上查找HTMLDivElement属性。它不存在。
  2. 在对象的原型上查找firstChild属性。
  3. 继续原型链,直到我们在firstChild上找到firstChild
  4. Node.prototype访问者属性描述符定义,这意味着属性访问会导致执行Node.prototype.firstChild函数。
  5. 执行getter函数期间的get值是特定的DOM元素,您要求的this值/ Firefox使用firstChild值来做一些引擎盖下的内容查找DOM元素的第一个孩子。
  6. 因此,当你这样做时:

    this

    你真的在做:

    var val = document.getElementById("header").firstChild;
    

    或(不太可读):

    var elm = document.getElementById("header");
    var nodeProto = elm.__proto__.__proto__.__proto__.__proto__;
    var propDescriptor = Object.getOwnPropertyDescriptor(nodeProto, "firstChild");
    var getterFunc = propDescriptor.get;
    var val = getterFunc.call(elm);  // invoke the getter with `this` set to `elm`