【问题标题】:Better way to Get Property Than using Lodash获得财产比使用 Lodash 更好的方法
【发布时间】:2018-04-11 21:39:44
【问题描述】:

所以我们的代码中到处都是这些废话,IMO 太多了。例如:

const profileId = _.get(account, 'profileId', null);
const player = _.get(someCustomObject, 'player'); // Why??????  why not just someCustomObject.player?

当您可以通过对象访问对象属性时,对所有事情都使用 lodash 是我还是荒谬!一般来说,我们使用的是 lodash,它在很多地方都显得过于矫枉过正,并且使代码更加冗长且难以阅读。

在这种情况下,我们也不需要 lodash:

const profileId = _.get(account, 'profileId', null);

如果没有 lodash,有什么方法可以做到这一点?那是我的问题。以下是一些想法:

const profileId = (account && account.profileId) || null

还有其他想法吗?

更新

有趣的是,与 Ori 的答案一致,但在这里只是一个观察。我想删除默认 profileId 为 null 只是因为我觉得没有必要。但是为什么不把profileId设置为account对象呢?

const account = {};
const game = { player: 'Sam' }

console.log(`account: ${account}`);

const { profileId } = account || {};
const { player } = game || {};

console.log(`profileId: ${profileId} // why is this undefined if account has value {}???`);
console.log(`player: ${player}`);

即使帐户设置为文字,我仍然会得到undefined for profileId 以上。这很奇怪..?

最终解决方案 (在codepen.io 中运行它,因为你需要加载 lodash)

console.log(" Example Clean Code without Lodash")
console.log('===============================')

let account = {};
const game = { player: 'Sam' }

console.log(`account: ${account}`);

const { profileId = null } = account;
const { player } = game || {};

console.log(`profileId: ${profileId}`);
console.log(`player: ${player}`);

/* using IIFE so profileId doesn't conflict with example above */
(() => {
  console.log("Example using unnecessary Lodash")
  console.log('===============================')

  let profileId = _.get(account, 'profileId', null);
  const game = { player: 'Sam' } 

  console.log(`account2: ${account}`);

  const { player } = game || {};

  console.log(`profileId: ${profileId}`);
  console.log(`player: ${player}`);
})();

【问题讨论】:

  • “更好”是一个广义的术语。

标签: javascript reactjs lodash


【解决方案1】:

Lodash 的 _.get() 非常好,例如,如果您想在对象 profile.player.id 中嵌套一些东西,并且如果没有找到任何内容或出现错误,它也有一个默认值,例如这个示例 _.get(account, 'profileId', null);

如果是对象的直接属性,可以使用destructuring with a default

const account = {};
const game = { player: 'Sam' }


const { profileId = null } = account || {};
const { player } = game || {};

console.log(profileId);
console.log(player);

一个更好的解决方案可能是 stage-1 proposal-optional-chaining 如果它符合规范,尽管你现在可以将它与 babel 插件一起使用:

const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
};

const baz = obj?.foo?.bar?.baz; // 42

const safe = obj?.qux?.baz; // undefined

【讨论】:

  • 这些都不是_.get() 的完全替代品,因为如果accountgamenullundefined,它们都会抛出错误。
  • 对,但我的可以工作:const profileId = (account && account.profileId) || null
  • 我只想摆脱所有不必要的 _get() ,即使它可能为空。我在我们的代码中发现所有这些都非常冗长,代码已经够乱了
  • 是的,我也是这么想的,你上次的编辑,那行得通..甚至const { profileId = null } = account || null;
  • @JLRishe - 确实如此。我为nullundefined 添加了一个后备选项。而且,它不会涵盖_.get() 所做的所有事情。
【解决方案2】:

_.get 是一个辅助方法,使用它有几个原因。它可以帮助您减少代码/逻辑重复,更难出错,更容易阅读和维护您的代码。

如果您担心_.get(account && account.profileId) || null 之间的性能差异,我认为您不必担心,除非您有一个巨大的代码库调用_.get 数百次。这是 lodash 的 get 函数定义和它的依赖关系。

function get(object, path, defaultValue) {
  var result = object == null ? undefined : baseGet(object, path);
  return result === undefined ? defaultValue : result;
}

function baseGet(object, path) {
  path = castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

function castPath(value, object) {
  if (isArray(value)) {
    return value;
  }
  return isKey(value, object) ? [value] : stringToPath(toString(value));
}

function toKey(value) {
  if (typeof value == 'string' || isSymbol(value)) {
    return value;
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

您始终可以对具有 2 种不同变体的代码执行基准测试,然后确定它是否足以让您使用本机实现。

我刚刚做了一个基准测试,其中 lodash 的 ._get 比原生实现慢 95%。我想你可以负担得起,除非你的代码严重依赖._get,如上所述。 也许更好的选择是编写自己的辅助函数来摆脱 lodash 的开销,并实现与 lodash 的 get 相同的优点以及接近原生的性能。您可以定义自己的辅助函数,提前知道将传递给它的数据类型,从而为您的特定用例编写零开销的辅助函数。

【讨论】:

  • >更易于阅读和维护您的代码。我认为它很冗长......如果你一直在使用它,它会在你的代码库中留下沉重的足迹。
  • >除非你有一个巨大的代码库,调用 _.get 数百次。是的,我们确实有:)。这就是为什么我首先发布这个,我更喜欢我的选择,并且停止过度使用 lodash,我希望在 lodash 上留下更少的足迹,只使用该死的语言。我也厌倦了人们忘记在我们的文件中为 lodash 添加导入。
  • >也许更好的选择是编写自己的辅助函数来摆脱 lodash 的开销,并实现与 lodash 的 get 相同的优点以及接近原生的性能。我更喜欢这个主意
  • 我最终会选择const { profileId = null } = account;。简单、干净、易于阅读,不需要 lodash。
  • @PositiveGuy 但是如果 account 未定义,那么它会抛出异常 - _.get(account, 'profileId') 会阻止这个问题。一旦 null/undefined coalescence feature 进入 ES,我们将有一个更好的解决方案:const profileId = account?.profileId ?? null
【解决方案3】:

这是文章“Safely Accessing Deeply Nested Values In JavaScript”中的一个不错的单行字

export function _get(object, path, defval = null) {
    if (typeof path === "string") path = path.split(".");
    return path.reduce((xs, x) => (xs && xs[x] ? xs[x] : defval), object);
}

您也可以使用这个很棒的零依赖实用程序库 https://github.com/angus-c/just

【讨论】:

  • 不幸的是,它不适用于这种情况:_get({p:{a:[0,1,2,3]}}, 'pa[1]') 而 lodash 工作
  • function _get(object, path,defval) { if (typeof path === "string") path = path.split("."); return path.reduce((xs, x) => (xs && xs[x]?xs[x]:x.split("[").reduce( (xxs, xx) =>(xxs && xxs[xx. replace(/(.*)]/,'$1')]? xxs[xx.replace(/(.*)]/,'$1')]:defval),xs)), object); } // 优雅还差很远
  • @DanieleCruciani 你总是可以_get({a: [1, 2]}, 'a.1')...如果你必须“兼容lodash”你可以path = path.replace(/\[/g, '.').replace(/\]/g, '').split('.')
【解决方案4】:

这来自最近的讨论:

var template = "string with {obj.prop} first of 0: {results[0].first}";
var data = {obj: {prop: 'mine'}, results:[{first:'f'},{first:'ff'}] }
var regex = /{(.*?)}/g;
var str = template.replace(regex, (e, key) => _.get(data, key, ''));
console.log(str);

但是你可以写一个函数:

const interpolate = (template, data) => template.replace(/{(.*?)}/g, (e, key) => _.get(data, key, ''));

当然使用起来更简单:

let str = interpolate("string with {obj.prop} first of 0: {results[0].first}", data)

一般来说,在您不知道要替换什么的任何地方,_.get() 都是不错的选择。

更新:修复缺失的正则表达式!

【讨论】:

  • 并将 _.get 替换为来自@xpuu 的 _get(),所以我们有一个用于字符串插值的 2 行代码
猜你喜欢
  • 2012-12-14
  • 2017-09-15
  • 2016-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多