抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

在对象中获取或设置一个层级很深的属性,通常要先判断是否存在才能进行下一层的遍历,这将使得我们的代码臃肿不堪。

痛点

现有一列同步数据

const obj = {
    client: {
        user: {
            username: "sunzehui",
            age: "20"
        }
    }
}

此时我们访问客户端(client)的用户名(username)数据一般写法:

const username = obj && obj.client && obj.client.user && obj.client.user.username;

每一层都需要判断属性是否存在,不存在(undefined)的话再访问其属性便会报错

TypeError: Cannot read properties of undefined (reading 'xxx')

同样,在设置对象的深层属性的时候,也有这种情况:

const user = obj && obj.client && obj.client.user;
if (user){
  user.sex = "man"
}

解决方案

取值器

实现同步变量迭代取值器,一层层取值,取不到的时候返回传入的默认值,未传入即undefined。

Object.prototype.Getter = function (path, defaultValue) {
  	// 将路径转换成数组便于操作
    const keys = path.split(".")
    let result = this[keys[0]];

    for (let i = 1; i < keys.length; i++) {
      	// 如果上一次的属性不存在,则返回传入的默认值
        if (result === void 0) {
            return defaultValue;
        // 如果存在,则继续向下寻找
        } else {
            result = result[keys[i]]
        }
    }
    return result || defaultValue;
}

// 测试
obj.Getter("client.user.username")	// sunzehui
obj.Getter("client.user.sex", "man")	// man

赋值器

每次迭代时,如不存在该属性,则创建一份对象(可能会覆盖,也可能继续迭代下一层)

Object.prototype.Setter = function (path, value) {
    const keys = path.split(".")
    let result = this;
    for (var i = 0; i < keys.length - 1; i++) {
        if (result[keys[i]] === void 0) {
            result[keys[i]] = {}
        }
      	// 当第 i 层不是对象的时候,此时不能进行赋值,抛出异常
        if (!(result[keys[i]] instanceof Object)) {
            throw new Error("can't set property on no-object at " +
                keys.slice(0, i + 1).join("."));
        }
        result = result[keys[i]];
    }

    return result[keys[i]] = value;
}

// 测试:
obj.Setter("client.user.sex", "man"); // obj.client.user.sex -> "man"
obj.Setter("client.user.sex.type", "human") 
// Error: can't set property on no-object at client.user.sex

tip:这里用var是为了 for 循环外面可以用到 i 变量,利用了 var 声明的变量不带块级作用域的特性。

总结

写的很简单,没有实现数组的访问,赋值器那边 instanceof Object 写的也很潦草

其实 lodash 这个库已经实现了 _.get() 和 _.set() 方法

lodash.get | Lodash 中文文档 | Lodash 中文网 (lodashjs.com)

lodash.set | Lodash 中文文档 | Lodash 中文网 (lodashjs.com)

评论