2015-09-26 48 views
3

我希望有人能向我解释下面的代码中发生了什么。我发现很难包围我的头,为什么这个封闭对待原始信息和引用的方式不同。我希望我在这里失去一些明显的东西。Javascript关闭:原始与参考行为

function returnFunction(x, y) { 
    return function() { 
     alert("x:" + x + " - nb of elements in y:" + y.length); 
    }; 
} 

var f = 0; 
var g = []; 
var h = []; 

for(var i = 0; i < 3; i++) { 
    f += 1; 
    g.push(i); 
    h.push(returnFunction(f, g)); 
} 

for(var i = 0; i < 3; i++) { 
    h[i](); 
} 

// this is what gets returned 
// x:1 - nb of elements in y: 3 
// x:2 - nb of elements in y: 3 
// x:3 - nb of elements in y: 3 

// why do x and y get treated differently by js in this case? 

回答

1

原始类型包含实际数据,而引用类型只包含内存地址(指向对象数据所在的内存位置的指针)。因此,当你想检查数组的length字段(这是一个引用类型)时,首先需要找到对象数据所在的内存地址是什么(你可以看看y变量),你可以看看地址,最后你看看数据。

现在,当您调用一个函数时,对于每个参数,都会创建它们的值副本并存储在您的函数作用域中可用的局部变量中。因此,每当您将数组作为参数传递时,数据所在的内存地址的副本将存储在局部变量中(只复制内存地址,而不是整个对象)。

因此,要回答你的问题:不,原始类型和引用类型在传递给函数时不会有不同的处理方式。在两种情况下都进行复制,除了基元包含实际数据,而引用不包含它们,它们包含一个地址(指向实际数据的指针)。当您按照地址获取数据时,数据可能在您复制地址和检查数据之间进行了修改。

0

g阵列的returnFunction范围之外定义。 Javascript总是通过价值传递。在引用的情况下,引用本身被复制,但它仍然是原始数组对象,因此它最终重复3次。

0

这是因为内部函数只有对数组的引用,并且该数组在调用该函数之前被更改。

您可以通过只访问内部函数中的原始值来修复它。

function returnFunction(x, y) { 
    var len = y.length; // Primitive value 
    return function() { 
     alert("x:" + x + " - nb of elements in y:" + len); 
    }; 
} 

或者,您可以复制数组。即使原始数组在外部改变,副本也不会被改变。

function returnFunction(x, y) { 
    y = y.slice(); // Copy it 
    return function() { 
     alert("x:" + x + " - nb of elements in y:" + y.length); 
    }; 
} 
0

这是因为闭包持有对参数的引用。 g数组在被调用之前被修改。为了避免这种情况,您需要创建一个传入数组的副本,并将其存储在函数中。

3

这是因为,上下文被绑定到引用,而不是被调用/创建时参考的 快照。

让我们穿行码,逐块为具有更清晰

for(var i = 0; i < 3; i++) { 
    f += 1; 
    g.push(i); 
    h.push(returnFunction(f, g)); 
} 

上述循环执行3次,每一次它把一些值在g阵列和h阵列。

因此,让我们进入内部,填入什么值。 在此之前,请用下面的代码清除它。

function returnFunction(x, y) { 
    return function() { 
     alert("x:" + x + " - nb of elements in y:" + y.length); 
    }; 
} 

调用上面的函数一次,会返回一个功能,再次调用它意味着无论你在第一次收到的参考,它会显示一个警告。简而言之,(returnFunction(5,[4,5,6])())将显示警报 "x:5 - nb of elements in y: 3"。 //它看起来像y正在接受一个数组参数,因为在alert中我们有y.length属性。

值填充

loop_number:

1. - - - - F = 1 - - - - G = [1] - - - - H [返回时调用的功能( 1,array-g [1])]

2. - - - - f = 2 - - - - h [用(1, array-g [1]),使用(2,array-g [1,2])调用时返回函数]]

3。(1,array-g [1])调用时的返回函数,用(2)调用时的返回函数。 ,阵列克[1,2]),返回功能当与(3,阵列克[1,2,3])]

最后

for(var i = 0; i < 3; i++) { 
    h[i](); 
} 

我们遍历h称为数组,即我们在上面的解释中的循环3处的数组h中的值。它具有已经具有上下文值的内部函数。即它知道什么是x,什么是每次调用时的y。 记住,第二个参数是我们发送的h数组引用。

因此,如果我们执行像h[i]()那样的函数,它将执行第一个x的主值和y数组引用。尽管当我们使用g数组调用returnFunction并且它只有一个值时,返回的函数与引用绑定,而不是在该快照中绑定的值。 所以输出被打印的阵列大小为// x:1 - nb of elements in y: 3

虽然执行returnFunction相同的推移对第二和第三循环。

0

g数组被修改3次并且从不复制。基元通过值传递并被复制。因此,当闭包被定义时,它们保持前一个循环的值。最后一个循环打印相同阵列的长度3次。