作为数组如何使用对象?我们知道对象和数组在JS中的操作方式不同,但是我们可以通过封装的方式给对象加上一层包装器,这样就可以像数组一样使用了。我们主要使用Object.keys()、Object.values()、Object.entries()、Proxy。Object.keys看MDN上的解释:Object.keys()方法会返回一个由给定对象自身的可枚举属性组成的数组,数组中属性名的顺序和返回对象的正常循环遍历同样的顺序。即Object.keys可以得到对象的所有属性名,生成一个数组。varobj={a:0,b:1,c:2};console.log(Object.keys(obj));//console:['a','b','c']Object.values看一看MDN上的解释:Object.values()方法返回给定对象本身所有可枚举属性值的数组,值的顺序与使用for...in循环相同(区别在于for-in循环For例如,原型链中的属性)。Object.values()返回一个数组,其元素是在对象上找到的可枚举属性值。varobj={foo:'bar',baz:42};console.log(Object.values(obj));//['bar',42]Object.entries看MDN上的解释:Object.entries()方法返回给定对象自身可枚举属性的键值对数组,排列顺序与使用for...in循环遍历对象时返回的顺序相同(区别在于for-in循环还枚举原型链属性)。Object.entries()返回一个数组,其元素是属性名称和属性值的数组。constobj={foo:'bar',baz:42};console.log(Object.entries(obj));//[['foo','bar'],['baz',42]]ProxyProxy是JS最新的对象代理方法,用于创建一个对象的代理对象,从而实现对基本操作(如属性查找、赋值、枚举、函数调用等)的拦截和定制。代理可以用来封装对象的原始操作。当进行对象操作时,会被Proxy处理,这样我们就可以实现数组操作命令了。基本获取示例consthandler={get:function(obj,prop){returnpropinobj?对象[道具]:37;}}constp=newProxy({},handler);p.a=1;p.b=undefined;console.log(p.a,p.b)console.log('c'inp,p.c)上例中,当对象中不存在属性名时,默认返回值为37,无操作转发代理使用Proxy对原对象进行包装生成一个代理对象p,对代理对象的操作将转发给原对象。lettarget={};letp=newProxy(target,{});p.a=37;//操作转发到目标console.log(target.a);//37.操作已正确转发我们将实现以下函数:forEach、map、filter、reduce、slice、find、findKey、includes、keyOf、lastKeyOf。实现数组函数forEach数组中的forEach函数定义:arr.forEach(callback(currentValue[,index[,array]])[,thisArg])。数组中的forEach需要传入一个函数。函数的第一个参数是当前操作的元素值,第二个参数是当前操作的元素索引,第三个参数是被操作的对象。对于对象,我们将参数设置为:currentValue、key、target。我们可以使用Object.keys来遍历对象。Object.keys(target).forEach(key=>callback(target[key],key,target))这里需要target和callback参数,我们通过函数封装。functionforEach(target,callback){Object.keys(target).forEach(key=>callback(target[key],key,target))}所以我们可以这样调用它:consta={a:1,b:2、c:3}forEach(a,(v,k)=>console.log(`${k}-${v}`))//a-1//b-2//c-3封装通过代理:consthandler={get:function(obj,prop){returnforEach(obj)}}constp=newProxy(a,handler)p.forEach((v,k)=>console.log(`${k}-${v}`))当然上面的方法不行。我们主要看最后一句。它的执行方式和数组的forEach完全一样。当我们调用Proxy封装的对象获取数据时,会调用get函数,第一个参数为原始对象,第二个参数为属性名-forEach,这里需要修改我们的forEach函数。首先p.forEach的参数是一个函数,所以我们代理对象的返回值需要接收一个函数作为参数,所以修改如下:functionforEach(target){return(callback)=>Object.keys(target).forEach(key=>callback(target[key],key,target))}所以完成代码是:functionforEach(target){return(callback)=>Object.keys(target).forEach(key=>callback(target[key],key,target))}consthandler={get:function(obj,prop){returnforEach(obj)}}consta={a:1,b:2,c:3}constp=newProxy(a,handler)p.forEach((v,k)=>console.log(`${k}-${v}`))//a-1//b-2//c-3我们要把上面的代码打包成一个模块供外部使用:consttoKeyedArray=(obj)=>{constmethods={forEach(target){return(callback)=>Object.keys(target).forEach(key=>callback(target[key],key,target));}}常量方法键=对象。keys(methods)consthandler={get(target,prop){if(methodKeys.includes(prop)){returnmethods[prop](target)}返回反射。get(...arguments)}}返回新的Proxy(obj,handler)}consta={a:1,b:2,c:3}constp=toKeyedArray(a)p.forEach((v,k)=>console.log(`${k}-${v}`))以上就是forEach的实现和封装,其他功能的实现类似全部源代码如下:consttoKeyedArray=(obj)=>{constmethods={forEach(target){return(callback)=>Object.keys(target).forEach(key=>callback(target[key],key,target));},map(target){return(callback)=>Object.keys(target).map(key=>callback(target[key],key,target));},reduce(target){return(callback,accumulator)=>Object.keys(target).reduce((acc,key)=>callback(acc,target[key],key,target),accumulator);},forEach(target){returncallback=>Object.keys(target).forEach(key=>callback(target[key],key,target));},filter(target){returncallback=>Object.keys(target).reduce((acc,key)=>{if(callback(target[key],key,target))acc[key]=target[key];返回acc;},{});},slice(target){return(start,end)=>Object.values(target).slice(start,end);},find(target){returncallback=>{return(Object.entries(target).find(([key,value])=>callback(value,key,target))||[])[0];};},findKey(target){returncallback=>Object.keys(target).find(key=>callback(target[key],key,target));},includes(target){returnval=>Object.values(target).includes(val);},keyOf(target){返回值=>Object.keys(target).find(key=>target[key]===value)||无效的;},lastKeyOf(target){返回值=>Object.keys(target).reverse().find(key=>target[key]===value)||无效的;}}constmethodKeys=Object.keys(methods)consthandler={get(target,prop){if(methodKeys.includes(prop)){返回方法[prop](target)}const[keys,values]=[Object.keys(目标),Object.values(目标)];if(prop==='长度')返回keys.length;if(prop==='keys')返回键;if(prop==='values')返回值;if(prop===Symbol.iterator)returnfunction*(){for(valueofvalues)yieldvalue;返回;};返回Reflect.get(...arguments)}}returnnewProxy(obj,handler)}constx=toKeyedArray({a:'A',b:'B'});x.a;//'A'x.keys;//['a','b']x.values;//['A','B'][...x];//['A','B']x.length;//2//插入值x.c='c';//x={a:'A',b:'B',c:'c'}x.length;//3//数组方法x.forEach((v,i)=>console.log(`${i}:${v}`));//日志:'a:A','b:B','c:c'x.map((v,i)=>i+v);//['aA','bB,'cc]x.filter((v,i)=>v!=='B');//{a:'A',c:'c'}x.reduce((a,v,i)=>({...a,[v]:i}),{});//{A:'a',B:'b',c:'c'}x.slice(0,2);//['A','B']x.slice(-1);//['c']x.find((v,i)=>v===i);//'c'x.findKey((v,i)=>v==='B');//'b'x.includes('c');//truex.includes('d');//false.keyOf('B');//'b'x.keyOf('a');//nullx.lastKeyOf('c');//'C'
