成都网站制作公司seo问答
数组本质是对象,键就是索引,值就是元素。
push /unshift 在数组最后/最前添加
pop /shift 把数组最后/最前的元素删除,返回的是被删除的元素
splice(0,2,5)从第0给位置开始删除2个元素,并添加一个元素
数组自带的排序方法
let arr=[2,5,4,3,1]
arr.sort((y,x)=>x-y)x-y>0交换顺序,不>0就不会交换顺序
最后结果时[2,3,4,5,6]顺序
arr.sort(x,y)
迭代方法 every some filter map foreach reduce
arr.every((item)=>item>12)//如果每一项都满足条件,结果才是truearr.some((item)=>item>12)//只要有其中一项满足条件,结果就是true
map映射,简单来说,就像是一个“翻译器”或者“转换器”。它的主要工作就是将一种东西(比如数字、文字、对象等)按照一定的规则转换成另一种东西。
举个例子,假设你有一个列表,里面装着一些数字:[1, 2, 3, 4, 5]。现在你想把每个数字都变成它的两倍。那么,你可以使用map映射来实现这个目标。map映射会遍历这个列表,把每个数字都拿去乘以2,然后返回一个新的列表:[2, 4, 6, 8, 10]。
再比如,你有一个装着名字的列表:[“张三”, “李四”, “王五”]。你想把每个名字都变成“你好,XXX”的格式。使用map映射,你可以很容易地得到一个新的列表:[“你好,张三”, “你好,李四”, “你好,王五”]。
所以,map映射就是一个非常方便的工具,可以帮助我们快速地对一组数据进行统一的转换或处理。
forEach
arr.forEach((item,index)=>console.log(item,index))//返回数组里每个元素和索引值
reduce
第一个元素是上一次执行的返回值
let res=arr.reduce((item1,item2)=item1+item2)//将每一个元素累加
迭代器对象
每一个数组都有一个迭代器
let ite=arr[Symbol.iterator]()
如上拿到迭代器接口
只要符合迭代器对象就能使用for of、循环遍历对象
for(let i of arr){console.log(i); }
console.log(arr.entries())
结果是迭代器对象
就能拿到键值对
for(let i of arr.entries()){console.log(i);}
拿到键
for(let i of arr.keys()){console.log(i);}
拿到元素的值
for(let i of arr.values()){console.log(i);}
Arry.from将一个类似于数组的对象转化为真的数组结构
function test(){console.log(Array.from(arguments));}
test(1,2,3)
虽然test函数没有形参,但是用arguments可以找到,但是arguments是一个对象用from可以转化成对象
搜索 indexof lastIndexof find findIndex findLast findLastIndex includes
indexof
如果包含就返回正确的索引值,如果不包含就返回-1
let arr=[12,22,23]
arr.indexOf(15)
//结果是-1
includes
包含就返回true,不包含返回false
arr.includes(15)
find能返回最先满足条件的元素
findLast从后面开始查找
findIndex返回索引值
findLastIndex找出数组里最后一个大于10的元素
let res2=arr.findLastIndex(item=>item>10)
栈结构的封装
定义一个类,初始化一个元素。
class stack{constructor(){this.item=[]}}let stack =new stack()
现在可以直接跳过constructor
class stack{items=[]}
-
pop() 方法:
pop()
方法应该是从栈中移除并返回最后一个元素,而不是添加元素。因此,pop()
方法不需要参数,并且应该调用this.items.pop()
来移除数组中的最后一个元素。
-
peek() 方法:
peek()
方法用于查看栈顶元素,但不移除它。因此,peek()
方法也不需要参数,并且应该返回this.items[this.items.length - 1]
,而不是调用this.items.peek()
。因为this.items
是一个数组,而数组没有peek()
方法。push(data)
方法- 接受一个参数
data
,并将其添加到栈顶。 - 返回undefined
-
栈
-
class stack {items = []pop(){this.items.push()}push(){this.items.push(data)}peek(data){return this.items[this.items.length-1]}isEmpty(){// return this.items.length===0return this.items.at(-1)}size(){return this.items.length}clear(){return this.items=[]}Tostring(){return this.items.join(',')}}let stack =new stack()
由于我们可以随便用比如this.item.splice(0,2,9)的方式破坏栈,可以加下划线
_items = []
表示栈的私有性,来保护栈里的元素
-
直至发布es13后才支持自带的属性用在前面加井号的方式
-
#items = []
每次访问stack.#items.push()用stack里面的方法才能改变栈的元素。直接stack.item会报错.
-
用栈方法应用——解决十进制转换
-
每一次把一个十进制的整数除以2的余数用push方法把余数push到栈里越往后的余数月堆到栈顶,再用pop方法把每次余数取出来,也就是从最后的余数取出
let stack =new stack()function Dec(Decnumber){let remStack=new stack()let number=Decnumberlet string=""while(number>0){remStack.push(number)number=Math.floor(number%2)}while(!(remStack.isEmpty())){string+=remStack.pop()}return string}Dec(50)
限制进制数如:8
-
进制数如果是16,但是没有定义用ABCDEF表示10-16就需数组或者字符串定义
-
function ECD(edcnumber){let string=''let rank=new stack()let number=edcnumberwhile(number>0){rank.push(number%2)number=Math.floor(number / 2) }while(!(rank.isEmpty())){string+=rank.pop()}return string} EDC(500,16)//让500按照16进制转换
队列
-
先进先出:像日常做核酸,排在列队头的人做完出去,队尾可以随时加人
-
队列和栈差不多,不同的在于
-
dequeue:返回队头被删除的元素,方法里面用shift来删除第一个元素
-
enqueue:表示队尾添加
-
front:返回队头
class stack{#item=[] dequeue(){ return this.#item.shift()}enqueue(data){this.#item.push(data)}front(){return this.#item.push.at(0)}isEmpty(){return this.#item.length===0}size(){return this.#item.length}clear(){ this.#item=[]}Tostring(){return this.#item.join(" ")}}
但是:用shift的缺点是删除第一个元素就使后面的元素都往后移动一个。若数据太多对渲染效果有害,而delet方法使删除的元素位置上是empty。
-
class Queue{#items={}#lowCout=0//记队头的索引值#count=0//为每次改变往后移记录索引值dequeue() {if(this.isEmpty()){return undefined}//防止出现size负数let res=this.#items[this.#lowCout]delete this.#items[this.#lowCout]this.#lowCout++return res}enqueue(data) {this.#items[this.#count]=datathis. #count++}front() {return this.#items[this.#lowCout]}isEmpty() {return this.size()===0}size() {return this.#count-this.#lowCout}clear() {this.#items={}this.#lowCout = 0this.#count = 0}Tostring() {}}
-
最应该采用对象obj的方式,来模拟数组
obj={0:1,1:2,3:3}
那么数据结构就会改变,所以之前的队列就不能用了。
-
队列应用-——击鼓传花
-
首先由几个玩家,规定一轮击鼓数为7.转为编程思想就是由队头被删并加在队尾,队列轮回后到谁时第七个结束谁出局。首先定义几个玩家,和击鼓传花数
-
game(['kerwin','xiaoming','tiechui','gangaer','guludunzi'],7)
封装game函数
-
1,new一个队列
-
let queue=new Queue()
-
2,邀请玩家入场!
-
for(let i=0;i<list.length;i++){queue.enqueue(list[i]) }
-
3,开始游戏!
-
while(queue.size>1){for(let i=0;i<num;i++){queue.enqueue( queue.dequeue())}
4,获胜者出列颁奖!
-
return queue.dequeue()
完整代码
-
function game(list,num){ let queue=new Queue() for(let i=0;i<list.length;i++){queue.enqueue(list[i]) } while(queue.size>1){for(let i=0;i<num;i++){queue.enqueue( queue.dequeue())}console.log(queue.dequeue(),"淘汰了"); } return queue.dequeue() } game(['kerwin','xiaoming','tiechui','gangaer','guludunzi'],7)
双端队列
-
可以从队头出,也可以从队头进,可以从队尾出,也可以从队尾进。例如,日常排队从队头插队,也可以看队太长了从队尾直接离开
-
把方法改为从头加addFront
-
分为三个情况:
-
lowcount(开头索引值)=0;把数组中后面的元素向后移动一位,把第一个位置空出来。然后在第一个位置上添加数据data。
-
else{//lowcount从0开始for(let i=this.#count;i>0;i--){this.#items[i]=this.#items[i-1]}this.#items[0]=data //0的位置被移出来,就可以添加数据this.#count++}
-
lowcount(开头索引值)>0;把lowcount减一,留出0的位置。添加data
-
if(this.#lowCout>0){this.#lowCout--this.#items[this.#lowCout]=data
-
数组为空就可以直接添加复用addback方法。
-
//如果为空if(this.isEmpty()){this.addBack(data)}
还有新颖的是在队尾删除
-
因为count是从0开始计数,最后一个元素队尾元素的索引是
#count - 1
,因为数组索引从0开始。因此,#count
本身并不直接指向队尾元素,而是指向队尾元素之后的下一个位置(即队列的长度)。removeBack(){if(this.isEmpty()){return}this.#count--let res=this.#items[this.#count]delete this.#items[this.#count]return res}
其他和单队列差不多
-
class Queue{#items#count=0#lowcountremoveFont(){let res=this.#items[this.#lowcount]delete this.#items[this.#lowcount]return res}addBack(){this.#items.this.#count=[data]this.count++}addFront(){if(this.isEmpty()){this.#items.addBack()}else{if(this.#lowcount>0){this.#lowcount--this.#items[this.#lowcount]=[data]}else {for(let i=0;i<this.#count;i++)this.#items[i+1]=this.#items[i]for(let i=this.#count;i>0;i--)this.#items[i]=this.#items[i-1]}}}removeBack(){if(this.isEmpty()){return}this.#count--let res=this.#items[this.#count]delete this.#items[this.#count]return res}isEmpty(){return this.size()===0}size(){return this.#count-this.#lowcount}peekFront(){return this.#items[this.#lowcount]}peekBack(){if(this.isEmpty()){return }return this.#items[this.#count-1]}toString(){let str=""for(let i=0;i<this.#count;i++){str=`${this.#items[i]}`}return str} } let dequeue=new Queue() function test(str){ const lowstr=str.toLocalLowerCase().split("").join let queue=new Queue() for(let i=0;i<lowstr.length;i++){queue.addBack(lowstr[i]) } console.log(dequeue);}
应用:回文检查如dad
-
:split方法会自动缩进空格
-
function test(str){ const lowstr=str.toLocalLowerCase().split("").join let queue=new Queue() for(let i=0;i<lowstr.length;i++){queue.addBack(lowstr[i]) } while(dequeue.size()>1){if(dequeue.removeBack!==dequeue.removeFont){ isEqual=falsebreak;} } } test('DA D')
链表
-
模拟栈和队列,也有自己独特的魅力
-
在构造函数中设置
next
为null
可以确保每个新创建的节点都默认没有指向下一个节点,合链表节点的初始状态。this.element = element;
这行代码的作用是将传递给构造函数的element
参数的值赋给新创建的节点实例的element
属性。 -
class Node {constructor(element) {this.element = element; // 节点存储的数据this.next = null; // 指向下一个节点的链接} }
-
在linkedList中再定义记录节点位置和头节点
-
1,在constructor构造函数器中用定义并初始化两个变量,cout用来记录链表中的节点,应为要从链头到链尾一点点找,还要初始化一个链头head
-
2,push从后面插入
-
push(){const node = new Node(element)if(head==null){this.head=Node }else{let current=this.headwhile(current.next!==null){current =current.next}current.next=node}this.count++}
-
3,删除分为两种:删除特定位置,删除特点元素数据
-
删除特定位置
-
//指定位置删除removeAt(index){if(index>=0&&index<this.count){let current=this.headif(index===0){this.head=this.head}else{let previousfor(let i=0;i<index;i++){previous=currentcurrent=current.next}previous.next=current.next}this.current--return current.element}}
-
参数检查:
- 为了保护head不被破坏复制给current找节点头
- 首先检查传入的索引
index
是否有效,即是否在链表的范围内(0 到this.count - 1
)。
- 首先检查传入的索引
-
删除头节点:
- 如果
index
为 0,表示要删除的是头节点。在这种情况下,只需要将头节点指向下一个节点即可。
- 如果
-
删除非头节点:
- 如果
index
大于 0,则需要遍历链表找到要删除的节点及其前一个节点。 - 使用两个指针
previous
和current
,其中current
初始指向头节点。 - 遍历链表,直到
current
指向要删除的节点。 - 然后,将
previous
的next
指针指向current
的next
,从而跳过current
节点,实现删除。
- 如果
-
更新计数:
- 删除节点后,应该减少链表的节点计数
this.count
。但在您提供的代码中,这一步被遗漏了。 - 你可能有这样的疑惑
-
为什么
current
是正确的节点 - 初始化时,
current
指向头节点,这是已知的。 - 循环确保了
current
指针从头节点开始,逐个节点移动,直到到达指定的索引位置。 - 循环结束后,
current
指针所指向的节点就是我们要删除的节点,因为我们已经根据index
的值移动了current
指针相应的次数。 -
因此,不需要将
current
复制为index
,因为current
的位置是通过遍历链表来确定的,而不是通过直接设置索引值。循环的结构和current
指针的移动确保了在循环结束时,current
指向的是正确的节点。 -
实验效果,
为简化方法,封装一个函数来接节点
-
getNodeAt(index){if(index>=0&&index.this.count){let node=this.headfor(let i=0;i<index;i++){node=node.next}return node}}
在removeAt函数中precious调用函数接收节点
-
removeAt(index){if(index>=0&&index<this.count){let current = this.headif(index===0){this.head=this.head}else{let previous=this.getNodeAt(index-1)previous.next=currentprevious.next=current.next}this.current--return current.element}}
上面是根据节点位置删除,如果你想删除一个数据就需要下面的方法
-
规定两个方法,第一个用于比对节点中你想找到的数值,并返回索引值
-
indexOf(element){let current =this.headfor(let i=0;i<this.count;i++){if(方法判断(element,current.element)){return i}current=current.next} return -1 }
在这个
indexOf
函数中,如果方法判断(element, current.element)
返回true
,那么return i;
会被执行,函数会返回索引i
,并且不会再执行current = current.next;
或者任何后续的循环迭代。整个函数调用会结束,控制权会返回到调用indexOf
函数的地方。 -
第二个用于接收索引值,调上一个删除方法
-
例如这样一个方法
-
equalFn(a,b){return a===b}
但是如果是一个对象,就不能这样简单对比,因为引用数据类型在栈中存放地址,每个对象地址不一样指向堆内存中实际对象的地址。
-
可以直接暴力的转换成字符串
-
equalFn(a,b){return JSON.stringify(a)===JSON.stringify(b)}
但是如果把两个对象交换位置,就不能判断了
-
可以直接用第三方库imumutable
-
双向链表
-
每个节点即能指向前一个(prev),有能指向后一个(next)
-
插入值
-
如果想在头部插入
-
insert(element,index){if(index>=0&&index<=this.count){const node=new Node(element)if(index===0){const current =this.headnode.next=currentthis.head=node } }
-
if(index === 0){ // 如果插入位置是链表头部
这行代码检查是否要在链表的头部插入新节点。
index
是插入位置的索引,如果它是0,意味着新节点应该成为新的头节点。 -
node.next = this.head; // 新节点的下一个节点设置为当前头节点
这行代码将新节点(
node
)的next
属性设置为当前的头节点(this.head
)。这样,新节点就指向了原来的头节点,保持了链表的连续性。node
:新创建的节点,即将插入链表。this.head
:当前链表的头节点。node.next = this.head
:将新节点的next
指针指向当前的头节点。-
如果想插入的位置不是头部
-
思想:
-
示例
假设链表当前状态如下:
复制
Head -> A -> B -> C
其中,A 是头节点,B 是第二个节点,C 是第三个节点。现在,我们想要在B和C之间插入一个新节点X:
previous = this.getNodeAt(1);
// 获取B的引用current = previous.next;
// 获取C的引用node.next = current;
// 设置X的next指向Cprevious.next = node;
// 设置B的next指向X-
插入后的链表状态:
复制
Head -> A -> B -> X -> C
总结
- 当在链表的特定位置插入新节点时,需要找到该位置的前一个节点和当前节点。
- 将新节点的
next
指向当前节点。 - 将前一个节点的
next
更新为新节点。 - 具体代码
-
else{const previous=this.getNodeAt(index-1)//获取前一个节点const current =previous.next//让前一个节点指向新节点引用的位置node.next=current//把新建的节点放进去previous.next=node}
完整插入代码
-
insert(element,index){if(index>=0&&index<=this.count){const node=new Node(element)if(index===0){const current=this.head//保护headnode.next=current//当前头节点作为新节点的下一个this.head=node//把新节点作为头节点}else{const previous=getNodeAt(index-1)const current=previous.nextnode.next=currentprevious.next=node}this.count++return true}return false }
剩下几个简单的方法
-
isEmpty(){return this.size===0 } size(){return this.count } gethead(){return this.head }
双端链表
-
还可以继承单链表的属性
-
class DoubleNode extends Node {constructor(element) {super(element); // 调用父类Node的构造函数this.prev = null; // 初始化DoubleNode特有的pre属性} }
-
super(element)
调用Node
类的构造函数,并将element
参数传递给父类构造函数。这样,DoubleNode
实例就继承了Node
类的element
属性 -
之前封装的单向链表中很多方法需要重新封装
-
push
-
push(){const node=new DoublyNode()//prev next if(this.head===null){this.head=nodethis.tail=node }else{this.tail.next=nodenode.prev=nodethis.tail=node }
如果头部为空,就直接加
-
头部不为空
-
让链表尾部next与新节点连接,新节点prev指向尾部,再让尾部彻底等于新节点
-
insert
- 不说了,思想全在图里
-
insert(element,index){const node=DoublyLinked(element)let current=this.headif(index>=0&&index<=this.count){if(index===0){if(this.head===null)this.head=nodethis.tail=node}else{node.next=this.headthis.head.prev=nodethis.head=node} }else if(index===this.count){current=this.tailcurrent.next=nodenode.prev=currentthis.tail=node}else{const previous=getNodeAt(index-1)current=previous.nextnode.next=currentcurrent.prev=nodeprevious.next=nodenode.prev=previous}this.count++return true}
removeAt删除
-
注意:index要小于this.count,因为count从0开始,最后的count值没有节点(和insert不同)
-
按照索引值删除和insert思想差不多,需要注意的是如果让head的next指向下一个会出问题,因为曾经的尾部被head占了,所以之前让head直接赋值为head的next—— this.head=current.next
-
removeAt(index){if(index>=0&&index<this.count){let current=this.headif(index===0){this.head=current.nextif(this.count===1){this.tail===null}else{this.head.prev=undefined} }if(index===this.size()-1){let current =this.tailcurrent.prev=this.tailthis.tail.next=undefined}else{let previous=this.getNodeAt(index-1)current=previous.nextprevious.next=current.nextcurrent.next.prev=previous}this.count--return current.element}}
remove
-
按照数值删除和单链表方法一样
-
循环链表
-
就是让最后节点的next指向head,循环起来
-
insert插入
-
insert(element,index){let node=new DoublyLinked(element)let current=this.headif(index>=0&&index<=count){if(index===0){if(head===0){this.head=nodenode.next=this.head}else{node.next=currentcurrent = this.getNodeAt(this.size() - 1)//不能保证后面的节点指向head,下面解决,获得尾部元素this.head=node//current.next=this.head//让尾部元素next指向head,保证循环}}else{const previous=this.getNodeAt(index-1)current=previous.nextprevious.next=nodenode.next=current}this.count++ return true}return false }
removedAt——按索引删除
-
removeAt(index){ if(index>=0&&index<count){let current=this.headif(index===0){if(this.size()===1){let last=this.getNodeAt(this.size()-1)this. head=this.head.nextlast.next=head}}else{let previous=this.getNodeAt(index-1)current=previous.nextprevious.next=current.next//不用怕删除后没有循环,因为被删的current的next就指向head}this.count--return current.element } }
集合set
-
无序且唯一的项组成
-
因为对象的属性不能重复适合作为集合,下面我们用对象模拟
-
属性和属性名都是一个内容,存100就是100:100所以想要操作某个元素直接this.items[element]就可以
-
建一个class封装增删查改
-
class kerwin{// constructor(){// this.items={}// }items={}add(element){if(!this.has(element)){this.items[element]=elementreturn true}return false}delete(element){ if(this.has(element)){delete this.items[element]return true }}has(element){ return element in this.items}clear(){ return this.items={}}size(){ return Object.keys(this.items).length}values(){ return Object.values(this.items)}}
其中size方法用
Object.keys()
方法会返回一个包含对象所有自有属性(不包括原型链上的属性)的键(key)的数组。 -
应用:检查数组中是否有重复元素
-
var arr=[1,2,3,3,3,4]arr.forEach(items=>{myList.add(items)})
Es6中的set属性
-
迭代器
-
符合迭代器的元素可以用多个next遍历,也可以用for...of遍历,也可以用展开运算符变为普通数组
-
与上面我们自己封装的set不同在于,Es6的size是一个属性,我们是方法
-
应用
-
交集,并集,差集——从后端接收数据时候使用
-
var mySetA=new Set([1,2,3])var mySetB=new Set([2,3,4])// 并集var mySetAnd=new Set([...mySetA],[...mySetB])// 交集——只有数组才有filter方法var mySetJiao=new Set([...mySetA].filter(item=>mySetB.has(item)))//差集var mySetJiao=new Set([...mySetA].filter(item=>!mySetB.has(item)))
字典
-
和集合很相似,但集合是【值,值】,字典【键,值】,键可以是对象,也可以是任意数据类型
-
class Dictionary{table={}//将键转化为字符串tostrFn(item){if(item===null){return 'NULL'}else if(item===undefined){return 'UNDEFINDED'}else if(item==='string'||item instanceof String){return item}return JSON.stringify(item)} }
tostrFn
-
:使用
tostrFn
方法将key
转换为一个字符串。这是因为在 JavaScript 对象中,键总是被转换为字符串。tostrFn
方法的执行步骤如下:- 如果
item
(在这里是key
)是null
,返回字符串'NULL'
。 - 如果
item
是undefined
,返回字符串'UNDEFINED'
。 - 如果
item
是字符串或String
对象的实例,直接返回该字符串。 - 否则,使用
JSON.stringify
将item
转换为字符串。
- 如果
-
检查键存在性:使用转换后的键字符串在
this.table
中查找。this.table
是一个对象,用于存储字典的键值对。 -
返回结果:如果找到对应的键(即
this.table
中存在该键),则this.table[this.tostrFn(key)]
不会是null
,haskey
方法返回true
。如果没有找到,或者对应的值是null
,则返回false
。 注意:如果haskey
方法中检查的是!== null
,这意味着如果键存在但对应的值是undefined
或其他非null
值,它也会返回true
。所以改为!=null就可以了-
set
- 检查是否有键
- 第一种方法
//检查是否含有键haskey(key) {return this.table[this.tostrFn(key)] != null}set(key,value){if(key!==null&&value!==null){const tablekey=this.tostrFn(key)this.table[tablekey]=valuereturn true}return false}}
这种方法将键存放的被转化后的字符串键名,所以另起一个方法二将value中存键+值,保证获取到最原始的键
-
方法二
-
set(key,value){if(key!==null&&value!==null){const tablekey=this.tostrFn(key)this.table[tablekey]=new ValuePair(key,value)return true}return false}}var mysseet=Dictionary()class ValuePair{constructor(key,value){this.key=keythis.value=value}}
valuepair类创建一个实例对象保存一份原始的key,value
-
其他方法
-
get(key){const ValuePair=this.table[this.tostrFn(key)]return ValuePair ==null?undefined:ValuePair.value}remove(key){if(this.haskey(key)){delete this.table.tostrFn(key)return true}return false}keys(){return this.keyValues().map(item=>item.key)}values(){return this.keyValues().map(item=>item.value)}keyValues(){return Object.values(this.table)//Object.values() 只返回自有属性的值,不返回继承属性的值。}size(){return Object.keys(this.table).length}isEmpty(){return this.size===0}clear(){return this.table=null}forEach(cb){const ValuePair=this.keyValues()for(let i=0;i<ValuePair.length;i++){cb(ValuePair[i].key,ValuePair[i].value)}}
散列表
-
有上字典可以看到,由于set每次查找键都需要转化为JSON字符串很麻烦,并且遍历上万条大数据很消耗性能,所以散列表可以把键通过散列函数转换为一个数组作为索引,再从散列表中查找
-
set方法(就相当于push)
-
set (key,value){if(key!=null&&value!=null){const position=111this.table[position]=ValuePair(key,value)return true}return false}
需要有一个方法能给table设置类似position=111这个索引数字
-
hash表就派上用场了,将table中的键转化为字符串,
-
如果直接是数字就更好,不用转换,
-
一旦是字符串就用charCodeAt方法转化为ASCALl码
-
,防止hash值过大用取余
-
hashCode(key){const tablekey=this.tostrFn(key)for(let i=0;i<tablekey.length;i++){hash+=tablekey.charCodeAt(i)}return hash%37}//putset (key,value){if(key!=null&&value!=null){const position= hashCode(key)this.table[position]=ValuePair(key,value)return true}return false}
-
其他方法——get remove
-
get(key){ const ValuePair=this.table[this.hashCode(key)] return ValuePair==null?undefined:ValuePair}remove(key){const deletePair=this.table[hash]const hash=this.hashCode(key)if(deletePair!==null){delete this.table[hash]return true}return false}
-
ES6中的map
-
const obj = {q: 235}; map.set(obj, 'aaaaa');
在这段代码中,
obj
是一个常量,引用了一个对象。当你调用map.set(obj, 'aaaaa')
时,你是将obj
这个引用作为键设置到了Map
中。如果你尝试直接使用字面量对象作为键,如下所示:
map.set({q: 123}, 'bbbbb');
这里的问题在于,每次你使用
{q: 123}
这样的对象字面量时,你实际上是在创建一个全新的对象。在JavaScript中,对象是通过引用来比较的,而不是通过值。因此,即使两个对象字面量看起来相同(例如{q: 123}
和另一个{q: 123}
),它们却是不同的引用,因此被视为不同的键。总结来说,不能直接把{q: 123}
传入map
作为键,是因为每次这样写都会创建一个全新的对象,而Map
是通过引用来识别键的。 -
wakemap——对象只能是键
-
有垃圾回收机制,和map不同,一旦object被null,objet就不会占内存,也就不会被遍历
- 删除节点后,应该减少链表的节点计数