繁体   English   中英

是否可以从闭包运行的eval(不使用this.variable)访问闭包变量? Javascript(Node.js)

[英]Is possible to access a closure variable from a eval that is run out of the closure (not using this.variable)? Javascript (Node.js)

最好的方式(IMO):

  test.add("Expect job not to be done.", function(expect){
       expect(job.done).toBe(false);
  })

这个问题已经更新,我在这里发布了如果有人可能需要它我是如何结束测试的。

原始问题如下......

我知道它甚至可能不可能,但我会清楚地解释为什么以及我希望它如何工作:

为什么?

我想做一些在缺少某些东西时不会失败的测试,如果存在与否,我不想测试所有内容。

例:

expect('Something', existingClosureVar.notExistingProperty.toString()).toBe('ho ho');

这将抛出这样的错误: TypeError:无法调用未定义的方法'toString'。

解决这个问题很容易,但也很痛苦!

if(existingClosureVar && existingClosureVar.notExistingProperty && existingClosureVar.notExistingProperty.toString){
     expect('Something', existingClosureVar.notExistingProperty.toString()).toBe('ho ho');
}

好吧,但如果它不存在我甚至没有注意到失败的测试! 也许一些更详细的解决方法可能会存在,但会使这个代码变得越来越大,当我想要的只是一件简单的事情时,这应该是最简单的事情。

该怎么办?

expect('Something', 'existingClosureVar.notExistingProperty.toString()').toBe('ho ho');

不知何故,expect函数应该可以访问闭包的本地变量 ,以使其工作。 它将在try-catch上下文中运行字符串,如果失败,那么测试也会失败。

我想要的确切事项:

var callingFn = function(){
    var a = 99;
    // remember, the eval can't sit here, must be outside
    var evalString = 'console.log(a)'; // should print 99
    expect(this, evalString); // 
}
var expect = function(context, evalString){
        var fn = function(){
                eval(evalString)
        }
        fn.call(context);

}
new callingFn(); // creates a this in the callingFn that is not the window object

如果我提供上下文,它可以工作,但......

这需要我使用“这个”。 获取变量的符号。 由于很多函数都是异步的,并且函数的上下文没有被维护(可以维护,但需要更多的工作)(或者我们可以使用闭包变量来保存上下文变量)。

例:

var callingFn = function(){
    var context = {b: 1};
    var evalString = 'b'; // prints 1
    expect(context, evalString)
}
var expect = function(context, evalString){
        var fn = function(){
                console.log(eval('this.' + evalString))
        }
        fn.call(context);

}
callingFn()​

我发现一个丑陋的解决方案:

var callingFn = function(){
    // setup the context
    var context = {};
    // give access to the local closure
    context.fn = function(evalString){
        console.log(eval(evalString))
    }
    var a = 99;
    var evalString = 'a'; // should print 99
    expect(context, evalString);
}
var expect = function(context, evalString){
    context.fn(evalString);
}
callingFn()​

可行的解决方案,但仍然太冗长:

而且我必须在代码前加4行,但是点击这里打开小提琴示例

    var callingFn = function(expect){
    // give access to the local closure
    expect.fn = function(evalString){
        try      {return [undefined, eval(evalString)];
        }catch(e){return [e, undefined];}
    }


    var a = {hey: 1, b: {c: 99}};
    console.log(expect('a.b.c', 99)); // true
    console.log(expect('a.b.c.d.f', 99)); // return the error
    console.log(expect('a.b.c', 44)); // false
    console.log(expect('a.hey', 1)); // true
}
var expect = function(evalString, target){
    var result = expect.fn(evalString);
    var err = result[0];
    var output = result[1];
    if(err){
        return err.stack;
    }else{
        return output === target;
    }
}
callingFn(expect)​

共享上下文:

小提琴链接

var callingFn = function(expect){
    // give access to the local closure
    var context = {};
    expect.context = context;
    context.a = {hey: 1, b: {c: 99}};
    console.log(expect('a.b.c', 99)); // true
    console.log(expect('a.b.c.d.f', 99)); // return the error
    console.log(expect('a.b.c', 44)); // false
    console.log(expect('a.hey', 1)); // true
}
var expect = function(evalString, target){
    var fn = function(evalString){
        try      {return [undefined, eval('this.' + evalString)];
        }catch(e){return [e, undefined];}
    }
    var result = fn.call(expect.context, evalString);
    var err = result[0];
    var output = result[1];
    if(err){
        return err.stack;
    }else{
        return output === target;
    }
}
callingFn(expect)​

本地var +字符串:

小提琴链接

var callingFn = function(expect){
    // give access to the local closure
    var a = {hey: 1, b: {c: 99}};
    console.log(expect(a, '.b.c', 99)); // true
    console.log(expect(a, '.b.c.d.f', 99)); // return the error
    console.log(expect(a, '.b.c', 44)); // false
    console.log(expect(a, '.hey', 1)); // true
}
var expect = function(object, evalString, target){
    var fn = function(evalString){
        try      {return [undefined, eval('object' + evalString)];
        }catch(e){return [e, undefined];}
    }
    var result = fn(evalString);
    var err = result[0];
    var output = result[1];
    if(err){
        return err.stack;
    }else{
        return output === target;
    }
}
callingFn(expect)​

//示例适用于Google Chrome,可能无法在其他浏览器中使用。

不, eval的代码在全局范围内运行。 为什么你需要再次使用eval?

使用匿名函数而不是将代码放在字符串中,所有与范围相关的问题都将消失。

var callingFn = function() {
    var a = 99;
    // remember, the eval can't sit here, must be outside
    expect(this, function() {
        console.log(a);
    });
}
var expect = function(context, callable) {
    try {
        callable.call(context);
    }
    catch(ex) {
        // handle exception
    }
}

演示: http//jsfiddle.net/ThiefMaster/P5VCR/

总之, 没有 您根本无法从eval代码访问闭包中的变量。

使用evalFunction构造函数创建的Function始终在全局范围内运行(或在严格模式下,未定义范围内)。 没有办法避免这种情况。

此外,您无法从函数外部(或该函数内部的函数)访问函数的作用域。 这是因为JavaScript具有静态范围 ; 范围在申报时确定。 函数不能更改其范围链。

如果您希望测试失败,如果属性未定义,最简单的方法可能是:

if (obj && obj.prop) expect('Some Test', obj.prop) ...
else fail('Some Test');

或者使用一个回调,其调用包含在try

expect('Some Test', function() {
    return obj.prop;
}).toBe('some value');

expect可以使用回调的返回值,或者如果您知道将始终定义相关标识符,则允许您传递一个简单值。

function expect(name, o) {
    var expectedValue;
    if (typeof o == 'function') {
        try { expectedValue = o(); }
        catch(ex) { /* fail here */ }
    } else expectedValue = o;

    ...
}

所以你可以用任何一种方式调用expect

function callingFn() {
    var localVar = 1, someObj = {};

    expect('Test var', localVar).toBe(1);
    expect('Test obj', function() { return someObj.missingProp.toString(); }).toBe('value');
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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