在将函数转换为字符串之前评估函数中的变量

时间:2016-11-17 19:29:52

标签: javascript

我使用pouchDb并查询数据库需要创建一个map函数(这是couchDB的标准做法)

此版本正常运作:

function (doc) {
  if (doc.type) {
    emit(doc.type)
  }
}.toString()

,结果是:

"function mapFunction(doc) {
  if (doc.type) {
    emit(doc.type);
  }
}"

但是,我试图将我的函数调用更加动态,因此我可以通过一个字段来构建map函数。考虑到这一点,我有一个名为field的变量,我将我的地图功能更改为:

var field = '_id'
function (doc) {
  if (doc[field]) {
    emit(doc[field)
  }
}.toString()

问题是,生成的字符串是这样的:

"function mapFunction(doc) {
  if (doc[field]) {
    emit(doc[field]);
  }
}"

但我需要它:

"function mapFunction(doc) {
  if (doc['_id']) { //or doc._id (I don't mind)
    emit(doc['_id']);
  }
}"

有可能实现这个目标吗?

编辑:更糟糕的情况是,我把它写成一个字符串并按照这种方式进行,但更愿意将它作为一个可读的函数。

5 个答案:

答案 0 :(得分:1)

也许是一个带有函数,变量名和值的生成器,并创建你想要的字符串。

这样的东西

function functionGenerator(func, variable, value){
  var r = new RegExp(variable,'gi');
  return func.toString().replace(r, value);
}

function mapFunction(doc) {
  if (doc[field]) {
    emit(doc[field]);
  }
}

var map = functionGenerator(mapFunction, 'field','\'_id\'');

console.log(map);
  
 

答案 1 :(得分:1)

您可以在Function原型上定义一个执行toString的新方法,但允许以对象格式传递变量集合 - 其中每个键都是要使用的变量。这些变量在函数的字符串表示中注入,在函数体打开后用var声明。

每个变量都获得其原始值的JSON表示。当然,这有一些限制,因为并非所有值都可以表示为JSON(循环引用,带方法的对象,等等)。但它肯定适用于原始值,如字符串:



Function.prototype.toStringWith = function(vars) {
    return this.toString().replace(/(\)\s*{)/, 
        '$1\n  var ' + Object.keys(vars)
            .map( key => key + ' = ' + JSON.stringify(vars[key]) )
            .join(',\n    ') + ';');
}

// Demo
var field = '_id'

var s = function mapFunction(doc) {
  if (doc[field]) {
    emit(doc[field])
  }
}.toStringWith({field}); // ES6 shortcut notation

console.log(s);




如果您有更多功能需要的变量"知道",如sizeweightbrand,那么您拨打.toStringWith({field, size, weight, brand}) ,等等。

注意:在函数源中搜索变量名称并将其替换为文字值的解决方案需要小心:变量名称可能出现在带引号的字符串中(单引号,双引号之间)或模板文字,或者是更大名称的一部分,不应该被替换。

答案 2 :(得分:0)

我认为最简单的解决方案是简单的正则表达式。



var field = '_id';
var a = function (doc) {
  if (doc[field]) {
    emit(doc[field])
  }
}.toString();
             
console.log(a.replace(/field/gi, field));




答案 3 :(得分:0)

正如我所评论的那样,因为你需要解析并重新生成这个字符串化的函数。我无法相信插件会强迫某人对函数进行字符串化。

由于从field__id(由于其他标识符等)替换广泛,因此您只能使用其初始值重新声明此field字符串化函数中的值(在顶部指定其值)。

不相关的建议: (提醒:var语句在整个范围内声明一个变量,因此也可以在var语句出现之前分配变量。)

//////////////////////////////////////////////
//////////////// References //////////////////
//////////////////////////////////////////////
var _stringify = JSON.stringify

//////////////////////////////////////////////
//////////////// Variables  //////////////////
//////////////////////////////////////////////
var field = '__id'
/* Store the variables to be copied in the top here */
var locals = { field: field }

/* String to contain the variables */
var stringified_locals = '',
    // thanks to this var length notation
    // we'll separate the variables by commas
    len = 0

/* Calculate the length of vars */
for (let prop in locals)
    ++len

/* Also useful for the variable separation */
i = 0; var i

/* Only declare the 'var' statement if there's at least
 * ONE var */
if (len)
    stringified_locals = 'var '

/* Now generate the string of variables */
for (let prop in locals) {
    let value = _stringify(locals[prop])
    stringified_locals += prop + ' = ' + value

    /* Add comma separator if neccessary */
    if (i++ < (len - 1))
        stringified_locals += ', '
}

/* And the complete stringified function */
stringified_locals + '\r\n' +
(function (doc) {
    if (doc.type) {
        emit(doc.type)
    }
}).toString()

得到了结果:

`var field = "__id"
function (doc) {
    if (doc.type) {
        emit(doc.type)
    }
}`

答案 4 :(得分:0)

你可以这样做:

"(function() {\n" +
    "var field = " + JSON.stringify(field) + ";\n" +
    "return " + mapFunction.toString() + ";" +
"})()"

警告:在极少数情况下,JSON.stringify不会产生有效的javascript。我不确切知道这些案件是什么,或者恶意用户是否有可能以某种方式利用它们。 (你相信谁提供field的价值?)