应用
我正在开发一个构建在AngularJS之上的简单Web应用程序。该应用程序应该能够脱机工作以及在线工作。当用户离线时,对数据的更改将存储在本地。因此,在离线模式下此应用程序中使用的id只是临时id,在上传到服务器时会被替换
问题
应用程序中使用的数据由复杂对象组成(具有对其他对象的关系/引用)。当我保存到服务器时,我希望视图能够使用新的“真实”ID进行更新。
但是,由于JavaScript使用对象作为引用而无法执行我想要的操作:$scope.data = newdata
这不是覆盖$ scope.data而是创建一个新对象。对旧数据的旧引用仍然存在。
简化示例
var x = {id: 1, name: "myObject"}
var c = x // c = {id: 1, name: "myObject"}
x = {id: 2, name: "myNewObject"}
// c = {id: 1, name: "myObject"}
如您所见,c仍然是对旧对象的引用。实际上,这会导致我的视图没有使用新数据更新,因为它仍然绑定到旧数据。 我需要的是覆盖在这个例子中x的属性。我需要以递归方式执行此操作,因为我的实际对象很复杂,但是它不应该输入任何循环引用,因为这可能会导致堆栈溢出。如果我用b覆盖a并且a具有b未获得的属性,则应删除这些属性。
我需要什么
我需要某种功能,用b(new object)中的属性覆盖(旧对象)中的所有属性。应删除存在于但不存在于b中的所有属性。
答案 0 :(得分:5)
经过深思熟虑,我找到了解决方案。它可能不是最有效的解决方案,但它确实适合我。时间复杂度可能会更好,所有改进建议都是值得欢迎的。第一个参数是要扩展的对象,第二个是要扩展的对象。第三个应该是一个布尔值,表明是否应该删除b中不存在的属性。
function extend(_a,_b,remove){
remove = remove === undefined ? false : remove;
var a_traversed = [],
b_traversed = [];
function _extend(a,b) {
if (a_traversed.indexOf(a) == -1 && b_traversed.indexOf(b) == -1){
a_traversed.push(a);
b_traversed.push(b);
if (a instanceof Array){
for (var i = 0; i < b.length; i++) {
if (a[i]){ // If element exists, keep going recursive so we don't lose the references
a[i] = _extend(a[i],b[i]);
} else {
a[i] = b[i]; // Object doesn't exist, no reference to lose
}
}
if (remove && b.length < a.length) { // Do we have fewer elements in the new object?
a.splice(b.length, a.length - b.length);
}
}
else if (a instanceof Object){
for (var x in b) {
if (a.hasOwnProperty(x)) {
a[x] = _extend(a[x], b[x]);
} else {
a[x] = b[x];
}
}
if (remove) for (var x in a) {
if (!b.hasOwnProperty(x)) {
delete a[x];
}
}
}
else{
return b;
}
return a;
}
}
_extend(_a,_b);
}
答案 1 :(得分:5)
使用&#34;扩展&#34;下划线和jquery中可用的方法:
//Clear all the 'old' properties from the object
for (prop in old_object) {delete old_object[prop]}
//Insert the new ones
$.extend(old_object, new_object)
答案 2 :(得分:3)
如果您的环境支持ECMAScript 2015,则可以使用Object.assign():
'use strict'
let one = { a: 1, b: 2, c: 3 };
let two = { b: 20, c: 30, d: 40 };
let three = Object.assign({}, one, two);
console.log(three);
// will output: Object {a: 1, b: 20, c: 30, d: 40}
(let
是ECMAScript 2015中新var
的本地范围版本)more..。
所以就你的简单例子而言:
var x = { id: 1, name: "myObject" };
Object.assign(x, { id: 2, name: "myNewObject" });
console.log(x);
// will output: Object {id: 2, name: "myNewObject"}
答案 3 :(得分:1)
我正在添加答案,尽管每个人都解释了原因和解决方案。
我之所以添加答案,是因为多年来我多次搜索过这个答案,并且总是基本上得到同样的2/3 SO问题。我把解决方案放在太难的篮子里,因为我一直在使用的代码有许多模块都遵循类似的设计模式;尝试解决你所遇到的同样问题的工作太过分了。
我学到了什么,希望它对其他人有一些价值,因为我实际上已经重新考虑了我们的代码库以避免这个问题(有时可能是不可避免的,但有时候肯定是),是要避免使用'static private variables'来引用Objects。
这可能更通用化,但举个例子:
var G = {
'someKey' : {
'foo' : 'bar'
}
};
G.MySingletonClass = (function () {
var _private_static_data = G.someKey; // referencing an Object
return {
/**
* a method that returns the value of _private_static_data
*
* @method log
**/
log: function () {
return _private_static_data;
} // eom - log()
}; // end of return block
}()); // end of Class
console.log(G.MySingletonClass.log());
G.someKey = {
'baz':'fubar'
};
console.log(G.MySingletonClass.log());
http://jsfiddle.net/goxdebfh/1/
正如您所看到的,提问者遇到了同样的问题。在我的例子中,这种使用引用Objects的私有静态变量到处都是,我需要做的就是直接查找G.someKey;
而不是将它存储为我的类的便利变量。最终结果(虽然由于不方便而更长)非常有效:
var G = {
'someKey' : {
'foo' : 'bar'
}
};
G.MySingletonClass = (function () {
return {
/**
* a method that returns the value of _private_static_data
*
* @method log
**/
log: function () {
return G.someKey;
} // eom - log()
}; // end of return block
}()); // end of Class
console.log(G.MySingletonClass.log());
G.someKey = {
'baz':'fubar'
};
console.log(G.MySingletonClass.log());
http://jsfiddle.net/vv2d7juy/1/
所以是的,也许没什么新问题已经解决了,但我觉得有必要分享这个,因为我甚至认为第一个例子是正确的做事方式。也许在某些情况下,它肯定没有结果。
希望在某个地方帮助某人!