【问题标题】:Passing variables by reference in javascript - passing variables to external test in mocha在javascript中通过引用传递变量-在mocha中将变量传递给外部测试
【发布时间】:2021-08-26 13:05:16
【问题描述】:

我知道 primitive 数据类型 通过引用传递 在 javascript 中不起作用,因此解决方法是将它们包装在对象中。但是考虑一个场景,变量的初始状态是null,然后变量被重新分配为Object。现在,如果将该变量作为参数传递给外部函数,它将作为引用传递还是最终在函数内部成为undefined

参考这个用例:

Login 端点的 mocha 测试中

方法一

describe('Login Endpoint Test', function(){
   let response = null;
   
   before('test pre-requisites', async function(){
      this.timeout(15000);
      response = await endpointCall(); //returns response Object
   });   

   it('simple endpoint test', function(){
      //response is availble here.
      response.should.have.status(200);
   });
  
   /*Importing an external testfile.*/
   require('./externalTest.spec.js')(response);
})

在 externalTest.spec.js 中

module.exports = (response) => {
   it('external test', function(){
     console.log(response);  // null;
   })
}
   

如果我将响应包装在 Object 中,它会起作用。为什么会这样?

方法二

如果我将响应包装在 Object 中,它会起作用。为什么会这样?

describe('Login Endpoint Test', function(){
   let data = {response: null};
   
   before('test pre-requisites', async function(){
      this.timeout(15000);
      data.response = await endpointCall(); //returns response Object
   });   

   it('simple endpoint test', function(){
      //response is availble here.
      data.response.should.have.status(200);
   });
  
   /*Importing an external testfile.*/
   require('./externalTest.spec.js')(data);
})

在 externalTest.spec.js 中

module.exports = (data) => {
   it('external test', function(){
     console.log(data.response);  // response object;
   })
}
   

注意:如果您发现有不清楚的地方,请告诉我。提前致谢。

【问题讨论】:

  • @ScottMarcus 你能解释一下为什么第一种方法不起作用,而后一种方法成功了吗?
  • Javascript 从不通过引用传递,总是值。关于对象的事情是变量/常量的值本身就是一个引用。因此,对象可用于“模拟”传递引用功能,但前提是代码的不同部分保持相同的对象引用。在您的“非工作”示例中,您正在覆盖该值,即删除引用。在您的工作示例中,您只是更改了被引用对象的属性,并且引用本身被维护。
  • Additional post that further explains 为什么看起来 JavaScript 有传递引用,但实际上没有。
  • @DevDesignerSid 一个空对象应该可以工作。可以这样想:一旦你在回调中执行response = ...,你的方法就会中断。 response.property = ... 有效。将response 声明为const 而不是let 以帮助您记住;)

标签: javascript node.js mocha.js javascript-objects chai


【解决方案1】:

我能做的最直接的比较是 PHP,在这方面它与 Javascript 有一些相当大的不同。

首先,在 PHP 中,如果将数组传递给函数,并在函数中更改数组,则原来的数组实际上并没有改变。如果传入数组,则数组按值完全复制

$arr = [];
func($arr);
var_dump($arr);

function func($arr) {
   $arr['key'] = 'value';
}

当您在 PHP 中 var_dump($arr) 时,您可能希望它具有 'key' => 'value',但在 PHP 中并非如此。

javascript 中的相同示例如下所示:

let obj = {};
func(obj);
console.log(obj);

function func(obj) {
   obj['key'] = 'value';
}

您可以看到,与 PHP 的数组示例不同,直接在 Javascript 中更改对象会在两个地方都更改它。

这给我们带来了下一个重大区别 - PHP 具有完整的按引用传递功能,如下所示:

$arr = [];
func($arr);
var_dump($arr);

function func(&$arr) {
   $arr['key'] = 'value';
}

PHP 中的 & 符号表示通过引用传递,因此在 PHP 中的该示例中,当您使用 var_dump($arr) 时,您会看到 key => value

然而,这个 & 符号实际上并没有使 PHP 的行为与这个变量相同 Javascripts 的行为。因为在 PHP 中,它是一个 TRUE pass by reference,你可以这样做:

$arr = [];
func($arr);
var_dump($arr);

function func(&$arr) {
   $arr = 2;
}

所以当你var_dump($arr) 时,它会输出2,因为func 实际上从根本上改变了$arr 的含义。

与 Javascript 相比,func 函数无法更改 obj 的实际含义,它所能做的就是改变您发送的内容。所以如果你尝试在 javascript 中做同样的事情:

let obj = {};
func(obj);
console.log(obj);

function func(obj) {
   obj = 2;
}

...那么当您console.log(obj) 时,您仍然会得到您开始使用的空对象。 func 无法通过使用= 符号来改变外界对obj 的看法。

【讨论】:

    【解决方案2】:

    实际上我想我对它的工作原理有一些想法。

    为什么方法 #1 失败了?

    当需要并调用外部函数时,我们将响应变量传递给它。此响应变量当前的值为 null。而before 函数将响应变量重新分配给一个新的响应对象,它实际上并未在导出函数执行上下文中更新,因为null 是标量原始值(数字、字符串、布尔值、未定义、空值、符号)。 对于标量原始值,javascript 使用 stack 内存结构,并且在每次重新分配时,我们将在堆栈顶部添加一个新值,并将变量指向它。所以导出的函数不知道压入堆栈的新值,而只是对旧值的引用。

    为什么方法 #2 成功了

    现在,当我们将响应包装为一个对象时,我们有效地使用了两种内存结构,一种是 stack,另一种是 heap。响应的原始值存储在堆中,并在堆栈中维护对此的引用。因此,当 before 函数更新数据对象的键 (response) 时,它实际上不会 push 一个新值到堆栈顶部,而是在堆中更新,therby保持相同的参考。这样当导出函数里面的it执行时,就可以找到原始数据了。

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      我已经运行了您的代码,问题很简单。我会用首先运行的行来注释它,以便您查看事件的顺序

      1 describe('Login Endpoint Test', function(){ 
         2 let data = null;
         
         3 before('test pre-requisites', async function(){
            8 this.timeout(15000);
            9 data.response = await endpointCall(); //returns response Object
         });   
      
         4 it('simple endpoint test', function(){
            //response is availble here.
            10 data.response.should.have.status(200);
         });
        
         /*Importing an external testfile.*/
         5 require('./externalTest.spec.js')(data);
      })
      
      // different file
      6 module.exports = (data) => {
         7 it('external test', function(){
           11 console.log(data.response);
         })
      }
      

      这里的重要位是 (2) 和 (5)。你说的是data is null,然后是require expternalTest.spec.js and pass it whatever data is (which is null)

      因此,eternalTest.spec.js 只是立即获取null 的数据,它永远不会也永远不会有其他任何东西。你已经通过了null,就是这样,就是这样。

      在你的第二个方法中,执行顺序是完全一样的,但是你没有传给它null,你传给了它对象。所以当然随着对象的变化(并且response 属性得到一个值),expternalTest.spec.js 所看到的data 会更改为,因为data 就是那个对象!

      【讨论】:

      • 是的? 我刚刚在回答中详细说明了这一点。无论如何感谢您的观看。
      猜你喜欢
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 2011-12-06
      • 1970-01-01
      • 2021-12-20
      相关资源
      最近更新 更多