简体   繁体   中英

Getting the scope right with eval

I'm working on this script that lets you build regex with interpolated variables. At the moment I got this and it works beautifully:

function sRegExp( regex, vars ) {
  vars = Array.prototype.slice.call( arguments, 1 );
  regex = regex.toString();
  var newRegex = regex.replace(/(^\/|\/$|\/([igm]+)$)/g, '')
    .replace( /#\{(\d)\}/g, function( a, b ) { return vars[ +b ]; });
  var mods = regex.match( /\/([igm]+)$/ );
  return new RegExp( newRegex, mods ? mods[1] : '' );
}

And I use it like so:

function func() {
  var foo = 'lol';
  return sRegExp( /baz #{0}/i, foo );
}

console.log( func() ); //=> /baz lol/i

I want to improve this script by using the variable name instead of having to use an index and pass in the variables as params so I thought of using eval , so I got rid of vars and refactored the code a bit:

function sRegExp( regex ) {
  regex = regex.toString();
  var newRegex = regex.replace(/(^\/|\/$|\/([igm]+)$)/g, '')
   .replace( /#\{(\w+)\}/g, function( a, b ) { return eval( b ); });
                 __^__                               __^__
  var mods = regex.match( /\/([igm]+)$/ );
  return new RegExp( newRegex, mods ? mods[1] : '' );
}

The problem now with the previous example is this:

console.log( func() ); //=> foo is not defined

But in the global scope...

var foo = 'lol';
function func() {
  return sRegExp( /baz #{foo}/i );
}

console.log( func() ); //=> /baz lol/i

How can I set the context of eval . I tried eval.call(func) but this obviously didn't work. Any ideas?

You cannot set the scope of eval . It inherits the local execution context, and thus only sees what its calling context sees (in this case, within func ). Unfortunately there is no way round this.

When control enters an execution context for eval code, the previous active execution context, referred to as the calling context, is used to determine the scope chain, the variable object, and the this value. If there is no calling context, then initialising the scope chain, variable instantiation, and determination of the this value are performed just as for global code.

From: ES 3.1 10.2.2 Eval Code

I'm no fan of eval nor of cluttering the global space. So, my answer is biased.

You can pass the named variables as a parameters.

function func() {
  return sRegExp( /baz #{foo}/i, { foo: "lol" } );
}

And

function sRegExp( regex, vars ) {
  ...
  var newRegex = regex.replace(/(^\/|\/$|\/([igm]+)$)/g, '')
      .replace( /#\{(\w+)\}/g, function( a, b ) { return vars[b]; });
  ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM