一、适用范围
传递NamePath,根据NamePath给对象的目标位置赋值,且不会影响对象中其它属性。
关于NamePath:antd组件库中的Form.Item属性,可以输入NamePath,会自动填充至对象的目标位置
场景:比如一个对象 obj , 想给对象目标位置赋值,如果你知道这个路径是固定的,那当然可以直接通过点语法 obj.a.b 一步步赋值,但是当需要赋值的路径是动态生成的时候,就不能通过点语法了,需要根据用户传入的路径来进行动态赋值,比如一会想给 obj.a.b赋值,一会想给 obj.c.d[2].e赋值。
二、使用示例
三、代码及原理
原理已经在下方注释中写的很详细了,主要是利用数组的reduce迭代方法,把指针一步步迭代到目标位置,然后赋值
当path中的路径在obj中存在时,将会直接沿着这条线往下走,当路径不存在时,将会生成新的对象或数组,再继续走下去
/**
* 根据NamePath,将对象中指定位置的值赋值
* @param {object} obj 要被操作的对象
* @param {(string | number)[] | string} path 路径数组
* @param {*} value 要赋值的数据
* @returns {object} 返回修改后的对象
*/
const setObjByNamePath = (obj, path, value) => {
if (Object(obj) !== obj) return obj; // 当传入的obj不是对象时
// 如果path不是数组,则从字符串路径转数组
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
//迭代path,在obj上沿着path一步步往下,找到目标位置,targetPoint是迭代后,目标位置的上一级指针
let targetPoint = path.slice(0, -1).reduce((lastObj, nowKey, i) => {//slice(0, -1)去掉了最后一个,迭代除最后一个以外的所有内容,每次都返回一个对象/数组的指针给下一个元素调用
return Object(lastObj[nowKey]) === lastObj[nowKey] // Object(lastObj[nowKey])构造这个键值的对象,如果和原本的不一样说明key不存在
? lastObj[nowKey]// 是的话,key存在,那么就把当前的对象继续传递下去
// 不存在的话,就需要创建这个key,下面判断要新建的是对象还是数组
: lastObj[nowKey] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] //下一个键是数组索引(数字)的话 注:>> 0是转数字,如果不是纯数字就会变成0, +path[i + 1]也是转数字,但是不是纯数字也可以转
? [] //是的话,就新建一个数组,防止下面使用索引报错
: {}// 否的话,就新建一个对象
}, obj);//这里的obj是初始值(初始的顶层对象)
//到最后,把值分配
targetPoint[path[path.length - 1]] = value; //最后将值分配给最后一个键
return obj; //返回修改后的对象的地址,有可能有人操作会用到
};
//测试用例 1 //指定的路径存在时,会直接赋值
let testObj = {
a:{
b: []
},
ccc: 2
}
setObjByNamePath(testObj , ["a","b", 2] , 666) //在testObj.a.b[2]的位置赋值666
console.log(testObj)
//测试用例 2 //指定的路径不存在时,会创建这个路径
let testObj2 = {}
setObjByNamePath(testObj2 , ["a",1, "b"] , 777) //在testObj2.a[1].b的位置赋值777
console.log(testObj2)