51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

深入了解下JavaScript与JSON之间的一些事情

其实在前端开发过程中,经常会用到JavaScript和JSON,对于二者它们之间的关系,您能说清楚吗?接下来一起了解下吧。

JSON对象包含两个方法: 用于解析 JavaScript Object Notation (JSON) 的 parse() 方法,以及将对象/值转换为 JSON字符串的 stringify() 方法。除了这两个方法, JSON这个对象本身并没有其他作用,也不能被调用或者作为构造函数调用。

描述

JavaScript Object Notation

JSON 是一种语法,用来序列化对象、数组、数值、字符串、布尔值和 null 。它基于 JavaScript 语法,但与之不同:JavaScript不是JSON,JSON也不是JavaScript。

误解:JSON并不是JavaScript 的子集?

这些年来,我们一遍又一遍地听到它:" JSON是JavaScript的子集"。你猜怎么了?他们错了。错,错,错。您会发现,在细节中,并且没有避免的方法:并非所有JSON格式的文本都是合法的JavaScript代码:


{"JSON":"ro
cks!"}

复制上面的确切代码,然后将其粘贴到Firebug或Web Inspector中的一对括号内(避免在JavaScript语法中产生歧义):

json-sucks.png


等一下 SyntaxError: Unexpected token ILLEGAL?那没有任何意义!这只是一个普通的对象文本,怎么能说是一个SyntaxError?

在适当的JSON解析器中尝试相同的代码。完全没有问题。

当然,这不只是常规对象文字。里面也有一个偷偷摸摸的小Unicode字符:就在" ro"和" cks!"之间。有一个很小的U + 2028。您的浏览器可能不会显示它,因为它是空格:LINE SEPARATOR,但它仍然存在。如果将字符替换为U + 2029(PARAGRAPH SEPARATOR),则会出现完全相同的问题。

JSON + U + 2028 =☺

根据JSON规范,您可以在任何字符串中安全地使用此字符。它不是引号,也不是反斜杠,也不是控制字符。这只是一个奇怪的Unicode空格字符:

json-string.gif


JavaScript + U + 2028 =☹

另一方面,ECMA-262(JavaScript基于的标准)对字符串的定义略有不同:根据7.8.4字符串文字,字符串可以包含任何内容,只要它不是引号,反斜杠或行终止符即可:

oubleStringCharacter ::
    SourceCharacter but not double-quote " or backslash \ or LineTerminator 
    \ EscapeSequence

SingleStringCharacter ::
    SourceCharacter but not single-quote ' or backslash \ or LineTerminator 
    \ EscapeSequence

什么是行终止符?让我们看一下!

以下字符被认为是行终止符:

  • \u000A -换行

  • \u000D -回车

  • \u2028 -分线器

  • \u2029 -段落分隔符

JavaScript中的任何字符串都不能包含文字U + 2028或U + 2029。

由于这两个不可见的Unicode字符,JSON 并不是 JavaScript的子集。

在大多数应用程序中,您不会注意到此问题。首先,行分隔符和段落分隔符没有被广泛使用。其次,任何适当的 JSON解析器在解析时都不会出现问题。

但是,当您使用JSONP处理时,就无法解决:您被迫在浏览器中使用JavaScript解析器。而且,如果您要发送别人输入的数据,那么一个很小的U + 2028或U + 2029可能会潜入并破坏您漂亮的跨域API。

解决方案

幸运的是,解决方案很简单:如果我们查看JSON规范,就会发现U + 2028或U + 2029可能出现的唯一位置是字符串。因此\u2028,\u2029只要需要发送一些JSONP,我们就可以简单地将每个U + 2028替换为(转义序列),并将U + 2029替换为。

它已经在Rack :: JSONP中修复,我鼓励所有发送JSONP的框架或库都执行相同的操作。这是大多数语言的单行修补程序,结果仍然是100%有效的JSON。

JavaScript 与 JSON 的区别

| JavaScript类型 | JSON 的不同点 | |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 对象和数组 | 属性名称必须是双引号括起来的字符串;最后一个属性后不能有逗号。 | | 数值 | 禁止出现前导零( JSON.stringify 方法自动忽略前导零,而在 JSON.parse 方法中将会抛出 SyntaxError);如果有小数点, 则后面至少跟着一位数字。 | | 字符串 | 只有有限的一些字符可能会被转义;禁止某些控制字符; Unicode 行分隔符 (U+2028)和段分隔符 (U+2029)被允许 ; 字符串必须用双引号括起来。请参考下面的示例,可以看到 JSON.parse() 能够正常解析,但将其当作JavaScript解析时会抛出 SyntaxError 错误: let code = '"\u2028\u2029"';JSON.parse(code);  // 正常eval(code);  // 错误 |

完整的JSON语法定义如下:


JSON = null
    or true or false
    or JSONNumber
    or JSONString
    or JSONObject
    or JSONArray

JSONNumber = - PositiveNumber
          or PositiveNumber
PositiveNumber = DecimalNumber
              or DecimalNumber . Digits
              or DecimalNumber . Digits ExponentPart
              or DecimalNumber ExponentPart
DecimalNumber = 0
             or OneToNine Digits
ExponentPart = e Exponent
            or E Exponent
Exponent = Digits
        or + Digits
        or - Digits
Digits = Digit
      or Digits Digit
Digit = 0 through 9
OneToNine = 1 through 9

JSONString = ""
          or " StringCharacters "
StringCharacters = StringCharacter
                or StringCharacters StringCharacter
StringCharacter = any character
                  except " or \ or U+0000 through U+001F
               or EscapeSequence
EscapeSequence = \" or \/ or \\ or \b or \f or \n or \r or \t
              or \u HexDigit HexDigit HexDigit HexDigit
HexDigit = 0 through 9
        or A through F
        or a through f

JSONObject = { }
          or { Members }
Members = JSONString : JSON
       or Members , JSONString : JSON

JSONArray = [ ]
         or [ ArrayElements ]
ArrayElements = JSON
             or ArrayElements , JSON

在JSONNumber(数字内部不允许包含空格)或JSONString(字符串内部的空格被解释为相应的字符,否则就有问题了)之外的任何位置可以有多余的空白字符。JSON只支持这些空白字符: 制表符(U+0009),回车(U+000D),换行(U+000A)以及空格(U+0020)。

方法

JSON有两种方法:JSON.parse()和JSON.stringify()

JSON.parse()

解析JSON字符串并返回对应的值,可以额外传入一个转换函数,用来将生成的值和其属性, 在返回之前进行某些修改。

JSON.stringify()

返回与指定值对应的JSON字符串,可以通过额外的参数, 控制仅包含某些属性, 或者以自定义方法来替换某些key对应的属性值。

Polyfill

JSON对象可能不被老版本的浏览器支持。可以将下面的代码放到JS脚本最开始的位置,这样就可以在没有原生支持 JSON 对象的浏览器(如IE6)中使用 JSON对象。

以下算法是对原生JSON对象的模仿:

if (!window.JSON) {
  window.JSON = {
    parse: function(sJSON) { return eval('(' + sJSON + ')'); },
    stringify: (function () {
      var toString = Object.prototype.toString;
      var isArray = Array.isArray || function (a) { return toString.call(a) === '[object Array]'; };
      var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'};
      var escFunc = function (m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1); };
      var escRE = /[\\"\u0000-\u001F\u2028\u2029]/g;
      return function stringify(value) {
        if (value == null) {
          return 'null';
        } else if (typeof value === 'number') {
          return isFinite(value) ? value.toString() : 'null';
        } else if (typeof value === 'boolean') {
          return value.toString();
        } else if (typeof value === 'object') {
          if (typeof value.toJSON === 'function') {
            return stringify(value.toJSON());
          } else if (isArray(value)) {
            var res = '[';
            for (var i = 0; i < value.length; i++)
              res += (i ? ', ' : '') + stringify(value[i]);
            return res + ']';
          } else if (toString.call(value) === '[object Object]') {
            var tmp = [];
            for (var k in value) {
              if (value.hasOwnProperty(k))
                tmp.push(stringify(k) + ': ' + stringify(value[k]));
            }
            return '{' + tmp.join(', ') + '}';
          }
        }
        return '"' + value.toString().replace(escRE, escFunc) + '"';
      };
    })()
  };
}

业界更专业, 更强大的JSON对象 polyfills 是 JSON2 和 JSON3。

赞(0)
未经允许不得转载:工具盒子 » 深入了解下JavaScript与JSON之间的一些事情