JSON.stringify() 、JSON. parse()、eval()详解

1,397 阅读5分钟

JSON.stringify()

  • 语法:JSON.stringify(value, replacer, space)

  • value:是必选字段。就是你输入的对象,比如数组,类等

  • replacer:可选。它又分为2种方式,一种是数组,第二种是方法。

    1、如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化

    2、如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理 

  • space:可选,指定缩进用的空白字符串,用于美化输出

    1)如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格

    2)如果仅仅是字符串,就在每行输出值的时候把这些字符串附加上去。当然,最大长度也是10个字符

    3)如果是一些转义字符,比如“\t”,表示回车,那么它每行一个回车

    4)如果该参数没有提供(或者为 null),将没有空格。

image.png

JSON. parse()

  • 语法:JSON.parse(text, reviver)

  • text:必需, 一个有效的 JSON 字符串

  • Reviver:可选,转换结果的函数, 将为对象的每个成员调用此函数

    1、当遍历到最顶层的值(解析值)时,传入 reviver 函数的参数会是空字符串 ""

    2、函数的遍历顺序依照:从最内层开始,按照层级顺序,依次向外遍历

  • 注:不允许用逗号作为结尾

image.png

eval()

  • 语法:eval(string)

  • string:一个表示 JavaScript 表达式、语句或一系列语句的字符串。表达式可以包含变量与已存在对象的属性。

  • 返回值:返回字符串中代码的返回值。如果返回值为空,则返回 undefined。

  • 转JSON注意点:使用eval来解析JSON格式字符串的时候,会将{}解析为代码块

    1、在JSON格式的字符串前面拼接上 "var o =";

    2、把JSON格式的字符串使用()括起来,就不会将{}解析为代码块,而是表达式。

1、非严格模式下直接调用 eval() 时,里面使用 var 声明的变量和使用 function 声明的函数会修改当前词法作用域,里面使用 let 和 const 声明的变量不会修改当前词法作用域,但是会在当前创建新的词法作用域。

    /**
     * 使用 var 声明的变量和 function 声明的函数修改了当前词法作用域。
     * 在 inner 内部创建的变量 a 和函数 fnA 覆盖了外部的变量 a 和函数 fnA。
     * 变量 a 和函数 fnA 在 inner 内没有被提升。
    */
    (function () {
      const a = 0;

      function fnA() {
        return 10;
      }

      (function inner() {
        console.log(a); // 0
        console.log(fnA()); // 10
        eval('var a = 1; function fnA () { return 11; };');
        console.log(a); // 1
        console.log(fnA()); // 11
      }());

    }());
    
    //  使用 const 和 let 声明的变量没有修改当前词法作用域,在当前创建了新的词法作用域。
    //  在 eval() 外部无法访问变量 a 和 b。
    
    (function () {
      eval('const a = 2; let b = 3;');
      console.log(a); // ReferenceError
      console.log(b); // ReferenceError
    }());

2、非严格模式下间接引用 eval() 时,会直接运行在全局环境中,里面使用 var 声明的变量和使用 function 声明的函数会修改全局词法作用域,里面使用 let 和 const 声明的变量不会修改全局词法作用域,但是会在全局环境创建新的词法作用域。

var g = 0;
(function () {
  const g = 1, ev = eval;

  function fnG() {
    return 10;
  }

  ev('console.log(g)');  // 0
  ev('var g = 2; function fnG () { return 11; };');

  console.log(g);  // 1
  console.log(fnG()); // 10
}());

console.log(g); // 2
console.log(fnG()); // 11

3、严格模式下直接调用的 eval() 时,会在当前创建一个新的独立的词法作用域。

(function () {
  const a = 0;

  function fnA() {
    return 10;
  }

  (function inner() {
    'use strict';
    console.log(a); // 0
    console.log(fnA()); // 10
    eval('var a = 1; function fnA () { return 11; };');
    console.log(a); // 0
    console.log(fnA()); // 10
  }());

}());

4、严格模式下间接引用的 eval() 时(只有在 eval() 内的字符串里面开启严格模式时,字符串才会以严格模式执行),会在全局环境创建一个新的独立的词法作用域。

var m = 0;

(function () {
  const m = 1, ev = eval;

  function fnM() {
    return 10;
  }

  ev('\'use strict\'; console.log(m)'); // 0
  ev('\'use strict\'; var m = 2; function fnM () { return 11; };');

  console.log(m); // 1
  console.log(fnM()); // 10
}());

console.log(m); // 0
console.log(fnM()); // ReferenceError

5、使用 window.eval() 等同于间接引用 eval() 。

// 非严格模式下使用
var x = 0;

(function () {
  const x = 1;

  function fnX() {
    return 10;
  }

  window.eval('console.log(x)');  0
  window.eval('var x = 2; function fnX () { return 11; };'); 

  console.log(x); // 1
  console.log(fnX());  // 10
}());

console.log(x);  // 2
console.log(fnX()); // 11



// 严格模式下使用

var y = 0;

(function () {
  const y = 1;

  function fnY() {
    return 10;
  }

  window.eval('\'use strict\'; console.log(y)'); // 0
  window.eval('\'use strict\'; var y = 2; function fnY () { return 11; };');

  console.log(y); // 1
  console.log(fnY());  // 10
}());

console.log(y); // 0
console.log(fnY()); // ReferenceError

6、 eval() 中执行的代码只能调用 JS 解释器(Interpreter)来解释执行,无法被即时编译器(JIT Compiler)优化, eval() 中的执行的代码可能会导致 JS 引擎在已经生成的机器代码中进行变量查找和赋值,带来性能问题。

7、 eval() 使用不当可能会导致里面执行的字符串容易遭受恶意修改,带来安全问题(比如 XSS 攻击)。

8、使用 eval() 会干扰代码压缩工具的行为。代码压缩工具一般会将局部变量名重命名为更短的变量名(如 a 和 b 等),以便减小代码体积。当使用了 eval() 时,由于外部的局部变量可能会被 eval() 访问到,代码压缩工具便不会对可能会被 eval() 访问到的局部变量名进行压缩,会降低代码压缩率。

  • 注:最好永远不要使用eval函数,它使用与调用者相同的权限执行代码。

  • 如果你用 eval() 运行的字符串代码被恶意方修改,最终可能会在您的网页/扩展程序的权限下,在用户计算机上运行恶意代码。

  • 更重要的是,第三方代码可以看到某一个 eval() 被调用时的作用域,这也有可能导致一些不同方式的攻击。相似的 Function 就不容易被攻击。