是对象的深度计算吗?掉进死胡同了

新手上路,请多包涵

// 实现一个setter方法,例如
let setter = function (content, key, value) {

// 实现方法

}
let n = {

a: {
    b: {
        c: { d: 1 },
        bx: { y: 1 }
    },
    ax: { y: 1 }
}

}
// 修改值
setter(n, 'a.b.c.d', 3);
console.log(n.a.b.c.d); // 打印 3
setter(n, 'a.b.bx', 1);
console.log(n.b.bx); // 打印 1

阅读 1.9k
2 个回答

简单版本,如果中间有数组下标之类的你需要参考 lodash 的实现

const setter = (content, key, value) => {
  const keys = key ? key.split('.') : ''
  
  const setProp = (c, remainedKeys, value) => {
    const currentKey = remainedKeys.shift()
    if (remainedKeys.length === 0) {
      c[currentKey] = value
    } else {
      c[currentKey] = Object.assign({}, c[currentKey])
      setProp(c[currentKey], remainedKeys, value)
    }
  }
  
  setProp(content, keys, value)
}

let n = {
  a: {
      b: {
          c: { d: 1 },
          bx: { y: 1 }
      },
      ax: { y: 1 }
    }
}

setter(n, 'a.b.c.d', 3)
console.log(n.a.b.c.d)  // 3

setter(n, 'a.b.bx', 1)
console.log(n.a.b.bx)  // 1

setter(n, 'c.d', 5)
console.log(n.c.d)  // 5

大概的实现了一下,数字索引那没有判断索引是否有0开头的情况,可以多加个正则判断一下/[0-9]|[1-9]d*/、以及其它的字段是否符合规范需要另加验证。
简单思路就是找到里面的.和[]取属性操作符,然后拼接成一个递归赋值的函数。

const setter = (content, key, value, reRoot = false) => {
    // reRoot设置为true可能会重写原对象,需要获取函数返回值
    const rule = /(\.|\[(\d+|(['"])((?:(?!\3)[^\\]|\\.)*)\3)])/g;
    const fields = [];
    const isArrayOrObject = (obj) =>
        Array.isArray(obj) ||
        Object.prototype.toString.call(obj).slice(8, -1) === "Object";
    let match = null;
    let curIndex = 0;
    const addField = (startIndex, endIndex) => {
        if (endIndex > startIndex) {
            const fieldName = key.slice(startIndex, endIndex);
            fields.push({
                value: fieldName,
                quote: /^\d+$/.test(fieldName) ? "" : '"',
            });
        }
    };
    while ((match = rule.exec(key)) !== null) {
        const [all, _, numKey, quote, strKey] = match;
        const {
            index
        } = match;
        const matchedLen = all.length;
        addField(curIndex, index);
        // matchedLen === 1表示匹配了.操作符
        if (matchedLen > 1) {
            fields.push({
                value: strKey !== undefined ? strKey : numKey,
                quote: quote || "",
            });
        }
        curIndex = index + matchedLen;
    }
    // 最后一个属性值、如果有的话
    addField(curIndex, key.length);
    // 下面开始拼接函数
    const target = "__RESULT__";
    const fnBody = [];
    const lastExp = fields.reduce((exp, field) => {
        const {
            quote,
            value
        } = field;
        fnBody.push(
            `${exp} = isArrayOrObject(${exp}) ? ${exp} : ${quote ? "{}" : "[]"}`
        );
        return `${exp}[${quote}${value}${quote}]`;
    }, target);
    fnBody.push(`${lastExp} = ${value}`);
    fnBody.push(`return ${target}`);
    return new Function(
          [target, "isArrayOrObject"].join(","),
        `try{${(reRoot ? fnBody : fnBody.slice(1)).join(
            ";"
          )}}catch(e){console.log(e);}`
    )(content, isArrayOrObject);
};
let obj = {};
setter(obj, "a.b.c", 1);
console.log(obj.a.b.c); // 输出1
setter(obj, "a.0", 5); // 也可以直接用点操作符截数字索引
console.log(obj.a[0]); // 输出5
setter(obj, "a[1]", 2);
console.log(obj.a[1]); // 输出2
setter(obj, "a['b.c']", 3);
console.log(obj.a["b.c"]); // 输出3
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题