Javascript闭包:原始行为与引用行为
Javascript closures: primitive vs reference behaviour
我希望有人能给我解释一下下面的代码是怎么回事。我发现很难理解为什么这个闭包以不同的方式对待原语和引用。我希望我错过了一些明显的东西。
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?
这是因为上下文被绑定到引用,而不是引用被调用/创建时的快照。
让我们逐块地遍历代码,以便更清晰
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 - - - -g = [1,2] - - - - h[使用(1,array-g[1])调用返回函数,使用(2,array-g[1,2])调用返回函数]
3。 - - - - f = 3 - - - -g = [1,2,3] - - - - h[使用(1,array-g[1])调用返回函数,使用(2,array-g[1,2])调用返回函数,使用(3,array-g[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
时,第二个和第三个循环也是如此。
基本类型包含实际数据,而引用类型仅包含内存地址(指向对象数据所在的内存位置的指针)。
所以当你想检查数组的length
字段时(这是一个引用类型),你首先需要找到对象数据所在的内存地址(你查看y
变量),你去那个地址,最后你看数据。
现在,当您调用函数时,对于每个形参,将生成其值的副本并存储在函数作用域中可用的局部变量中。因此,每次将数组作为参数传递时,数据所在的内存地址的副本将存储在局部变量中(仅复制内存地址,而不是整个对象)。
所以,回答你的问题:不,基本类型和引用类型在传递给函数时没有区别对待。在这两种情况下都会进行复制,除了原语包含实际数据,而引用不包含,它们包含地址(指向实际数据的指针)。当您跟踪地址时,您将获得数据,但是在您复制地址和检查数据之间,数据可能已被修改。
g
数组是在returnFunction
范围之外定义的。Javascript总是按值传递。在引用的情况下,引用本身被复制,但它仍然是原始数组对象,因此它重复了3次最终值。
这是因为内部函数只有对数组的引用,而该数组在调用该函数之前已被更改。
你可以通过只访问内部函数中的原始值来修复它。
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);
};
}
这是因为闭包保存了对实参的引用。在函数被调用之前,g数组被修改。为了避免这种情况,您需要复制传入的数组,并将其存储在函数中。
g数组被修改了3次,从未被复制。原语是按值传递和复制的。因此,当定义闭包时,它们保留前一个循环的值。最后一个循环打印相同数组的长度3次。
- 做<img>或者<画布>保存对原始(大)dataUrl对象的引用
- 骨干模型克隆创建对原始模型的引用
- 如何进行服务器端HTTP重定向更新引用程序(更改原始引用程序)
- 更改范围时如何正确引用原始对象
- 缩小后对原始来源的引用
- 维护 jQuery.clone() 元素对原始元素的引用
- 如何在 JavaScript 中覆盖全局函数,但保留对原始函数的引用
- Javascript - 原始与引用类型
- jQuery 插件,保留对原始元素的引用
- 停止在 javascript 中更新原始对象引用
- 主干集合 - 筛选和呈现集合将丢失对原始未筛选集合的引用
- 如何获取原始值的引用
- Object.assign保留对原始对象的引用
- 为什么引用引用don't更新原始对象
- 使源映射引用远程计算机上的原始文件
- 使用highland.js在引用原始流数据的情况下执行串行异步任务
- Javascript闭包:原始行为与引用行为
- 通过jQuery.fn.data()分配变量将保留对原始标记值的引用
- 如何在javascript中创建一个完全独立的关联数组克隆或复制,即不引用原始数据值
- Ember.js覆盖的控制器不引用原始模型