使用eval导入javascript中的命名空间

using eval to import namespaces in javascript

本文关键字:命名空间 javascript eval 导入 使用      更新时间:2023-09-26

我正试图用javascript将一个命名空间的内容转储到另一个命名空间中。我唯一能想到的直接做到这一点的方法如下:

var CHESSMEN = {
  Chariot: function(color) {
    return {
      color: color,
      abbreviation: 'R',
      type: 'Chariot'
    };
  },
  Horse: function(color) {
    return {
      color: color,
      abbreviation: 'H',
      type: 'Horse'
    };
  },
  Elephant: function(color) {
    return {
      color: color,
      abbreviation: 'E',
      type: 'Elephant'
    };
  },
  Advisor: function(color) {
    return {
      color: color,
      abbreviation: 'A',
      type: 'Advisor'
    };
  },
  General: function(color) {
    return {
      color: color,
      abbreviation: 'G',
      type: 'General'
    };
  },
  Cannon: function(color) {
    return {
      color: color,
      abbreviation: 'C',
      type: 'Cannon'
    };
  },
  Soldier: function(color) {
    return {
      color: color,
      abbreviation: 'S',
      type: 'Soldier'
    };
  }
}
Object.freeze(CHESSMEN)
function Game(board) {
  for (var piece in CHESSMEN) {
    eval('var ' + piece + ' = CHESSMEN.' + piece);
  }
  if (typeof(board) === 'undefined') {
    var board = {
      a1: new Chariot('red'),
      b1: new Horse('red'),
      c1: new Elephant('red'),
      d1: new Advisor('red'),
      e1: new General('red'),
      f1: new Advisor('red'),
      g1: new Elephant('red'),
      h1: new Horse('red'),
      i1: new Chariot('red'),
      b3: new Cannon('red'),
      h3: new Cannon('red'),
      a4: new Soldier('red'),
      c4: new Soldier('red'),
      e4: new Soldier('red'),
      g4: new Soldier('red'),
      i4: new Soldier('red'),
      a10: new Chariot('black'),
      b10: new Horse('black'),
      c10: new Elephant('black'),
      d10: new Advisor('black'),
      e10: new General('black'),
      f10: new Advisor('black'),
      g10: new Elephant('black'),
      h10: new Horse('black'),
      i10: new Chariot('black'),
      b8: new Cannon('black'),
      h8: new Cannon('black'),
      a7: new Soldier('black'),
      c7: new Soldier('black'),
      e7: new Soldier('black'),
      g7: new Soldier('black'),
      i7: new Soldier('black')
    };
  }
  // et cetera
}

这里,我使用for in循环来迭代冻结对象的键,并使用eval将这些键及其相关值转储到Game构造函数的作用域中。很多人告诉我不要使用eval,但我看不到其他直接合并名称空间的方法。这不安全吗?对我来说,将棋子构造函数称为CHESSMEN对象的属性会更好吗?

正如@zerkms所指出的,您应该使用CHESSMEN.Chariot etc:来访问它们

var board = {
  a1: new CHESSMAN.Chariot('red'),
  b1: new CHESSMAN.Horse('red'),
  c1: new CHESSMAN.Elephant('red'),
  //etc.

此外,如果您要使用new,您几乎肯定不希望以现有的方式实现Chariot。你想要的模式是:

  Chariot: function(color) {
    this.color = color;
    this.abbreviation = 'R';
    this.type = 'Chariot'
  },

否则,当您调用new时,返回的将是您正在创建的对象文字,而不是您可能想要的Chariot的实例。如果您真正需要的只是返回的值,那么您可以在不使用new:的情况下编写代码

var board = {
  a1: CHESSMAN.Chariot('red'),
  b1: CHESSMAN.Horse('red'),
  c1: CHESSMAN.Elephant('red'),
  //etc.

这是可能的,但非常不鼓励,在严格模式下是不可能的。

with (CHESSMEN) {
    // Now CHESSMEN's properties are available without saying "CHESSMEN.whatever"
    console.log(Chariot === CHESSMEN.Chariot); // logs "true"
}

我重申:这是一个坏主意。很难判断任何标识符指的是什么,它可能运行得更慢,而且它不兼容前向。有关详细信息,请参阅MDN。

至于eval的安全性:如果在以后的某个时候,你的代码发生了变化,使得CHESSMEN可以具有用户定义的属性,那么攻击者可以将属性命名为foo; doHorriblyEvilThing();,从而导致你的代码做出可怕的邪恶行为。