我知道,对于基元数据类型,通过引用传递在javascript中不起作用,所以解决方法是将它们封装在对象中。但是考虑一个场景,其中变量的初始状态为null
,然后将变量重新分配为Object
。现在,如果该变量作为参数传递给外部函数,它将作为引用传递,还是最终在函数内成为undefined
。
为了参考,考虑这个用例:
在登录端点的摩卡测试中
方法1
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
中,它就可以工作了。为什么会这样?
方法2
如果我将响应封装在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;
})
}
注意:如果您发现不清楚,请告诉我。提前谢谢
我能做的最直接的比较是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使用该变量的行为与Javascript的行为相同。因为在PHP中,它是通过引用传递的TRUE,所以可以这样做:
$arr = [];
func($arr);
var_dump($arr);
function func(&$arr) {
$arr = 2;
}
所以当你var_dump($arr)
时,它会输出2
,因为func
实际上改变了$arr
在基本层面上的含义。
与Javascript相比,func
函数不能改变obj
的实际含义,它所能做的就是改变你发送的内容
let obj = {};
func(obj);
console.log(obj);
function func(obj) {
obj = 2;
}
那么当你console.log(obj)
时,你仍然会得到你开始使用的空对象。func
不能通过使用=
符号来改变外界认为obj
指的是什么。
实际上,我想我对它的工作原理有一些想法。
为什么方法#1失败
当需要并调用外部函数时,我们会将响应变量传递给它。该响应变量当前的值为null
。before
函数将响应变量重新分配给一个新的响应对象,它实际上不会在导出函数的执行上下文中更新,因为null
是标量基元值之一(Number、String、Boolean、undefined、null、Symbol)。对于标量基元值,javascript使用stack内存结构,每次重新分配时,我们都会在堆栈顶部添加一个新值,变量就会指向它。因此,导出的函数不知道推送到堆栈的新值,而只知道对旧值的引用。
为什么方法#2成功
现在,当我们将响应包装为对象时,我们实际上使用了两种内存结构,一种是堆栈,另一种是堆。响应的原始值存储在堆中,并在堆栈中维护对它的引用。因此,当before函数更新数据对象的键(响应)时,它实际上不会在堆栈顶部推送一个新值,而是在堆中更新,否则将保持相同的引用。因此,当导出函数内的it
执行时,它可以找到原始数据。
希望这能有所帮助。
我已经运行了您的代码,问题很直接。我将用哪些行首先运行来注释它,以便您查看事件的顺序
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)
。
因此,永远测试.spec.js只会立即以null
的形式获取数据,而且它永远不会也不可能有其他数据。你已经通过了null
,就这样,就是这样。
在第二个方法中,执行顺序完全相同,但您没有将其传递为null,而是将其传递给了对象。当然,随着对象的变化(response
属性得到一个值),expternalTest.spec.js
所看到的data
也会变化,因为data
就是那个对象!