迷你javascript电子表格系统中“ with”的替代

时间:2019-05-20 15:49:52

标签: javascript

我刚刚遇到了这个优雅的javascript电子表格代码,我之前从未见过:http://jsfiddle.net/ondras/hYfN3/

它使用名为getter对象的单元格引用作为DATA对象的属性,并使用“ with”对单元格值进行范围评估。


//elm.id is the cell reference, DATA is an object whose properties are these getter wrappers
Object.defineProperty(DATA, elm.id, {get:getter});

魔术发生在吸气剂中

    //My comments but jsfiddle code from Ondřej Žára's fiddle 
    //Cell value getter function..  
    var getter = function() {
        var value = localStorage[elm.id] || ""; //Direct cell contents
        if (value.charAt(0) == "=") { //Got a formula, work it out
            //strip the '=' and evaluate recursively with this getter func
            with (DATA) return eval(value.substring(1));
        } else { // Else just send back the cell contents
            return isNaN(parseFloat(value)) ? value : parseFloat(value); 
        }
    };

这是一件很美的事情,但是考虑到“ with”即将消失,我想知道是否有一种方法可以在不扩大DATA对象范围的情况下简洁地替换用法?

2 个答案:

答案 0 :(得分:4)

我会避免使用with构造函数evalFunction

我过去做过类似的事情:

function compile(lexicalScope) {
  const params = Object.keys(lexicalScope).join(',');
  const values = Object.values(lexicalScope);

  return function (expr) {
    const compiledFn = new Function(params, '"use strict"; return ' + expr);
    return compiledFn.apply(null, values);
  };
}

const variables = {A1: 100, B1: 200};

const evaluator = compile(variables);

console.log(evaluator("A1 + B1"));    // 300
console.log(evaluator("B1 * 100")); // 20000

基本上,我们传递一个对象,该对象将充当函数的词法作用域,我们提取属性名称并将其传递给Function构造函数的第一个参数。

然后在构造函数的主体中,我想首先将函数定义为严格,因为默认情况下,由Function构造函数创建的函数以草率模式运行。

最后,我们只返回表达式。然后,我们使用apply执行动态功能。

编辑:我能够看一下电子表格,并对我的方法进行了一些修改,以使其能够与该实现一起使用。

首先,DATA对象的所有getter都是不可枚举的,这意味着Object.keys不返回任何属性,而是解析表达式中可能出现的标识符,而不是使用所有可用的属性,并将它们用作动态函数的可能参数:

function compile(expr) {
  // possible parameters for dynamic function
  const identifiers = [...new Set(expr.match(/[A-Z]+\d/g))]
  const compiledFn = new Function(identifiers, '"use strict"; return ' + expr)
  return function(context) {
    // extract values
    const values = identifiers.map(id => context[id])
    return compiledFn(...values)
  }
}

这样,动态函数将只接收表达式中实际使用的参数。

它如何工作?

compile函数实际上是函数的工厂,当我们使用表达式(例如compile('A1+B2*C3'))调用它时,它将使用Function构造函数和格式如下:

function anonymous(A1,B2,C3) {
  "use strict";
  return A1+B2*C3
} 

此函数将存储在compile函数的关闭范围内。返回另一个函数,该函数将对象作为参数接收,并在其中存储属性,具体取决于表达式中使用的标识符,以提取它们并将其应用于我们最初创建的动态函数。

您可以看到一个有效的示例here

如果我们记忆 compile函数,则可以进一步优化。

如果您有兴趣,可以阅读this article,了解使用evalnew Function之间的区别。

答案 1 :(得分:3)

假设您的单元格标识符的格式为<letter(s)><digit(s)>,例如B6AZ19,则可以使用以下替换用DATA.作为前缀:

return eval(value.substring(1).replace(/\b[A-Z]+\d+\b/g, "DATA.$&"));