JavaScript Array.push非原始值变量使用问题及闭包函数数组变量疑问
关于JavaScript数组push和闭包的两个问题解答
问题1:为啥推入非原始值时,Array.push的表现和原始值不一样?
其实你可能有点误解啦——Array.push()不管推原始值还是引用类型,都是可以用变量的!两者的差异完全是JavaScript对不同类型的存储机制导致的:
- 对于原始值(比如数字、字符串、布尔值这些),它们是直接存在栈内存里的。当你把变量push进数组时,数组里存的是这个值的拷贝版。之后你再修改原变量,数组里的内容不会跟着变,比如:
let num = 10; let arr = []; arr.push(num); num = 20; console.log(arr[0]); // 还是10,和原变量没关系了
- 而引用类型(比如对象、数组、函数)是存到堆内存里的,变量本身只是个“指向堆内存的地址”。当你push这个变量时,数组里存的是这个地址的拷贝,和原变量指向同一个堆内存里的对象。所以之后你修改原变量指向的对象内容,数组里的元素自然也会跟着变,比如:
let obj = {name: "小明"}; let arr = []; arr.push(obj); obj.name = "小红"; console.log(arr[0].name); // 变成小红了,因为俩变量指向同一个对象
你觉得“push好像没使用变量”,其实是引用类型的共享特性搞的鬼,本质上push确实用了变量哦~
问题2:闭包函数里,为啥数组里的函数都引用同一个i?
先看你给的这段经典代码:
function buildFunctions() { var arr = []; for (var i = 0; i < 3; i++) { arr.push(function() { console.log(i) }) } return arr; } var fs = buildFunctions(); fs[0](); // 3 fs[1](); // 3 fs[2](); // 3
核心原因有两个:
var没有块级作用域:用var声明的i是属于buildFunctions整个函数作用域的,整个for循环里只有这一个i变量——每次循环只是更新它的值,不是创建新的i。- 闭包捕获的是变量引用,不是当时的值:你push进数组的那些匿名函数并没有立即执行,它们只是“记住”了自己所在的作用域链,也就是捕获了对这个唯一的
i的引用。
等你之后调用fs[0]()这些函数时,for循环早就跑完了,i的值已经变成3了,所以所有函数打印的都是这个最终的3。
如果想让每个函数打印对应的0、1、2,有两个简单办法:
- 改用
let声明i:let有块级作用域,每次循环都会创建一个新的i变量,每个函数捕获的是当前循环的那个独立的i:
function buildFunctions() { var arr = []; for (let i = 0; i < 3; i++) { arr.push(function() { console.log(i) }) } return arr; } var fs = buildFunctions(); fs[0](); // 0 fs[1](); // 1 fs[2](); // 2
- 用立即执行函数(IIFE)创建独立作用域:把当前的
i传进去当参数,每个IIFE里的参数都是独立的,函数捕获的是这个参数:
function buildFunctions() { var arr = []; for (var i = 0; i < 3; i++) { arr.push((function(num) { return function() { console.log(num) } })(i)) } return arr; } var fs = buildFunctions(); fs[0](); // 0 fs[1](); // 1 fs[2](); // 2
内容的提问来源于stack exchange,提问作者Fatah
相关产品推荐
相关产品推荐

