如何在 JavaScript 中给定其字符串名称设置对象属性(对象属性的..)?
Posted
技术标签:
【中文标题】如何在 JavaScript 中给定其字符串名称设置对象属性(对象属性的..)?【英文标题】:How to set object property (of object property of..) given its string name in JavaScript? 【发布时间】:2020-04-21 11:59:53 【问题描述】:假设我们只得到了
var obj = ;
var propName = "foo.bar.foobar";
我们如何将属性obj.foo.bar.foobar
设置为某个值(比如“hello world”)?
所以我想实现这一点,而我们只有字符串中的属性名称:
obj.foo.bar.foobar = "hello world";
【问题讨论】:
重复***.com/questions/6842795/… 这可能对试图理解这个问题的答案的人有所帮助...***.com/questions/39060905/… 【参考方案1】:function assign(obj, prop, value)
if (typeof prop === "string")
prop = prop.split(".");
if (prop.length > 1)
var e = prop.shift();
assign(obj[e] =
Object.prototype.toString.call(obj[e]) === "[object Object]"
? obj[e]
: ,
prop,
value);
else
obj[prop[0]] = value;
var obj = ,
propName = "foo.bar.foobar";
assign(obj, propName, "Value");
【讨论】:
是的,当路径还不存在时,这个似乎也可以工作。 为什么要检查 typeof 属性?无论如何,您继续功能流程。 @StephanBönnemann,如果它不是一个字符串,当我们需要用obj[prop[0]] = value;
设置属性时,我们正在迭代中
@StephanBönnemann 不确定你的意思。添加它是为了使解决方案具有通用性,以便我们可以将数组或字符串作为prop
传递。
这不适用于数组。模板[0].item[0].format.color【参考方案2】:
由于这个问题的答案似乎是错误的答案,我只参考a similar question的正确答案
function setDeepValue(obj, value, path)
if (typeof path === "string")
var path = path.split('.');
if(path.length > 1)
var p=path.shift();
if(obj[p]==null || typeof obj[p]!== 'object')
obj[p] = ;
setDeepValue(obj[p], value, path);
else
obj[path[0]] = value;
用途:
var obj = ;
setDeepValue(obj, 'Hello World', 'foo.bar.foobar');
【讨论】:
嗯。你的答案看起来像我的一模一样:) 看起来我在typeof
中遗漏了=
。但坦率地说,typeof
是确保您不会尝试拆分对象的唯一方法,这是我遇到的事情之一。剩下的就是简单的递归。
如果我用foo.bar.foobar
和foo.bar2.foobar2
调用它两次怎么办?
在这种情况下,它确实在设置foo.bar2.foobar2
时重置了obj.foo
。编辑了代码。糟糕,没有看到您建议的修改,正在修复中。
@Cerbrus 还有一件事。如果为 obj.foo.bar 分配了一个值,然后我尝试 setDeepValue foo.bar.foobar 它失败并出现错误(除非该值是布尔值false
)。可能最安全的方法是测试 obj[p] 是否是一个对象。如果不是对象,请将其替换为 。由于 null 是一个对象,所以正确的测试应该是 if(obj[p]!=null && typeof obj[p]=== 'object')
【参考方案3】:
我知道这是一个旧的,但我在答案中只看到自定义函数。
如果您不介意使用库,请查看 lodash _.set
和 _.get
函数。
【讨论】:
【参考方案4】:编辑:我创建了一个jsPerf.com testcase 来将接受的答案与我的版本进行比较。 事实证明我的版本更快,尤其是当你深入的时候。
http://jsfiddle.net/9YMm8/
var nestedObjectAssignmentFor = function(obj, propString, value)
var propNames = propString.split('.'),
propLength = propNames.length-1,
tmpObj = obj;
for (var i = 0; i <= propLength ; i++)
tmpObj = tmpObj[propNames[i]] = i !== propLength ? : value;
return obj;
var obj = nestedObjectAssignment(,"foo.bar.foobar","hello world");
【讨论】:
This appears to be slightly slower in Chrome 比建议的递归函数。 但是,它在 IE / FF 中明显更快。 (不过,Chrome 的迭代次数最多 / 秒,V8 是执行 javascript 的野兽) @Cerbrus 似乎依赖于浏览器,我自己创建了测试用例jsperf.com/nested-object-assignment 代码性能测试101:不要每次迭代都初始化你的函数 仅适用于空的第一个参数对象,否则会覆盖原始对象数据。【参考方案5】:所有解决方案在设置时都会覆盖任何原始数据,因此我对以下内容进行了调整,也将其变成了一个对象:
var obj =
nestObject.set(obj, "a.b", "foo");
nestObject.get(obj, "a.b"); // returns foo
var nestedObject =
set: function(obj, propString, value)
var propNames = propString.split('.'),
propLength = propNames.length-1,
tmpObj = obj;
for (var i = 0; i <= propLength ; i++)
if (i === propLength)
if(tmpObj[propNames[i]])
tmpObj[propNames[i]] = value;
else
tmpObj[propNames[i]] = value;
else
if(tmpObj[propNames[i]])
tmpObj = tmpObj[propNames[i]];
else
tmpObj = tmpObj[propNames[i]] = ;
return obj;
,
get: function(obj, propString)
var propNames = propString.split('.'),
propLength = propNames.length-1,
tmpObj = obj;
for (var i = 0; i <= propLength ; i++)
if(tmpObj[propNames[i]])
tmpObj = tmpObj[propNames[i]];
else
break;
return tmpObj;
;
还可以将函数更改为 Oject.prototype 方法,将 obj 参数更改为此:
Object.prototype = setNested = function() ... , getNested = function() ...
.setNested('a.c','foo')
【讨论】:
【参考方案6】:这是我刚刚从几个线程 + 一些自定义代码编译的 get 和 set 函数。
它还会创建现场不存在的键。
function setValue(object, path, value)
var a = path.split('.');
var o = object;
for (var i = 0; i < a.length - 1; i++)
var n = a[i];
if (n in o)
o = o[n];
else
o[n] = ;
o = o[n];
o[a[a.length - 1]] = value;
function getValue(object, path)
var o = object;
path = path.replace(/\[(\w+)\]/g, '.$1');
path = path.replace(/^\./, '');
var a = path.split('.');
while (a.length)
var n = a.shift();
if (n in o)
o = o[n];
else
return;
return o;
【讨论】:
我喜欢这个,因为它也有数组索引,例如“财产[3]”。但是,要让 setValue 像这样工作,我们还必须添加 "path = path.replace(/[(\w+)]/g, '.$1');"到 setValue 方法。【参考方案7】:这是一个使用引用的简单函数。
function setValueByPath (obj, path, value)
var ref = obj;
path.split('.').forEach(function (key, index, arr)
ref = ref[key] = index === arr.length - 1 ? value : ;
);
return obj;
【讨论】:
【参考方案8】:您可以拆分路径并检查以下元素是否存在。如果没有将对象分配给新属性。
然后返回属性的值。
最后赋值。
function setValue(object, path, value)
var fullPath = path.split('.'),
way = fullPath.slice(),
last = way.pop();
way.reduce(function (r, a)
return r[a] = r[a] || ;
, object)[last] = value;
var object = ,
propName = 'foo.bar.foobar',
value = 'hello world';
setValue(object, propName, value);
console.log(object);
【讨论】:
【参考方案9】:这是一个返回更新后的对象
function deepUpdate(value, path, tree, branch = tree)
const last = path.length === 1;
branch[path[0]] = last ? value : branch[path[0]];
return last ? tree : deepUpdate(value, path.slice(1), tree, branch[path[0]]);
const path = 'cat.dog';
const updated = deepUpdate('a', path.split('.'), cat: dog: null)
// => cat: dog: 'a'
【讨论】:
【参考方案10】:一个非常简单的。
这个实现应该非常高效。 它避免了递归和函数调用,同时保持了简单性。
/**
* Set the value of a deep property, creating new objects as necessary.
* @param Object obj The object to set the value on.
* @param String|String[] path The property to set.
* @param * value The value to set.
* @return Object The object at the end of the path.
* @author github.com/victornpb
* @see https://***.com/a/46060952/938822
* @example
* setDeep(obj, 'foo.bar.baz', 'quux');
*/
function setDeep(obj, path, value)
const props = typeof path === 'string' ? path.split('.') : path;
for (var i = 0, n = props.length - 1; i < n; ++i)
obj = obj[props[i]] = obj[props[i]] || ;
obj[props[i]] = value;
return obj;
/*********************** EXAMPLE ***********************/
const obj =
hello : 'world',
;
setDeep(obj, 'root', true);
setDeep(obj, 'foo.bar.baz', 1);
setDeep(obj, ['foo','quux'], '?');
console.log(obj);
// ⬇︎ Click "Run" below to see output
【讨论】:
【参考方案11】:我正在寻找一个不会覆盖现有值并且易于阅读并且能够想出的答案。把这个留在这里,以防它帮助其他有相同需求的人
function setValueAtObjectPath(obj, pathString, newValue)
// create an array (pathComponents) of the period-separated path components from pathString
var pathComponents = pathString.split('.');
// create a object (tmpObj) that references the memory of obj
var tmpObj = obj;
for (var i = 0; i < pathComponents.length; i++)
// if not on the last path component, then set the tmpObj as the value at this pathComponent
if (i !== pathComponents.length-1)
// set tmpObj[pathComponents[i]] equal to an object of it's own value
tmpObj[pathComponents[i]] = ...tmpObj[pathComponents[i]]
// set tmpObj to reference tmpObj[pathComponents[i]]
tmpObj = tmpObj[pathComponents[i]]
// else (IS the last path component), then set the value at this pathComponent equal to newValue
else
// set tmpObj[pathComponents[i]] equal to newValue
tmpObj[pathComponents[i]] = newValue
// return your object
return obj
【讨论】:
【参考方案12】:与 Rbar 的答案相同,在使用 redux reducers 时非常有用。我也使用 lodash clone 而不是扩展运算符来支持数组:
export function cloneAndPatch(obj, path, newValue, separator='.')
let stack = Array.isArray(path) ? path : path.split(separator);
let newObj = _.clone(obj);
obj = newObj;
while (stack.length > 1)
let property = stack.shift();
let sub = _.clone(obj[property]);
obj[property] = sub;
obj = sub;
obj[stack.shift()] = newValue;
return newObj;
【讨论】:
【参考方案13】:Object.getPath = function(o, s)
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i)
var k = a[i];
if (k in o)
o = o[k];
else
return;
return o;
;
Object.setPath = function(o, p, v)
var a = p.split('.');
var o = o;
for (var i = 0; i < a.length - 1; i++)
if (a[i].indexOf('[') === -1)
var n = a[i];
if (n in o)
o = o[n];
else
o[n] = ;
o = o[n];
else
// Not totaly optimised
var ix = a[i].match(/\[.*?\]/g)[0];
var n = a[i].replace(ix, '');
o = o[n][ix.substr(1,ix.length-2)]
if (a[a.length - 1].indexOf('[') === -1)
o[a[a.length - 1]] = v;
else
var ix = a[a.length - 1].match(/\[.*?\]/g)[0];
var n = a[a.length - 1].replace(ix, '');
o[n][ix.substr(1,ix.length-2)] = v;
;
【讨论】:
【参考方案14】:这是一个使用作用域Object
的简单方法,该方法通过路径递归设置正确的道具。
function setObjectValueByPath(pathScope, value, obj)
const pathStrings = pathScope.split('/');
obj[pathStrings[0]] = pathStrings.length > 1 ?
setObjectValueByPath(
pathStrings.splice(1, pathStrings.length).join('/'),
value,
obj[pathStrings[0]]
) :
value;
return obj;
【讨论】:
【参考方案15】:来个简单又短的怎么样?
Object.assign(this.origin, [propName]: value )
【讨论】:
以上是关于如何在 JavaScript 中给定其字符串名称设置对象属性(对象属性的..)?的主要内容,如果未能解决你的问题,请参考以下文章
Javascript给定一个带有html标签名称和字符串的数组返回没有dom的html
在给定变量名称为字符串的模块内部修改 __main__.variable
如何在Javascript中使用其旁边元素的值更改单元格的元素?
如何在我的 javascript AJAX 代码中显示所选国家/地区的城市名称和人口?