Passing primitives by reference, part 2

N

nick

Well, it's the weekend again, time for more experimental fun with
js :)

Last week I was trying to pass primitives by reference (or, more
precisely, passing mutable object wrappers representing
primitives) ... anyway the point was to be able to do stuff like this
(pseudocode):

func ModifyValue(a,b) { increase a by b; }
let x = 4;
ModifyValue(x, 3);
print(x); // x is now 7

Now, last time I tried to accomplish this by adding function
properties to Object's prototype, which as I learned is a Bad Thing
(it breaks iterating over object properties using 'for ... in'). This
time I'm trying another approach; again I'd love to hear feedback from
you guys...

This time, instead of modifying Object's prototype, I'm modifying
prototypes only for constructors of primitive types. Before I go on
with the implementation, this brings up my first worry... a String, in
some browsers, acts as a collection of characters-as-strings with
'for ... in.' Does anyone actually use that functionality, or is it ok
to add stuff to String's constructor?

More generally, will it be a huge performance hit if I modify
constructors for Boolean, Integer, and String with one small function
property?

Alright, here we go:
....

(function(){
var p = [Boolean,Number,String];
for(var t in p) p[t].prototype.set =
function(v) { return wrap(Object(this), v) }
function wrap(o, v)
{
var c = o.constructor;
o.value = v===undefined ? c(o) : v ? c(v) : c();
o.valueOf = function() { return o.value.valueOf() }
o.toString = function() { return o.value.toString() }
o.set = function(v) { o.value = v ? c(v) : c(); return o }
return o;
}
})();

....
This extends [Boolean,Number,String].prototype with a method 'set'
that changes the current object by adding a property 'value' and
overriding valueOf and toString to return 'value'.

On the fourth line, 'Object(this)' is needed instead of just 'this'
for Chrome to act as expected when using the "// this works too"
notation below. The rest should be pretty straightforward. Here's a
quick test:
....

// some test functions
function square(n) { n.set(n*n) }
function bold(n) { n.set('<b>'+n+'</b>') }

// let's do some tests...

var foo = new Number(5);
// var foo = (5).set(); // this works too
var bar = foo;
alert(bar);

square(bar);
alert(foo);

bar.set(3);
square(foo);
alert(bar);

var baz = new String("asfaf");
// var baz = "asfaf".set(); // this works too
bold(baz);
alert(baz);

....

Thoughts? Criticisms? Cries of outrage from PE? Can't wait to hear
'em :)

-- Nick
 
A

Asen Bozhilov

nick said:
(function(){
  var p = [Boolean,Number,String];
  for(var t in p) p[t].prototype.set =

For-in lookup in prototype chain, and enumerate properties which
doesn't have {DontEnum} attribute. You should filter properties, which
inherited from objects in prototype chain.
    function(v) { return wrap(Object(this), v) }

Object(this)? Why do you call `Object' constructor? The `this' value
always refer `object' in ECMA 262-3
implementations.
  function wrap(o, v)
  {
    var c = o.constructor;
    o.value = v===undefined ? c(o) : v ? c(v) : c();
    o.valueOf = function() { return o.value.valueOf() }
    o.toString = function() { return o.value.toString() }
    o.set = function(v) { o.value = v ? c(v) : c(); return o }
    return o;
  }

})();

And every time when i call `set' method, you create two new functions.

var x = new Number(10);
x.set(50);

| 1. assign to `valueOf' reference to `object' which internal
[[Prototype]] refer Function.prototype
| 2. assign to `toString' reference to `object' which internal
[[Prototype]] refer Function.prototype

x.set(10);

| 3. same as a step 1.
| 4. mark `object' from step 1 for garbage collection
| 5. same as a step 2
| 6. mark `object' from step 2 for garbage collection
 
N

nick

nick said:
(function(){
  var p = [Boolean,Number,String];
  for(var t in p) p[t].prototype.set =

For-in lookup in prototype chain, and enumerate properties which
doesn't have {DontEnum} attribute. You should filter properties, which
inherited from objects in prototype chain.

Thanks for your response. Not sure I understand what you mean here,
I'll think on it more.
Object(this)? Why do you call `Object' constructor? The `this' value
always refer `object' in ECMA 262-3
implementations.

Already noted... On the fourth line, 'Object(this)' is needed instead
of just 'this' for Chrome to act as expected...
  function wrap(o, v)
  {
    var c = o.constructor;
    o.value = v===undefined ? c(o) : v ? c(v) : c();
    o.valueOf = function() { return o.value.valueOf() }
    o.toString = function() { return o.value.toString() }
    o.set = function(v) { o.value = v ? c(v) : c(); return o }
    return o;
  }

And every time when i call `set' method, you create two new functions.

var x = new Number(10);
x.set(50);

| 1. assign to `valueOf' reference to `object' which internal
[[Prototype]] refer Function.prototype
| 2. assign to `toString' reference to `object' which internal
[[Prototype]] refer Function.prototype

Is this coming from some debugging code you have or did you write it?

Anyway, missed |3., which (re)assigns to set an anonymous function (v)
which basically does 4. and 6. below
x.set(10);

| 3. same as a step 1.
| 4. mark `object' from step 1 for garbage collection
| 5. same as a step 2
| 6. mark `object' from step 2 for garbage collection

....but set has changed, and no longer calls wrap() ... it sets the
internal object you are referring to directly. So valueOf and toString
should only be set once. I think 3. and 5. should not happen.

-- Nick
 
N

nick

which basically does 4. and 6. below

wait, no it doesn't. I read your post too quickly. What it does is set
the object's 'value' property to something else (which I assume will
casue the GC to clean up the old value). So none of this should be
happening.
 
A

Asen Bozhilov

nick said:
Thanks for your response. Not sure I understand what you mean here,
I'll think on it more.

See above:

Array.prototype.o = Object;
var p = [Boolean,Number,String];
for(var t in p)
{
p[t].prototype.set = function(v) {
return wrap(Object(this), v);
};
}

You can filter with `hasOwnProperty' or with normal `for` loop:

for (var i = p.length; p--;)
{
p.prototype.set = function(){};
}

window.alert(typeof Object.prototype.set); //function
...but set has changed, and no longer calls wrap() ... it sets the
internal object you are referring to directly. So valueOf and toString
should only be set once. I think 3. and 5. should not happen.

Yes, my mistake. I did see overriding `set'. However, i still miss
general idea. Why do you want this? If you want explicit pointers you
can write in some low level language like `C`.

Regards.
 
N

nick

....
for (var i = p.length; p--;)

You're right, of course, I forgot iterating over arrays with for...in
is a no-no. I changed my code to use a normal in loop after you
mentioned it previously (identical to the example you wrote here
actually), but still was not sure if that was what you were referring
to. Thanks for clarifying. :)
Yes, my mistake. I did see overriding `set'. However, i still miss
general idea. Why do you want this? If you want explicit pointers you
can write in some low level language like `C`.

Well, don't forget many higher-level OO languages use pointers /
references too... C++, PHP, the .NET stuff, etc. In fact with PHP,
since objects are already passed by value, explicit references are
most commonly used for primitive datatypes (although variables/
parameters that are explicit references to objects do act slightly
differently from normal variables assigned to objects, so they could
technically have some use).

As for your question, I don't really have a good answer. It was just a
learning experiment; I wanted to see if it was possible without too
much hackery or overhead. I assume it could be used for things like
output variables, which might come in handy if you needed to add some
'return values' to something with a fixed API... imagine a method
returns an int, and other code relies on that behavior, but now you
also what the method to give a text description when it runs if the
script is in "verbose mode," say... output variables might come in
handy here?

I don't know, I guess I haven't really thought it through, it just
seems like something that might be useful at some point. I'll be sure
to make a followup post if I actually come up with a good use for
it. ;)

-- Nick
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top