V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sdjl
V2EX  ›  程序员

5 个前端 JS 函数,只为了优雅解决 a.b.c.d = 1 问题

  •  
  •   sdjl · 15 天前 · 4916 次点击

    前言

    最近更新了我的系列文章,其中有一部分是关于 JavaScript 语言中“点表示法”的使用

    文章中有 5 个工具函数是纯JavaScript的,我觉得不仅仅是小程序项目用得上,其他前端 JS 项目也应该用得上

    我把文章中的这一部分单独整理出来,给需要写前端代码的朋友参考参考

    你可以直接在 Github 项目 中的前端 utils.js 文件中找到这 5 个工具函数。

    下面是文章节选,完整的文章列表可以在 Github 项目 中查看。

    点表示法a.b.c

    用点表示法给对象赋值:putValue函数

    想象一下,如果a是一个对象,你要给a.b.c.d赋值为 1 ,你会这样写?

    let a // 通过某种方式获得的对象
    if (!a.b) {
      a.b = {}
    }
    if (!a.b.c) {
      a.b.c = {}
    }
    a.b.c.d = 1
    

    拜托,大学生才这样写。为此,我们引入了putValue函数:

    utils.putValue(a, 'b.c.d', 1)
    

    putValue函数会自动创建a.ba.b.c中间对象,它的声明如下:

    /**
    * 向对象中按照路径赋值,如果路径上的中间对象不存在,则自动创建。
    * @param {Object} obj - 目标对象。
    * @param {string} key - 属性路径,支持'a.b.c'形式。
    * @param {*} value - 要设置的值。
    * @param {Object} [options={}] - 可选参数。
    *   - {boolean} remove_undefined - 如果为 true 且 value 为 undefined ,则删除该属性。
    * @throws {Error} 如果 obj 为 null 或 undefined ,或路径不合法(如中间非对象)则抛出异常。
    */
    putValue(obj, key, value, {remove_undefined = true} = {}){
      // ...
    }
    

    使用这个函数有两个地方要注意,一个是如果路径上的中间对象不是对象,会抛出异常。例如a.b=2,这里b不是对象,此时会抛出异常。

    另一个是如果valueundefined,则会删除该属性。例如下面的代码:

    utils.putValue(a, 'b.c.d', undefined)
    console.log(a) // {b: {c: {}}},putValue 会自动创建中间对象,但不会自动删除空对象
    

    但若你真想赋值为undefined,可设置remove_undefined参数为false

    读取对象属性值:pickValue函数

    对应的,如果要获取a.b.c.d的值,可以使用pickValue函数:

    utils.putValue(a, 'b.c.d', 1)
    const value = utils.pickValue(a, 'b.c.d')
    console.log(value) // 1
    

    当然你也可以直接使用javascript的原生语法:

    const value = a.b?.c?.d
    

    这两种写法,当中间路径不存在时,均会返回undefined

    a.b?.c?.d这种写法是硬编码,而在实际开发中路径可能是动态的。例如用户要修改一个配置项,这个配置可能是user_config.font.size也可能是user_config.page.color.background,如果使用硬编码的方式,可能会写出这样的代码:

    if (key === 'font.size') {
      user_config.font.size = value
    } else if (key === 'page.color.background') {
      user_config.page.color.background = value
    }
    // 更多的 if 语句...
    

    这样写显然不够优雅,看看putValuepickValue的组合用法。

    // 写入用户配置:
    utils.putValue(user_config, key, value)
    
    // 读取用户配置
    const value = utils.pickValue(user_config, key)
    

    不管key怎么变,一句话搞定,感觉一下子和大学生拉开差距了是吧?

    向数组末尾添加元素:pushValue函数

    在实际开发中,常有向数组末尾添加数据的需求。例如记录用户最近的评论,此时你可以使用pushValue函数:

    const user_data = {} // 用户数据
    let comment = {content: '顶'} // 用户的评论
    
    utils.pushValue(user_data, `articles.recent_comments`, comment)
    
    console.log(user_data) // {articles: {recent_comments: [{content: '顶'}]}}
    

    在上面代码中,pushValue函数先是自动创建了user_data.articles.recent_comments数组,然后把comment添加到数组末尾。pushValue函数的声明如下:

    /**
     * 将值推入对象指定路径的数组中,若路径或数组不存在则自动创建。
     * @param {Object} obj - 目标对象。
     * @param {string} key - 数组属性的路径,支持'a.b.c'形式。
     * @param {*} value - 要推入的值。
     * @throws {Error} 如果路径不是数组,则抛出异常。
     */
    pushValue(obj, key, value){
      // ...
    }
    

    再次调用此函数,数组中就会有两个评论:

    comment = {content: '再顶'}
    
    utils.pushValue(user_data, `articles.recent_comments`, comment)
    
    console.log(user_data) // {articles: {recent_comments: [{content: '顶'}, {content: '再顶'}]}}
    

    向对象中添加多个属性:putObj函数

    前面我们使用putValue函数向obj对象写入了一个属性值,但如果你要写入很多个(例如 100 个)属性值,你可能会使用for循环:

    let user_config = {}
    let new_config_keys // 100 个新的配置项(数组)
    let new_config_values // 对应的 100 个值(数组)
    
    for (let i = 0; i < new_config_keys.length; i++) {
      utils.putValue(user_config, new_config_keys[i], new_config_values[i])
    }
    

    这样写没问题,但在实战中,你拿到的用户配置往往不是数组的形式,而很可能是一个对象,例如:

    let new_config = {
      font: {
        size: 16,
        'family.first': 'Arial',
        'family.second': 'sans-serif',
      },
      'page.color.background': '#fff',
      // ...
    }
    
    // 你对拿到的配置数据又进一步处理
    new_config.update_time = new Date()
    

    这种情况下你可以使用putObj函数一次性写入多个属性值:

    utils.putObj(user_config, new_config)
    
    console.log(user_config)
    
    /* 输出如下:
    {
      font: {
        size: 16,
        family: {
          first: 'Arial',
          second: 'sans-serif'
        }
      },
      page: {
        color: {
          background: '#fff'
        }
      },
      update_time: '...',
    }
    */
    

    注意putObj会自动处理上面new_config变量中各种路径的写法putObj函数的声明如下:

    /**
     * 将一个对象的所有属性按路径添加到另一个对象中。
     * @param {Object} obj - 目标对象。
     * @param {Object} obj_value - 要添加的属性对象,键支持'a.b.c'形式的路径。
     * @param {Object} [options={}] - 可选参数。
     *   - {boolean} remove_undefined - 如果为 true 且 value 为 undefined ,则删除该属性。
     * @throws {Error} 如果 obj 为 null 或 undefined ,或路径不合法(如中间非对象)则抛出异常。
     *
     * 注意
     *   若 obj_value 中出现重复路径,则后者会覆盖前者。
     *   如 obj_value = {a: {b: 1}, 'a.b': 2},则结果为 {a: {b: 2}}
     */
    putObj(obj, obj_value, { remove_undefined = true} = {}) {
      // ...
    }
    

    从对象中获取多个属性:pickObj函数

    同样的,我们可以一次性读取多个对象的属性值。例如虽然小程序中的用户配置非常复杂,但当前页面仅关注背景颜色、字体大小等少量配置项,你可以这样使用pickObj函数:

    let user_config // 某个用户的所有配置
    
    // 本页面需要关注的配置
    const keys = ['page.color.background', 'font.size', 'font.family']
    
    // 获取当前页面需要的配置
    const curr_config = utils.pickObj(user_config, keys)
    
    console.log(curr_config)
    
    /* 输出如下:
    {
      'page.color.background': '#fff',
      'font.size': 16,
      'font.family': {
        first: 'Arial',
        second: 'sans-serif'
      }
    }
    */
    
    console.log(curr_config.font) // undefined
    

    注意,传给pickObj函数的第二个参数是一个字符串数组,而不是对象。并且,pickObj返回的对象中,属性值不是以curr_config.font.size这样的形式返回,而是返回curr_config['font.size']

    当然,如果你想要curr_config.font.size这样的形式,可用putObj转换一下:

    let obj_config = utils.putObj({}, curr_config)
    
    console.log(obj_config.font.size) // 16
    

    点表示法在微信小程序中实战演示

    为什么要设计这几个函数?为什么要支持config.a.b.cconfig['a.b.c']两种写法混用?为什么传给putObj的第二个参数是对象,而传给pickObj的第二个参数是字符串数组?为什么pickObj返回的对象属性值不是config.a.b.c这样的形式,而是config['a.b.c']

    因为这样设计符合实战需求,一句话解释就是:“这样好用”

    下面我们通过几个案例来演示这些函数在实战中的应用。

    在 js 中设置用户配置

    假设用户首次打开小程序,你需要设置用户默认字体大小为 16 ,背景颜色为白色。可以这样写:

    let user_config = {}
    utils.putValue(user_config, 'font.size', 16)
    utils.putValue(user_config, 'page.color.background', '#fff')
    

    使用putValue设置后,你想修改字体大小和背景颜色?可以这样写:

    user_config.font.size = 18
    user_config.page.color.background = '#000'
    

    在 wxml 中实现修改用户配置

    你可能会在 wxml 页面中实现多个配置项的修改,并且使用同一个函数来处理。这时你可以这样写:

    <button bind:tap="changeConfig" data-key="font.size" value="16" >
    <button bind:tap="changeConfig" data-key="page.color.background" value="#fff">
    
    changeConfig(e){
      const { user_config } = this.data
      const { key, value } = e.currentTarget.dataset
    
      // 从 wxml 中获得点表示法的 key 字符串,直接调用 putValue 函数
      utils.putValue(user_config, key, value)
    
      // 修改背景色时顺便改一下字体颜色(两种写法混用)
      if (key === 'page.color.background' && value === '#fff') {
        user_config.page.color.font_color = '#000'
      }
    
      // 记录最近修改时间
      user_config.update_time = new Date()
    }
    

    在 wxml 中使用用户配置

    要在页面中使用page.color.backgroundpage.color.font_color的值,实现根据用户配置显示不同的颜色,可以这样写:

    <view style="background-color: {{color.background}}">
      <text style="color: {{color.font_color}}">
          Hello, WxMpCloudBooster!
      </text>
    </view>
    
    onLoad(){
      const { user_config } = this.data
      const color = utils.pickValue(user_config, 'page.color')
      this.setData({color})
    }
    

    你看,我们传递给pickValuekey根据实际需求可长可短。

    初始化默认的用户配置

    你希望为每个用户设置一个默认的用户配置,并且你想用常规方式写(不使用点表示法)。可以这样:

    // 默认配置
    const DEFAULT_CONFIG = {
      font: {
        size: 16,
      },
      page: {
        color: {
          background: '#fff',
          font_color: '#000'
        }
      },
      // 这里也可以使用点表示法 a.b.c ,但你不想这样写...
    }
    
    App({
      initConfig(){
        let { user_config } = this.data
        utils.putObj(user_config, DEFAULT_CONFIG) // user_config 的其他值会被保留
        // 保存用户配置...
      }
    })
    

    注意,上面代码中user_config可能会有其他没有出现在DEFAULT_CONFIG中的配置项,这些配置项会被保留。

    记录用户最近发表的内容

    假如你已经实现了“记录用户最近发布的评论”功能,代码如下:

    <button bind:tap="append" data-key="articles.recent_comments" data-prop="comment" >
    

    当用户点击这个按钮时,假设this.data中已经有一个comment对象,你可以这样添加评论:

    append(e){
      const { user_data } = this.data
      const { key, prop } = e.currentTarget.dataset
      const value = this.data[prop] // prop === "comment"
    
      utils.pushValue(user_data, key, value) // 注意这里用的是 push
    }
    

    上面这个append函数会把this.data.comment对象添加到user_data.articles.recent_comments数组的末尾。

    然后,此时你希望再增加一个按钮,可以把最近的点赞数据this.data.like添加到user_data.articles.recent_likes数组的末尾,那么只需一句:

    <button bind:tap="append" data-key="articles.recent_likes" data-prop="like" >
    

    完成了,你不需要修改append函数,只需要给data-keydata-prop属性设置不同的值即可。

    可见,点表示法很大的目的是为了在wxml中可以方便地指定路径,并在js中方便地处理这些路径。

    在页面中修改多个配置项

    假设你有一个修改用户配置项的页面,wxml代码如下:

    <!-- 注意这里有一个 for 循环 -->
    <view wx:for="{{configs}}">
      配置名称:{{item.title}}
      当前值:{{item.value}}
      输入新值:<input type="text" />
      点击修改:<button bind:tap="changeConfig"/>
    </view>
    

    上面代码使用了for循环,configs变量中有多少个值,就会显示多少个配置项。

    为了实现在用户打开页面时显示的是用户的当前值(而不是默认值),你还需要从user_config中读取当前用户的配置值。

    代码样例如下:

    // 代码中写死了需要修改的配置项以及默认值
    configs = [
      {title: '字体大小', key: 'font.size', value: 16},
      {title: '背景颜色', key: 'page.color.background', value: '#fff'},
      {title: '字体颜色', key: 'page.color.font_color', value: '#000'},
    ]
    
    // 读取当前用户的配置值
    const uc_obj = utils.pickObj(user_config, configs.map(item => item.key))
    
    // 注意,这里的 uc_obj 是 uc_obj['font.size'] 这样的形式,而不是 uc_obj.font.size
    
    // 用户当前值覆盖默认值
    configs.forEach(item => {
      if (uc_obj[item.key] !== undefined) {
        item.value = uc_obj[item.key]
      }
    })
    
    this.setData({configs}) // 传给 wxml 页面显示
    

    这样你就实现了修改多个配置项的页面,用户打开页面时显示的是用户当前的配置值。

    提问:假设我们坚决不使用点表示法,且要实现上面这些功能,你要如何设计才能如此简单、高效?

    让你的函数也支持点表示法

    好了,目前我们花了不少篇幅介绍点表示法,这是因为后面我们会介绍更多的工具函数,而这些工具函数都支持点表示法的调用方式

    当你编写自己的工具函数时,你可以调用putValuepickValuepushValueputObjpickObj这 5 个函数,轻松地让你的工具函数也支持点表示法。如果你不知道如何实现,可以参考utils.js中其他函数的代码。

    (文章节选完,如果你感兴趣的话可以看看 Github 项目

    68 条回复    2024-09-03 17:51:31 +08:00
    qwq11
        1
    qwq11  
       15 天前   ❤️ 6
    1. 有个项目叫 TypeScript ,专门解决这种问题的,应该挺小众的,不然你不应该不知道
    sdjl
        2
    sdjl  
    OP
       15 天前
    @qwq11 我不喜欢用强类型语言,感觉就好像绑着手脚跳舞
    gouflv
        3
    gouflv  
       15 天前 via iPhone   ❤️ 3
    感觉回到了 10 年前
    sdjl
        4
    sdjl  
    OP
       15 天前 via iPhone
    @gouflv 那 10 年后的今天应该什么样?
    lisongeee
        5
    lisongeee  
       15 天前
    > 假设我们坚决不使用点表示法,且要实现上面这些功能,你要如何设计才能如此简单、高效?

    修改 ast 实现就行,指定一个带有特定 Identifier 的 CallExpression 如

    __safe__(a.b.c = 1)

    将这个 CallExpression 修改为类似 if(!a){a={b:{c:1}}}else if(!a.b){a.b={c:1}}else{a.b.c=1} 的 IfStatement

    不过我看大佬你写的都是原生框架,估计懒得弄这种编译插件

    另外用字符串表示 MemberExpression ,如果改变量名的时候还得一个一个改,可维护性太低(如果你乐意那当我没说)
    renmu
        6
    renmu  
       15 天前 via Android
    用 操 作 配 置 文 件 确 实 挺 方 便
    nakar
        7
    nakar  
       15 天前 via Android
    lodash 的 set 了解下?
    DOLLOR
        8
    DOLLOR  
       15 天前
    对于 a.b.c.d = 1 ,如果 a.b.c.d 可能存在 undefined ,我一般用 lodash.merge 。
    对于合并默认配置,可以用 lodash.defaultsDeep 。

    一般还是避免使用计算属性名(即`obj[expr]`),免得后期维护困难。
    favourstreet
        9
    favourstreet  
       14 天前 via Android
    lens 和 optic 了解一下?楼主你这不是重新发明 lens 吗
    sdjl
        10
    sdjl  
    OP
       14 天前 via iPhone
    @nakar

    嗯,这个主要是给小程序使用,小程序只有 2m ,尽量不引入第三方库文件。
    daysv
        11
    daysv  
       14 天前
    写的很多, 我用 loadsh
    murmur
        12
    murmur  
       14 天前   ❤️ 4
    lisxour
        13
    lisxour  
       14 天前
    你这种玩法叫 object path ,类似于 json path 的东西,几乎所有主流语言应该都有现成的库
    liuidetmks
        14
    liuidetmks  
       14 天前
    @murmur 虽然但是, 这么说有些冒犯 jser
    murmur
        15
    murmur  
       14 天前
    @liuidetmks 有啥冒犯的,我就是写前端的,比谁都知道娱乐圈
    artiga033
        17
    artiga033  
       14 天前 via Android   ❤️ 10
    恭喜你成功搞废了 ide 的查找引用/实现/重命名等功能
    sdjl
        18
    sdjl  
    OP
       14 天前
    @lisxour 肯定是有现成的,只是不满足我的需求,并且在小程序中引入一个库文件感觉没必要。
    theprimone
        19
    theprimone  
       14 天前   ❤️ 1
    我还以为怎么个优雅法呢 😦
    BugCry
        20
    BugCry  
       14 天前 via Android
    不理解,但支持
    miaotaizi
        21
    miaotaizi  
       14 天前
    @sdjl 我要没记错 lodash 这种库好像可以 单个函数导入?
    hoythan
        22
    hoythan  
       14 天前
    @sdjl ...用你的 utils 不也是库吗,甚至人家的库才几行代码
    sdjl
        23
    sdjl  
    OP
       14 天前
    @hoythan 我写的 utils 库是给小程序开发者用的,在这里发文章只是探讨一下编写用户配置相关代码的一种思路。
    sdjl
        24
    sdjl  
    OP
       14 天前
    @renmu “用 操 作 配 置 文 件 确 实 挺 方 便”

    你把文章看完了?
    sdjl
        25
    sdjl  
    OP
       14 天前
    @lisxour “你这种玩法叫 object path ,类似于 json path 的东西,几乎所有主流语言应该都有现成的库”

    是的,但是实现的功能又不完全一样。我的 utils 里面有许多其他函数是基于这 5 个函数实现的。
    sdjl
        26
    sdjl  
    OP
       14 天前
    @artiga033 “恭喜你成功搞废了 ide 的查找引用/实现/重命名等功能”

    怎么说? 不是太能理解。
    lzgshsj
        27
    lzgshsj  
       14 天前
    按需引入功能都是现代工具库的标配了
    gouflv
        28
    gouflv  
       14 天前
    @murmur 更准确的说,这是小程序的圈子

    手撸工具库,拒绝强类型,这样的布道,真心让我替小程序的入门者担忧
    sdjl
        29
    sdjl  
    OP
       14 天前
    @lzgshsj “按需引入功能都是现代工具库的标配了”

    是的,后面看看怎么解决
    accelerator1
        30
    accelerator1  
       14 天前   ❤️ 1
    虽然可以自己手撸,但是 lodash-es + treeshake 肯定还是更加省心的。
    包括现在的 nodejs + ts 项目,也是编译成 esm 模块类型,就是为了 treeshake ,基础工具手撸没啥必要。
    sdjl
        31
    sdjl  
    OP
       14 天前
    @gouflv “手撸工具库,拒绝强类型”

    我指望着哪天有好心人把它改成 TS 语言的~
    sdjl
        32
    sdjl  
    OP
       14 天前
    @accelerator1 “但是 lodash-es + treeshake 肯定还是更加省心的”

    是的,你说得对,我这个库函数互相依赖程度比较高,等我想想是否有更好的办法。
    lisongeee
        33
    lisongeee  
       14 天前
    好奇除了我上面说的修改 ast 还有啥更好的方法?这种方法是对 code hint/refactor 最友好的方式

    如果你的项目无法接入编译插件或者没有构建系统支持,那确实不行
    courtier
        34
    courtier  
       14 天前 via Android   ❤️ 7
    我觉得大家不理解的主要原因是你这个具体是想解决什么问题……
    你标题是要优雅解决 a.b.c.d=1 ,那楼里的兄弟都提供了很多可以支持的现有方法
    有说 ts 能处理的,你就说不喜欢用;说 lodash 能支持,就说不要引入第三方库;说用你的库不也是引入,而且 lodash 也支持单函数导入或者干脆直接复制对应的函数代码进来,你就开始说是想探讨编写用户配置相关的思路了……
    说实话我属实没怎么看懂你具体是想要什么,想解决什么
    Torpedo
        35
    Torpedo  
       14 天前
    1.空值也是有意义的。
    2.类似函数 lodash 等都有实现。使用比较流行的公共库
    chill777
        36
    chill777  
       14 天前
    。。。多多少少有点原始人了
    tanranran
        37
    tanranran  
       14 天前
    ........

    遇到混淆的时候。。。有你哭的时候
    wzdsfl
        38
    wzdsfl  
       14 天前   ❤️ 1
    给人一种你好像懂前端但又没完全懂的感觉
    sdjl
        39
    sdjl  
    OP
       14 天前
    @lisongeee “如果你的项目无法接入编译插件或者没有构建系统支持,那确实不行”

    这个应该要支持,但也不能作为唯一的方式,因为那样会增加学习成本,后期我想想如何提供其他解决方案。
    sdjl
        40
    sdjl  
    OP
       14 天前
    @courtier “我觉得大家不理解的主要原因是你这个具体是想解决什么问题……”

    是的,标题有一定的歧义,不过发出来,看看大家怎么评价,也是学习。
    sdjl
        41
    sdjl  
    OP
       14 天前
    @courtier 这个是系列文章的一部分内容,系列文章主要是微信小程序云开发的经验分享,系列文章的目的是帮助开发新手快速上手云开发。
    sdjl
        42
    sdjl  
    OP
       14 天前
    @wzdsfl “给人一种你好像懂前端但又没完全懂的感觉”

    深刻~
    ClarkAbe
        43
    ClarkAbe  
       14 天前
    @sdjl 我不喜欢用弱类型语言,感觉就像跳舞时没有音乐,完全找不到节奏。
    sdjl
        44
    sdjl  
    OP
       14 天前
    @ClarkAbe 语言类型嘛,每个人有每个人的喜好,这个话题无法讨论出结果的,就不要讨论这个,以免帖子被进入水深火热。
    muzlin
        45
    muzlin  
       14 天前   ❤️ 1
    看见大家评论我就放心了
    ganbuliao
        46
    ganbuliao  
       14 天前
    @sdjl gpt or copilot 就是好心人可以帮你改成 TS 的
    intmax2147483647
        47
    intmax2147483647  
       14 天前   ❤️ 1
    吓死我了 看了开头就绷不住了 谁会用这种离谱的魔法赋值啊
    FishBear
        48
    FishBear  
       14 天前
    @intmax2147483647 确实 这就是魔法.... 需求千奇百怪..
    adoal
        49
    adoal  
       14 天前
    持续执行一个 operator ,但要处理中途可能为空值的情况,这算是个挺常见的需求。只不过大多数情况下是把空值一路传递下去,也就是 Maybe/Option 那种情况。
    davin
        50
    davin  
       14 天前
    这样的业务逻辑,到前端基本早就 flattern 了,或者叫服务端哥们优化数据结构
    toesbieya
        51
    toesbieya  
       14 天前 via Android
    写的没啥问题,唯一缺点是没有对应的 ts 类型声明,后续不好找使用者。能自己写的简单功能还引库,那才是有毛病
    wh1012023498
        52
    wh1012023498  
       14 天前   ❤️ 3
    给人一种刚学前端,然后解决了遇到的小问题,然后发帖分享的感觉。
    woniu7
        53
    woniu7  
       14 天前   ❤️ 1
    就点就点就点就!
    edotac
        54
    edotac  
       14 天前   ❤️ 1
    现在 a.b.c.d 中的 c 这个单词拼错了,想要重命名,但是 c 这个单词又是一个很常见的单词,各个文件中都有,请问如何快速重命名,这就是 17 楼的意思。

    使用变量 ide 可以帮你一键重命名,使用字符串的格式...自己全局搜索,然后挨个看,甚至搜索结果 99+,看都看不完
    xuld
        55
    xuld  
       14 天前   ❤️ 2
    程序员的逻辑是:
    1. 我不喜欢喝汤
    2. 但我嫌弃饭太干
    3. 所以我发明了菜泡饭
    4. 快来夸我牛逼
    kinghly
        56
    kinghly  
       14 天前 via Android
    op 应该发到掘金的,那里小白多,符合你的文章定位。
    juzisang
        57
    juzisang  
       14 天前
    建议改名叫 safeGet safeSet ,然后兼容所有类型的存取👀
    cheneydog
        58
    cheneydog  
       14 天前
    假如我有一个 key 叫 U.S.A 如何处理?
    wuchangming89
        59
    wuchangming89  
       14 天前
    ```ts
    /**
    * 安全获取对象属性
    * 例子:
    * const obj = { a: { b: { c: 1 } } };
    * const c = safeGet(() => obj.a.b.c);
    * console.log(c); // 1
    * const notExist = safeGet(() => obj.a.b.d);
    * console.log(notExist); // undefined
    *
    * @param getProps
    * @returns
    */
    export function safeGet<T>(getProp: () => T): T | undefined {
    try {
    return getProp();
    } catch (e) {
    return undefined;
    }
    }
    ```
    sdjl
        60
    sdjl  
    OP
       14 天前
    @kinghly "应该发到掘金的,那里小白多,符合你的文章定位。"

    好的,谢谢建议。
    gogogo1203
        61
    gogogo1203  
       14 天前
    真是辣眼睛... immer 直接就全干了. typescript+ zod 验证都是基操了。 有这个精力干点什么不好
    Leviathann
        62
    Leviathann  
       14 天前   ❤️ 1
    sdjl
        63
    sdjl  
    OP
       14 天前
    oamu
        64
    oamu  
       14 天前
    总有人认为自己写的比库强。
    qingyingwan
        65
    qingyingwan  
       14 天前
    我第一年学前端的时候也像你这样实现了一个小工具就很高兴想炫耀一下,现在已经工作 9 年了也不做纯前端了,很怀念之前那种做一个小功能点就很高兴的时候,加油。楼上的各种解决方案也都不错,但是回到 8 年前我也会选择自己手写一个,如果我没经历那种热情,现在估计早就失业了
    alleluya
        66
    alleluya  
       13 天前
    @gouflv 啊? 小程序还有圈子的吗 小程序不都是前端捎带着写的东西么
    lyxxxh2
        67
    lyxxxh2  
       13 天前
    这不就是 laravel 配置的操作...
    https://www.kancloud.cn/learnku_/framework/1835172
    ```
    $val = config('a.b.c')
    config('a.c.b',"helloworld")
    ```

    除了配置,没那个数据有这么多层级。
    所以创建个不常用函数,意义不大。
    scyuns
        68
    scyuns  
       13 天前
    我选 set
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1009 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 19:10 · PVG 03:10 · LAX 12:10 · JFK 15:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.