小编典典

JSON.parse不会在循环对象上出错

json

我正在尝试确定对象是否可以字符串化。此检查在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)返回对象的字符串版本,没有许多对象参数,这些参数显然无法进行字符串化。根据MDN,它应该出错。

谢谢!


阅读 313

收藏
2020-07-27

共1个答案

小编典典

您正在正确地捕获错误,但是(正如您所确定的)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.prototype或者Node.prototype(或元素的直接原型,像HTMLDivElement.prototypeHTMLAnchorElement.prototype)。

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

这里的技巧是原型属性没有 ,它们是 getter
函数。例如,当您要求输入firstChildHTMLDivElement,JavaScript引擎执行以下步骤:

  1. firstChild在对象本身上查找属性。不在那里
  2. firstChild在对象的原型上查找属性。
  3. 继续检查原型链,直到我们找到firstChildNode.prototype
  4. Node.prototype.firstChild访问器属性描述符 定义,这意味着属性访问会导致get函数的执行。
  5. this在执行getter函数期间,该值是firstChild您要求其值的特定DOM元素/ Firefox使用该this值对DOM元素的第一个子元素进行了一些幕后查找。

因此,当您这样做时:

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`

或(不太可读):

var val = Object.getOwnPropertyDescriptor(document.getElementById("header").__proto__.__proto__.__proto__.__proto__, "firstChild").get.call(document.getElementById("header"))
2020-07-27