【问题标题】:Is using Eval a good idea?使用 Eval 是个好主意吗?
【发布时间】:2018-01-22 12:00:11
【问题描述】:

我会遇到一种情况,我必须从 array 动态获取 object 值,而 object keys 来自 api。我通过使用eval 来采用这种方法。

class App extends React.Component {

  constructor() {
    super();
    this.state = {
      title: 'Developers',
      descp: 'They are just amazing! JK',
      names: ['title', 'descp']     
    }
  }

  getVal(objKey) {
    let { title, descp } = this.state;
    return eval(objKey);
  }

  render() {
  let {names} = this.state;
    return (
      <div>
        <h2>{this.getVal(names[0])}</h2>
        <div>{this.getVal(names[1])}</div>
      </div>
    )
  }

}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

上面的代码可以正常工作。

我后来发现的另一种方法:

render() {
  let {names} = this.state;
    return (
      <div>
        <h2>{this.state[names[0]]}</h2>
        <div>{this.state[names[1]]}</div>
      </div>
    )
  }

输出相同的结果。但我的问题是,如果我在以下情况下使用eval,这是一个好方法吗?

【问题讨论】:

  • 除非绝对必要,否则我会避免使用eval。括号符号在这里可以正常工作。
  • 你认为eval 是什么让你接触到了它?在这种情况下,它既不合适也没有帮助。老实说,我很好奇为什么?
  • 你后面的做法是正确的。我个人会避免使用eval(),但是当你有一个绝对不需要它并返回正确结果的解决方案时,永远不要使用它。
  • @Chris 这与 React 或 JSX 无关。
  • @Chris 就像 Aluan 所说,OP 的问题与 React 或 JSX 无关。这是关于通过字符串访问对象的属性。围绕它的所有 React 完全无关紧要。

标签: javascript reactjs ecmascript-6


【解决方案1】:

Eval 应该避免,因为它可能非常危险。您可以通过括号符号访问属性安全地替换您的 eval 调用。

getVal(objKey) {
    if(this.state.hasOwnProperty(objKey)){
        return this.state[objKey];
    } else {
        // handle missing property
    }
}

【讨论】:

  • 虽然在技术上是正确的,但这比问题底部的代码好多少...this.state[names[0]]?
  • @Archer 这允许您处理缺少属性而不是返回未定义的情况
  • @Archer 无论如何,我不明白为什么 op 想要保持属性名称处于状态。如果它们是静态的,那么这是存储它们的错误位置。
  • 然后询问 OP。
【解决方案2】:

Eval 通常被避免使用,因为它允许客户端在您的代码中插入和评估他们自己的表达式。

话虽如此,JavaScript 作为一种客户端语言已经允许用户完全访问,因此没有不使用它的充分理由。

只要用户只能弄乱他们自己的会话,我就不会担心。无论如何,安全性都应该在服务器端处理:当心但不要简单地忽略Eval

编辑 1 - 为 Eval 辩护

cmets 指出了一些问题,主要是性能/优化影响,this answer 对此进行了深入解释。基本上,因为它无论如何都是即时编译,所以在性能方面你并没有真正损失那么多。

作为一个用例的例子,这里是我整理的一个模板例子,它也使用了有争议的with 声明:

var Template = /** @class */ (function () {
    function Template(html) {
        this.html = html;
    }
    Template.prototype.apply = function (params, returnDOMObject) {
        if (params === void 0) { params = {}; }
        if (returnDOMObject === void 0) { returnDOMObject = false; }
        with (params) {
            var html = eval('`' + this.html.replace(Template.regexes.encapsulated, function (n) {
                return n
                    .replace(Template.regexes.start, '${')
                    .replace(Template.regexes.end, '}');
            }) + '`');
        }
        if (returnDOMObject) {
            return document.createRange().createContextualFragment(html);
        }
        return html;
    };
    Template.regexes = {
        encapsulated: new RegExp('{{.*?}}', 'igm'),
        start: new RegExp('\{{2,}', 'igm'),
        end: new RegExp('\}{2,}', 'igm')
    };
    return Template;
}());
//TEST
var persons = [
    { name: "Peter", age: 25 },
    { name: "Ole", age: 55 },
];
var templates = [];
var container = document.body.appendChild(document.createElement("div"));
var leftBox = container.appendChild(document.createElement("div"));
var rightBox = container.appendChild(document.createElement("div"));
leftBox.style.width = rightBox.style.width = "50%";
leftBox.style.height = rightBox.style.height = "500px";
leftBox.style.cssFloat = rightBox.style.cssFloat = "left";
var leftList = leftBox.appendChild(document.createElement("select"));
leftBox.appendChild(document.createElement("br"));
var leftText = leftBox.appendChild(document.createElement("textarea"));
leftText.style.width = "100%";
leftText.style.resize = "vertical";
var rightOutput = rightBox.appendChild(document.createElement("div"));
function updateLists() {
    leftList.innerHTML = '';
    for (var i = 0; i < templates.length; i++) {
        var template = templates[i];
        var option = document.createElement("option");
        option.value = option.innerHTML = template.name;
        leftList.appendChild(option);
    }
}
var h1Template = new Template("<h1>{{name}}</h1>");
var h2Template = new Template("<h2>{{age}} is no age!</h2>");
var pTemplate = new Template("<p>{{name}} may be {{age}}, but is still going strong!</p>\n<p>(When he's {{age*2}} though...)</p>");
var personTemplate = new Template("<p>\n{{ h1Template.apply(params) }}\n{{ h2Template.apply(params) }}\n{{ pTemplate.apply(params) }}\n</p>");
templates.push({ name: "personTemplate", template: personTemplate });
templates.push({ name: "h1Template", template: h1Template });
templates.push({ name: "h2Template", template: h2Template });
templates.push({ name: "pTemplate", template: pTemplate });
function updateOutput() {
    rightOutput.innerHTML = '';
    for (var pi = 0; pi < persons.length; pi++) {
        var person = persons[pi];
        rightOutput.appendChild(personTemplate.apply(person, true));
    }
}
function leftTextChange() {
    templates.find(function (val) { return val.name === leftList.value; }).template.html = leftText.value;
    updateOutput();
}
function leftListChange() {
    leftText.value = templates.find(function (val) { return val.name === leftList.value; }).template.html;
}
updateLists();
leftList.onchange = leftList.onkeyup = leftListChange;
leftText.onchange = leftText.onkeyup = leftTextChange;
leftListChange();
updateOutput();

在这里,用户输入的文本被实时解释,而用户正在观看。没有安全问题,因为它都是客户端。

【讨论】:

  • 确实,除了安全原因之外,还有很多工程原因应该避免使用eval。如果您解释了为什么在 OP 中使用 eval 是多余的,并且一个示例显示了它实际有用的示例,则此答案可能有效。不使用它的一个明显原因:它使代码更复杂,并阻止工具执行甚至最小的验证。
猜你喜欢
  • 2013-03-09
  • 1970-01-01
  • 2010-11-05
  • 2012-04-27
  • 1970-01-01
  • 2020-03-29
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
相关资源
最近更新 更多