为什么JavaScript中遍历双向链表不会修改原链表?
双向链表代码疑惑解析
实现代码
const LinkedListFactory = () => { let _list = {head: null, tail: null}; const append = (value) => { const node = NodeFactory(value); if (!_list.head) { _list.head = node; _list.tail = node; } else { _list.tail.next = node; _list.tail.next.prev = _list.tail; _list.tail = _list.tail.next; } updateDisplay(); } const prepend = (value) => { const node = NodeFactory(value); if (!_list.head) { _list.head = node; _list.tail = node; } else { _list.head = {value: node.value, next: _list.head, prev: null} _list.head.next.prev = _list.head; } updateDisplay(); } const size = () => { if (!_list.head) return 0; let count = 1; let current = _list.head; while (current.next) { current = current.next; count++; } return count; } const insertAt = (value, index) => { if (index < 0) return 'invalid input: negative index'; if (index === 0) return prepend(value); if (index >= size() || !_list.head) return append(value); //if index > list size OR list is empty, then append the value let current = _list.head; while (index > 0) { current = current.next; //(*) index--; } current = {value: value, next: current, prev: current.prev}; //(**) current.prev.next = current; //(**) current.next.prev = current; //(**) updateDisplay(); } return { append, prepend, insertAt, _list //only for testing purposes } } let list = LinkedListFactory(); list.append(1); list.append(2); list.prepend(0); console.log(list._list);
疑惑问题
上述是本人实现的包含head/tail属性的双向链表,每个节点具备prev/value/next属性,代码可正常运行,但存在疑惑:标注(**)的代码会修改闭包中的私有对象_list,而标注(*)的遍历代码仅移动current指针,未对_list造成任何修改。为何将current赋值为下一个节点时,不会删除当前节点?望得到原理解析或学习方向指引。
原理解析
核心原因是JavaScript中引用类型的变量和对象本身是分离的:
- 标注(*)的
current = current.next只是改变了current这个变量的指向:current原本保存的是某个链表节点的引用,执行这句后,它换成了下一个节点的引用。但这个操作完全没碰原链表的节点对象,也没修改节点之间的prev/next指针,所以原链表的结构毫发无损,自然不会删除任何节点。 - 标注(**)的代码不一样:先是创建了新节点,然后修改了
current.prev.next和current.next.prev——这两个属性属于原链表中已存在的节点对象(这些节点是_list的一部分),修改它们的指向才会真正改变链表的结构,把新节点插入进去。
简单说:要修改链表结构,必须改动节点的prev/next指针;单纯改变遍历用的变量指向,只是换了个"观察点",不会影响链表本身。
学习方向指引
可以重点学习JavaScript的引用类型与值类型的区别,以及内存模型中栈与堆的存储逻辑:
- 值类型(如数字、字符串)的变量直接存值,引用类型(对象、数组)的变量存的是堆内存中对象的地址。
- 变量赋值时,引用类型只是复制地址,不是复制对象本身;只有修改对象的属性,才会影响原对象。
内容的提问来源于stack exchange,提问作者caveman418
相关产品推荐
相关产品推荐

