技术分享
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)

刘誉

总有人要赢,为什么不能是我
首页
  • JavaScript

    • 构造函数和原型
    • Cookie和Session
    • Object.create(null)和{}
    • TypeScript配置
    • typescript入门到进阶
  • 框架

    • Vue-Router
    • React基础入门
  • 其它

    • Http协议
    • 跨域问题总结
  • 分析Promise实现
  • Axios源码分析
  • Webpack原理
  • vueRouter源码分析
  • Vue

    • Vite快速搭建Vue3+TypeScript项目
    • Cordova打包Vue项目的问题
    • Vue将汉字转为拼音,取出首字母
    • h5项目问题总结
  • JavaScript

    • new Function
  • 后端

    • Node.js中使用Crypto生成Token
    • Body-Parser处理多层对象的问题
  • 其它

    • 项目Demo汇总
    • Vuepress+Vercel搭建个人站点
    • 项目中能用到的
    • husky规范代码提交
  • Mongoose基础
  • Multer文件上传中间件的使用
  • JavaScript

    • 浅谈两数全等
    • JavaScript进制转换
    • 手写bind,apply,call和new
  • 算法

    • 数组去重和排序
    • 数组扁平化
    • 斐波那契数列
  • JavaScript 数据结构
  • 其它

    • webpack面试题
    • vite面试题
    • svg和canvas的优缺点
    • TypeScript面试题
    • Vue常见面试题
  • 计算机网络

    • 数据链路层
    • 网络层
  • Git的使用
  • Nginx的使用
  • CentOS7安装Nginx
  • 正则表达式
  • SEO搜索引擎优化
  • Serverless介绍
友链
GitHub (opens new window)
  • 项目 Demo 汇总
  • node中使用crypto生成token
  • new Function
    • 项目内容
      • 任务需求
      • 原代码
      • 原代码升级版
    • 自己实现一个
      • 分析
      • 最终修改版
  • body-parser处理多层对象的问题
  • Vite快速搭建Vue3+TypeScript项目
  • Cordova打包Vue项目的问题
  • Vue将汉字转为拼音,取出首字母
  • 项目中能用到的
  • Vuepress+Vercel搭建个人站点
  • husky规范代码提交
  • h5项目问题总结
  • vue动态换行,行末省略
  • mocks项目复盘
  • 项目
coderly
2020-05-25

new Function

# new Function

最近在项目中看到个写的不错的函数实现,拿出来和大家一起分享分享

# 项目内容

# 任务需求

  • 两个配置项,prev,和 curr
  • 它们的结构可能是这样:
let prev = {
  name: 'prev',
  lives: {
    eat: '想吃点东西',
    play: '想出去玩',
  },
}
let curr = {
  name: 'curr',
  lives: {
    eat: 'eat meat',
    play: 'play football',
    sleep: {
      time: 'morning',
      place: 'home',
    },
  },
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 我们发现它们有点像,但是数据又不同
  • 现在我们要实现一个 将 curr 的某些项(由你传入的参数决定)来更新 prev 的数据
  • 可能的情况:
    1. prev 有 curr 的属性,只是值不同
    2. prev 里面没有 curr 的属性,这时我们要将该属性对应的值都 在 prev 上呈现
  • 需要注意的是:我们这个操作并不是将 curr 里的内容和 prev 比较,有不同才更新到 prev 上,而是根据 用户 输入的 属性 ,将 curr 上对应的属性值更新到 prev 上

# 原代码

  • 有改动
function setConf(prev, curr, fileds, callback) {
  if (!Array.isArray(fileds)) throw new Error(`第 3 个参数必须为一个数组`)
  const isFunc = typeof callback === 'function'
  for (let i = 0; i < fileds.length; i++) {
    const key = fileds[i]
    if (typeof curr[key] === 'undefined')
      throw new Error(`属性${key}的值为undefined`)
    prev[key] = isFunc ? callback(key, curr[key]) : curr[key]
  }
}
1
2
3
4
5
6
7
8
9
10

【使用】:

let prev = {
  name: 'prev',
  lives: {
    eat: '想吃点东西',
    play: '想出去玩',
  },
}
let curr = {
  name: 'curr',
  lives: {
    eat: 'eat meat',
    play: 'play football',
    sleep: {
      time: 'morning',
      place: 'home',
    },
  },
}
setConf(prev.lives, curr.lives, ['eat', 'sleep'])
console.log(prev)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

结果
图片

【分析】:

  • 我们看到上面的结果,我们将 curr 上的 eat 和 sleep 属性的值 更新到了 prev 上
  • setConf(prev.lives, curr.lives, ['eat', 'sleep']) 我们传入了 prev.lives 和 curr.lives , 意思是:我们需要将 curr.lives 的某写属性 更新到 prev.lives 上,而需要更新哪些内容,我们 第 3 个参数 ['eat', 'sleep'] 就是 需要更新的属性

# 原代码升级版

function setValue(data, key, value) {
  const func = new Function(
    'obj',
    'value',
    `return obj['${key.split('.').join("']['")}'] = value`
  )
  return func(data, value) || ''
}
function getValue(data, key) {
  const func = new Function(
    'obj',
    `return obj['${key.split('.').join("']['")}']`
  )
  return func(data)
}
function setConf(prev, curr, fileds, callback) {
  if (!Array.isArray(fileds)) throw new Error(`第 3 个参数必须为一个数组`)
  const isFunc = typeof callback === 'function'
  for (let i = 0; i < fileds.length; i++) {
    let prevKey = ''
    let currKey = ''
    if (Array.isArray(fileds[i])) {
      prevKey = fileds[i][0]
      currKey = fileds[i][1]
    } else {
      prevKey = currKey = fileds[i]
    }
    if (typeof curr[currKey] === 'undefined')
      throw new Error(`属性${currKey}的值为undefined`)
    setValue(
      prev,
      prevKey,
      isFunc
        ? callback(prevKey, getValue(curr, currKey))
        : getValue(curr, currKey)
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

【使用】:

let prev = {
  name: 'prev',
  lives: {
    eat: '想吃点东西',
    play: '想出去玩',
    sleep: {
      time: 'old time',
    },
  },
}
let curr = {
  name: 'curr',
  lives: {
    eat: 'eat meat',
    play: 'play football',
    sleep: {
      time: 'morning',
      place: 'home',
    },
    newTime: '最新时间',
  },
}
setConf(prev.lives, curr.lives, ['eat', ['sleep.time', 'newTime']])
console.log(prev)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • 结果
    图片

【分析】:

  • 现在经过升级,setConf 第 3 个参数数组里面还可以是数组,里面的那个 数组的意思是:['sleep.time', 'newTime'] , 将 prev.lives 里的 sleep.time 设置为 curr.lives 里的 newTime 值
  • 这样该方法可以接受多个参数,并且 prev 里需要更新的属性 可以和 curr 里的属性不一样
  • 上面有一个缺陷,就是如果 prev.lives 对象里面 不存在 sleep 属性 是会报如下的错
let prev = {
  name: 'prev',
  lives: {
    eat: '想吃点东西',
    play: '想出去玩',
    // 删除 sleep 属性
  },
}
let curr = {
  name: 'curr',
  lives: {
    eat: 'eat meat',
    play: 'play football',
    sleep: {
      time: 'morning',
      place: 'home',
    },
    newTime: '最新时间',
  },
}
setConf(prev.lives, curr.lives, ['eat', ['sleep.time', 'newTime']])
console.log(prev)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • 结果
    图片

# 自己实现一个

  • 对于此前没有用过 new Function 创建过函数的我来说,我第一眼看到这个的使用就开始百度
  • new Function( 'obj', 'value', return obj['${key.split('.').join("']['")}'] = value ) 特别是 return 后面的内容,还真得找个例子模拟运行一遍才能看懂
  • 所以花了点时间在思考,可不可以用我自己的方法实现一个类似的功能

# 分析

# getValue

  • 该方法就是通过 不断对一个 对象 类型的数据,通过 . 取其对象的属性,直到最后你需要的那个 属性 对应的 值
let prev = {
  name: 'prev',
  lives: {
    eat: '想吃点东西',
    play: '想出去玩',
  },
}
1
2
3
4
5
6
7
  • 比如上面这个,如果我要获取到 play 属性 对应的 值
  • 我们一般是通过 prev.lives.play 来获取
  • 如果对这个获取的过程进行拆分,可以总结如下:
    1. 首先获取 lives 的属性值:let livesTmp = prev.lives
    2. 然后通过上一步的值,再获取 play 的属性值: let play = livesTmp.play
  • 所以它的处理过程是,不断获取到 上一个属性对应的 属性值 ,然后通过该 属性值 继续找 下一个属性 对应的 属性值
  • 我们知道 数组的 reduce 方法,在回调函数中的第一个参数,是上一次处理返回的结果,第二个是当前的值。
  • 所以我们用 reduce 方法改写 getValue 函数实现,如下:
function getValue(data, keys) {
  return keys.split('.').reduce((pre, key) => {
    return pre[key]
  }, data)
}
1
2
3
4
5
  • 加上错误处理:
function getValue(data, keys) {
  let value
  try {
    value = keys.split('.').reduce((pre, key) => {
      return pre[key]
    }, data)
    if (typeof value === 'undefined') {
      throw new Error(`配置项${keys}的值为undefined`)
    }
  } catch (e) {
    throw new Error(`配置项${keys}的值为undefined`)
  }
  return value
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# setValue

  • 我们借鉴上面的 getValue 的思路,getValue 是通过不断取属性值来取到你想获取的属性对应的值。
  • 那我们的 setValue 就是先通过查询当前属性有没有在 对象上,如果没有就添加,存在就查询 该属性值对应的下一个属性值,知道找到最后一个你要修改或者添加的属性和属性值。
  • 所以我们也通过 reduce 来改写 setValue 函数实现,如下:
function setValue(data, keys, value) {
  keys.split('.').reduce((pre, key, i, arr) => {
    if (i === arr.length - 1) pre[key] = value
    if (i != arr.length - 1 && typeof pre[key] === 'undefined') pre[key] = {}
    return pre[key]
  }, data)
}
1
2
3
4
5
6
7
  • 也可以用 如下不太好理解的写法,它和上面是等价的
function setValue(data, keys, value) {
  keys.split('.').reduce((pre, key, i, arr) => {
    ;(i === arr.length - 1 && (pre[key] = value)) ||
      (i != arr.length - 1 &&
        typeof pre[key] === 'undefined' &&
        (pre[key] = {}))
    return pre[key]
  }, data)
}
1
2
3
4
5
6
7
8
9

# 最终修改版

function getValue(data, keys) {
  let value
  try {
    value = keys.split('.').reduce((pre, key) => {
      return pre[key]
    }, data)
    if (typeof value === 'undefined') {
      throw new Error(`配置项${keys}的值为undefined`)
    }
  } catch (e) {
    throw new Error(`配置项${keys}的值为undefined`)
  }
  return value
}
function setValue(data, keys, value) {
  keys.split('.').reduce((pre, key, i, arr) => {
    ;(i === arr.length - 1 && (pre[key] = value)) ||
      (i != arr.length - 1 &&
        typeof pre[key] === 'undefined' &&
        (pre[key] = {}))
    return pre[key]
  }, data)
}
function setConf(prev, curr, fileds, callback) {
  const isFunc = typeof callback === 'function'
  let prevKey, currKey
  fileds.forEach((keys, i) => {
    prevKey = currKey = keys
    if (Array.isArray(keys)) {
      prevKey = keys[0]
      currKey = keys[1]
    }
    let pKey = prevKey.split('.').pop()
    let cKey = currKey.split('.').pop()
    let value = isFunc
      ? callback(pKey, getValue(curr, currKey))
      : getValue(curr, currKey)
    setValue(prev, prevKey, value)
  })
}
let prev = {
  name: 'prev',
  lives: {
    eat: '想吃点东西',
    play: '想出去玩',
  },
  province: {
    newAddr: '浙江省',
  },
}
let curr = {
  name: 'curr',
  lives: {
    eat: 'eat meat',
    play: 'play football',
    sleep: {
      time: 'morning',
      place: 'home',
    },
  },
  address: 'xx省xx市xxx',
}
setConf(prev, curr, [
  'lives.eat',
  ['lives.sleep.time', 'lives.sleep.time'],
  ['province.newAddr', 'address'],
])
console.log(prev)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

【结果】:

  • 这样我们通过 setConf(prev, curr, ['lives.eat', ['lives.sleep.time', 'lives.sleep.time'], ['province.newAddr', 'address']]),实现了追加不同属性并更新给出的属性
    图片
#JavaScript
上次更新: 2021/03/25, 12:42:28
node中使用crypto生成token
body-parser处理多层对象的问题

← node中使用crypto生成token body-parser处理多层对象的问题→

最近更新
01
代码片段
04-22
02
koa全家桶
03-29
03
mocks项目复盘
03-29
更多文章>
Theme by Vdoing | Copyright © 2021-2022 coderly | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式