JavaScript,在不丢失引用的情况下覆盖对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18441218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
JavaScript, overwrite object without losing reference
提问by Anton Gildebrand
Application
应用
I am working on a simple web application that is built on top of AngularJS. The application should be able to work offline as well as online. When the user is offline, the changes to the data is stored locally. Therefore, the id's that is used within this application in offline mode is only temporary id's, they get replaced when uploaded to the server
我正在开发一个基于 AngularJS 的简单 Web 应用程序。该应用程序应该能够离线和在线工作。当用户离线时,对数据的更改存储在本地。因此,在离线模式下在此应用程序中使用的 ID 只是临时 ID,上传到服务器时它们会被替换
Problem
问题
The data that are used in the application consists of complex objects (with relations/references to other objects). When i am saving to the server, i wanted the views to get updated with the new "real" id's.
However, since JavaScript works with objects as references im not able to do what i want to: $scope.data = newdata
This is not overwriting $scope.data but creates a new object. The old reference to the old data is still there.
应用程序中使用的数据由复杂的对象组成(与其他对象的关系/引用)。当我保存到服务器时,我希望使用新的“真实”ID 更新视图。然而,由于 JavaScript 使用对象作为引用,我无法做我想做的事情:$scope.data = newdata
这不是覆盖 $scope.data 而是创建一个新对象。对旧数据的旧引用仍然存在。
Simplified example
简化示例
var x = {id: 1, name: "myObject"}
var c = x // c = {id: 1, name: "myObject"}
x = {id: 2, name: "myNewObject"}
// c = {id: 1, name: "myObject"}
As you can see, c is still a reference to the old object. In practice, this causes that my view isn't updated with new data since it's still bound to the old data. What i need to is to overwrite the properties of, in this example, x. I need to do this recursively since my real objects are complex, however it shouldn't enter any circular references, since this will probably cause stack overflow. If i am overwriting a with b and a has properties that b hasn't got, those properties should be removed.
如您所见,c 仍然是对旧对象的引用。实际上,这会导致我的视图没有用新数据更新,因为它仍然绑定到旧数据。在本例中,我需要覆盖 x 的属性。我需要递归地执行此操作,因为我的真实对象很复杂,但是它不应该输入任何循环引用,因为这可能会导致堆栈溢出。如果我用 b 覆盖 a 并且 a 具有 b 没有的属性,则应删除这些属性。
What i need
我需要的
I need some sort of function that overwrites all properties in a (old object) with the properties in b (new object). All properties that exists in a but not in b should be removed.
我需要某种函数用 b (新对象)中的属性覆盖 a (旧对象)中的所有属性。所有存在于 a 但不存在于 b 中的属性都应该被删除。
采纳答案by Anton Gildebrand
I found a solution after some thinking. It's probably not the most efficient solution, but it does the job for me. The time complexity could probably be better, and all suggestions of improvement are welcome. First parameter is the object to be extended, the second the one to extend with. The third is supposed to be a boolean, indicating whether the properties in a that doesn't exist in b should be removed or not.
经过一番思考,我找到了解决方案。这可能不是最有效的解决方案,但它为我完成了这项工作。时间复杂度可能会更好,欢迎所有改进建议。第一个参数是要扩展的对象,第二个是要扩展的对象。第三个应该是一个布尔值,指示是否应该删除 b 中不存在的 a 中的属性。
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);
}
回答by Bobby Marinoff
Using the "extend" method which is available in underscore and jquery:
使用在下划线和 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)
回答by Nicekiwi
If your environment supports ECMAScript 2015, you can use Object.assign():
如果您的环境支持 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
is the new locally scoped version of var
in ECMAScript 2015) more...
(let
是var
ECMAScript 2015 中新的本地范围版本)更多...
So in the case of your simple example:
因此,就您的简单示例而言:
var x = { id: 1, name: "myObject" };
Object.assign(x, { id: 2, name: "myNewObject" });
console.log(x);
// will output: Object {id: 2, name: "myNewObject"}
回答by danjah
I'm adding an answer, even though everyone has explained both why and solutions.
我正在添加一个答案,尽管每个人都解释了原因和解决方案。
The reason I'm adding answer, is because I've searched for this answer a few times over the years and always basically come to the same 2/3 SO questions. I put the solutions in the too-hard-basket, because the code I've been working with has many modules all following similar design patterns; it's just been too much work to try and resolve what boiled down to the same issue you were having.
我添加答案的原因是因为这些年来我已经搜索了几次这个答案并且基本上总是遇到相同的 2/3 SO 问题。我把解决方案放在了太难的篮子里,因为我一直在使用的代码有许多模块都遵循类似的设计模式;尝试解决归结为您遇到的相同问题的问题太费力了。
What I've learned, and hopefully it holds some value for others out there now that I've actually re-factored our codebase to avoid this issue (sometimes maybe its unavoidable, but sometimes it definitely is), is to avoid using 'static private variables' to reference Objects.
我所学到的,希望它对其他人有一些价值,因为我实际上已经重构了我们的代码库以避免这个问题(有时它是不可避免的,但有时它确实是),是避免使用 'static私有变量'来引用对象。
This can probably be more genericised, but take for example:
这可能更通用,但例如:
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/
http://jsfiddle.net/goxdebfh/1/
As you can see, same problem experienced by the Questioner. In my case, and this use of private static variables referencing Objects was everywhere, all I needed to do was directly lookup G.someKey;
instead of storing it as a convenience variable for my Class. The end result (though lengthier as a result of inconvenience) works very well:
正如你所看到的,发问者遇到了同样的问题。就我而言,这种引用对象的私有静态变量的使用无处不在,我需要做的就是直接查找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/
http://jsfiddle.net/vv2d7juy/1/
So yeah, maybe nothing new given the question has been solved, but I felt compelled to share that because I was even lead to believe that the first example was the correct way to do things. Maybe in some cases it is, it definitely didn't turn out to be.
所以是的,鉴于问题已经解决,也许没有什么新东西,但我觉得有必要分享这一点,因为我什至相信第一个例子是正确的做事方式。也许在某些情况下确实如此,但事实证明并非如此。
Hopefully that helps someone, somewhere!
希望这可以帮助某人,某处!