Passing by reference

Continuing @WebReflection's idea from the now-closed ES Discuss mailing list, pass by reference would be a way to accept parts of objects and be able to modify them:

soSomething({a: 123, b: 456})

where

function doSomething({&b}) {
  b = 789 //  we can only modify b (which modifies the b property of the user's passed-in object.
}

The above concept as @WebReflection described is currently similar to:

function doSomething(o) {
  with (o) {
    b = 789 // we can modify b (which modifies the b property of the user's passed-in object).

    // but we can also modify `a`.
  }
}

Andrea mentioned security. If it were to be secure, it would need to be opt-in on the consumer side that passes arguments, not just in the library side:

soSomething(o with b) // or some syntax

Then, if an attacker changes the function header from doSomething({&b}) to doSomething({&a}) there would be a runtime error because the user did not allow a to be referenced.

1 Like

Just to be clear, your example would be the same as this, right?

function doSomething(o) {
  o.b = 789
}

I'm not sure how security is involved then, as this kind of mutability is already possible in Javascript today.

Related proposal: GitHub - rbuckton/proposal-refs: Ref declarations and expressions for ECMAScript

Which I think would look something like this:

function doSomething({ ref b }) {
  b = 789;
}
3 Likes

No, because the part I mentioned above about soSomething(o with b) (or some other syntax) allows the user to opt into defining what is accessible:

Plus, I didn't mention, if an attacker changes the header to doSomething(o) {, that would also trigger the runtime error because it would be like requesting access to all properties (similar to writing something like doSomething({&a, &b, &c, &d, &e, ..., &aa, &ab, ..., &az, &ba, ..., &zz, &aaa, ...etc}) with all the ... parts filled in with the infinte amount of permutations).

Well, here are my thoughts on the matter:

It's generally encouraged to try and keep your data structures immutable. Because of this, I'm not a big fan of any explicit syntax that encourages the opposite behavior (so I'm personally against the linked ref proposal that's currently being considered). Obviously, there are others with a differing viewpoint, otherwise that ref proposal wouldn't be a thing.

As for this concern about attackers, I'm not sure I see it. It's not common to create a program that calls foreign, potentially mallicious functions. I'm not sure there's even a safe way to do that kind of thing. But, assuming it is, and assuming you really need this kind of feature, you can always do something like this:

const myBigObj = { a: 1, b: 2, c: 3, d: 4, e: 5 }

// If you don't want to pass in all of your parameters,
// then don't! Just create a new object with a subset
// of parameters to pass in.
// We seal it to prevent the user from adding new properties.
const subsetOfParams = Object.seal({
  a: myBigObj.a,
  b: myBigObj.b,
  c: myBigObj.c,
})
hostileFunction(subsetOfParams)
// We can apply any modifications to the subsetOfParams obj
// back into the larger object after the function call.
Object.assign(myBigObj, subsetOfParams)

There's other ways to achieve this same kind of API, for example, through getters and setters, or via a proxy.

But in general, this feels like a badly designed API. It would be better if the hostileFunction() returned a new object instead of modifying the passed-in object, and there could potentially be better ways to compose the myBigObj, such that the properties being passed into hostileFunction() are already grouped together in a separate object.

function doSomething({ copy, &ref }) {
  // how do you prevent this?
  arguments[0].copy = "MWAHAHAHA!";
  delete arguments[0].anything;
}
1 Like

One thing I'm missing from this thread and the linked proposal: Why would you ever need a reference in JavaScript?

And secondly I'd like to mention that there is already a Reference (temporary value of a property access) in the specification, I would either name this proposal differently, or use the Reference in the same way it is already used, as a wrapper for an object and a property in it.