函数可以修改对象的参数,而不仅仅是属性

Function to modify object in arguments, not just a property

本文关键字:不仅仅是 属性 参数 修改 对象 函数      更新时间:2023-09-26

我相信这一定存在于某个地方,但我还没能找到它…

我正在尝试编写一个函数,该函数将对象作为参数并更新其引用。不是引用的属性,也不是对对象重新赋值,而是更新整个引用。

请注意,这里的PubSub只是为了演示传入和更新对象类型的异步性和灵活性。

最好用例子来解释:

//ideally how function would work
function watch(event, obj) {
    PubSub.on(event, function(model) {
        // I want to update the entire object
        // I understand that currently, this is just reassigning
        // I know I can do obj.prop = model, but that's not what I want to do
        obj = model;
    }
};
//example usage
var myObj = {"name" : "Tom"};
watch("anEvent", myObj);
console.log(myObj.name); //still "Tom"
// time passes, then somewhere
PubSub.trigger("anEvent", {"name" : "John"})
console.log(myObj.name); // now should read "John"

在这种情况下,bind()方法,例如$.bind()或柯里化将是有用的,或者只是为了在适当的上下文中使用"this"?

这是不可能的。

让你困惑的是,在js运行时,你没有办法管理实际的引用值,而只能给一个变量分配一些引用值。

就是这样——你可以给一个给定的变量名"附加"另一个引用,但是你不能修改这个引用的值。

清除我上面所说的:对象的内存是在初始化时分配的

var o = {};

之后,对未命名对象(对象没有名称,变量有名称)的引用被赋值给o变量。在此之后,您不能将另一个对象重新分配到内存中的同一位置。

我假设您已经阅读了我在以下章节的解释:为什么是对象'在函数调用中捕获的值?

如果你明白发生了什么。你想得够难的了。您将看到一个明显的工作。

因此,与其他人的答案相反,我要说:是的,这是可能的(在一定范围内)。


工作

首先回顾一下。你意识到你所拥有的实际上是对同一个对象的两个独立引用吗?分配一个引用不会导致另一个引用被重新分配:

var foo = {some : 'values'};
(function (bar) {
    // 'bar' in here points to the
    // same object as foo but is a
    // completely different reference
})(foo)
// is it obvious yet?

所以,当直接的bar = new_object不起作用时。注意,在您感兴趣的位置,两个引用都指向同一个对象

这意味着,即使你没有访问原始引用('foo'在我的例子,'myObj'在你的例子),你所做的是它指向的对象。这是我们可以利用的。

如果你不关心当前对象发生了什么,如果你不介意对象的所有引用都被修改,你可以简单地对它的所有属性进行深度删除,并用新对象的深度副本替换它们。

所以,当你不能做:

obj = model;

你可以:

for (var n in obj) {
    if (obj.hasOwnProperty(n)) {
        delete(obj[n]);
    }
}
for (var n in model) {
    if (model.hasOwnProperty(n)) {
        obj[n] = model[n];
    }
}

和你的代码应该像你期望的那样工作。

如果你经常这样做(或者你只是想让你的代码更容易读),你可以封装这个逻辑:

function reassignObjRef (ref, new_obj) {
  for (var n in ref) {
    if (ref.hasOwnProperty(n)) {
      delete(ref[n]);
    }
  }
  for (var n in new_obj) {
    if (new_obj.hasOwnProperty(n)) {
      ref[n] = new_obj[n];
    }
  }
}

所以你可以简单地做:

function watch(event, obj) {
    PubSub.on(event, function(model) {
        reassignObjRef(obj,model);
    }
};

引用只是变量,就像原语一样。重新赋值就是改变它的值。

你说

,而不是重新赋值对象,而是更新整个引用。

问题是它们的意思是一样的。如果你的意思是更新另一个指向这个对象的引用,使其指向其他对象,那么只有当其他引用在你的作用域中时才能这样做。

没有办法说,给定对特定对象的引用,将对该对象的所有引用更改为指向其他对象。听起来这就是你想要做的。对不起,不可能。您需要一个中间对象,该对象的属性是您想要更改的引用。

//ideally how function would work
function watch(event, obj) {
    PubSub.on(event, function(model) {
        // I want to update the entire object
        // I understand that currently, this is just reassigning
        // I know I can do obj.prop = model, but that's not what I want to do
        obj.model = model;
    }
};
//example usage
var myObj = {model: {"name" : "Tom"}};
watch("anEvent", myObj);
console.log(myObj.model.name); //still "Tom"
// time passes, then somewhere
PubSub.trigger("anEvent", {"name" : "John"})
console.log(myObj.name); // now should read "John"

没有办法知道或找到对一个对象的所有引用,因此没有通用的方法将所有引用重新分配给其他对象。你可以用闭包和getset方法静态地做到这一点(参见Douglas Crockford的Private Members in Javascript),但这似乎不是你想要的。

同样,bind特定于函数的this变量,该变量是函数执行上下文的一个参数。这与作用域无关