SOURCE

function isKeyValueObject(value) {
    return _.isPlainObject(value);
}

function isNumberLike(value) {
    return _.isNumber(value) || !Number.isNaN(Number(value));
}

function isArray(value) {
    return _.isArray(value);
}

function isNil(value) {
    return value == null;
}

function shouldBeArray(current, parent, key) {
  // already array or parent array -> need array
  if (isArray(current) || isArray(parent)) {
    return true;
  }

  // number key check
  if (typeof key === 'number') {
    // {} → object
    if (isKeyValueObject(current)) {
      return false;
    }

    // otherwise → default array
    return true;
  }

  return false;
}

function createContainer(current, parent, key) {
  // create a value container, need check container need to be an array or object
  return shouldBeArray(current, parent, key) ? [] : {};
}

function updateValueInSource(
  source,
  key,
  value
) {
  // default object type
  let freshSource = isArray(source) ? [...source] : isKeyValueObject(source) ? { ...source } : {};
  freshSource[key] = value;
  return freshSource;
}

function setInnerSource($source, path, value) {
  // no path, replaced with value
  if (path.length === 0) {
    return value;
  }

  const parentPath = path.slice(0, path.length - 1);
  const currentPath = path.slice(-1)[0];
  
  // only support array & object, default is object
  const source = isArray($source) ? [...$source] : isKeyValueObject($source) ? { ...$source } : {};
  
  // if update parentValue, need this ptr to update value
  const stack = [];
  let parentValue = source;
  
  // collect path
  parentPath.forEach(path => {
    const nextParentValue = parentValue[path];
    let next;

    if (isArray(nextParentValue)) {
      next = [...nextParentValue];
    } else if (isKeyValueObject(nextParentValue)) {
      next = { ...nextParentValue };
    } else {
      next = createContainer(nextParentValue, parentValue, path);
    }

    // push path trace in stack, then rebuilt obj by stack from end to start
    stack.push({
      container: parentValue,
      key: path,
    });
    parentValue = next;
  });
  
  let currentValue;
  if (isArray(parentValue)) {
    if (!isNumberLike(currentPath)) {
      console.debug('invalid path');
      return source;
    }
    const clone = [...parentValue];
    clone[currentPath] = value;
    currentValue = clone;
  } else if (isKeyValueObject(parentValue)) {
    currentValue = {
      ...parentValue,
      [currentPath]: value,
    };
  }
  
  let nextValue = currentValue;
  // back DFS
  for (let i = stack.length - 1; i >= 0; i--) {
    const { container, key } = stack[i];

    const prev = container[key];

    if (Object.is(prev, nextValue)) {
      nextValue = container;
      continue;
    }

    const clone = isArray(container) ? [...container] : { ...container };

    clone[key] = nextValue;
    nextValue = clone;
  }

  return nextValue;
}

const a = {
    name: 'ddd',
    list: [{ index: 1 }, { index: 2 }, { index: 3 }],
    obj: {
        age: 12,
        location: 'CN',
        times: [9, 18],
        obj: {
            age: 26,
            list: [1, 2, 3],
        }
    },
};

// √
const b = setInnerSource(a, ['name'], 'ccc');
console.log(b);
console.log(a === b);
console.log(a.obj === b.obj);
console.log(a.list === b.list);
console.log(a.list[1] === b.list[1]);
// √
const c = setInnerSource(a, ['list', 1], { index: 22 });
// console.log(c);
// console.log(a === c);
// console.log(a.obj === c.obj);
// console.log(a.list === c.list);
// console.log(a.list[0] === c.list[0]);
// console.log(a.list[1] === c.list[1]);
// √
const d = setInnerSource(a, ['obj', 'location'], 'ZH');
// console.log(d);
// console.log(a === d);
// console.log(a.obj === d.obj);
// console.log(a.list === d.list);
// console.log(a.obj.times === d.obj.times);
// console.log(a.obj.obj === d.obj.obj);
// console.log(a.list[0] === d.list[0]);
// √
const e = setInnerSource(a, ['list', '6'], 'CN');
// console.log(e);
// console.log(e.list);
// console.log(e.list[6]);
// console.log(a === e);
// console.log(a.obj === e.obj);
// console.log(a.list === e.list);
// console.log(a.obj.times === e.obj.times);
// console.log(a.obj.obj === e.obj.obj);
// console.log(a.list[0] === e.list[0]);
const f = setInnerSource(a, ['obj', 'obj', 'age'], 30);
// console.log(f);
// console.log(f.obj.obj);
// console.log(a === f);
// console.log(a.obj === f.obj);
// console.log(a.list === f.list);
// console.log(a.obj.times === f.obj.times);
// console.log(a.obj.obj === f.obj.obj);
// console.log(a.list[0] === f.list[0]);
// console.log(a.obj.obj.list === f.obj.obj.list);
console 命令行工具 X clear

                    
>
console