Prev: jQuery vs. My Library
Next: try catch catches when
From: nick on 23 Jan 2010 12:42 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
From: Asen Bozhilov on 23 Jan 2010 13:29 nick wrote: > (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
From: nick on 23 Jan 2010 13:49 On Jan 23, 1:29 pm, Asen Bozhilov <asen.bozhi...(a)gmail.com> wrote: > nick wrote: > > (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. > > 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. > 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
From: nick on 23 Jan 2010 14:09 On Jan 23, 1:49 pm, nick <nick...(a)fastmail.fm> wrote: > 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 > 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.
From: Asen Bozhilov on 23 Jan 2010 14:54
nick wrote: > 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[i].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. |