From: Kees on 25 Jan 2010 09:37 In my html page I get from a PHP server a big JSON-encoded string. In javascript I decode this string and I get a object with properties foundPersons and errorMsg. In my javascript code I have a Person prototype with handy methods like fullName()... Then I traverse the JSONDecodedObject.foundPersons and I retrieve the foundPersons[i] object. This object (of course) does not know the fullName() method. foundPersons[i] is a object not a Person. It would be nice if I could change foundPersons[i].prototype to Person.prototype. I've tried foundPersons[i].prototype=new Person(). But no luck: foundPersons[i].fullName is still not accessible. Do I have to create a new Person and copy then all data to the new created Person? Any ideas? Kees
From: Scott Sauyet on 25 Jan 2010 15:57 On Jan 25, 2:41 pm, Thomas 'PointedEars' Lahn <PointedE...(a)web.de> wrote: > Scott Sauyet wrote: >> Thomas 'PointedEars' Lahn wrote: >>> In a case as well known as this it is rather easy to do in a >>> cross-browser manner in several ways: >>> >>> var proto = Person.prototype; >>> >>> for (var p in proto) >>> { >>> var o = foundPersons[i]; >>> >>> if (typeof proto[p] == "function") >>> { >>> o[p] = proto[p]; >>> } >>> } > >> This works as long as none of the functions defined for Person rely on >> properties created during construction, right? >> >> That is, the following might not work (in any of the methods we've >> been discussing): > >> function Person(first, last, ...) >> this.first = first; >> this.last = last; >> this.created = new Date(); >> // ... >> } >> >> Person.prototype.toString = function() { >> return this.firstName + " " + this.lastName + " (" + >> this.created + ")"; >> } > > There is no good reason why the approach above it should not work with this > code. [ ... ] I guess it depends upon what you mean by "work". But perhaps I wasn't being clear. I believe that for the object derived from this: {"firstName": "Scott", "lastName": "Sauyet"} the fullName function will return "Scott Sauyet (undefined)", which may or may not be acceptable. For completeness, here's a more complete script: var people = [{first: "Thomas", last: "Lahn"}, {first: "Scott", last: "Sauyet"}]; function Person(first, last) { this.first = first; this.last = last; this.created = new Date(); } Person.prototype.fullName = function() { return this.first + " " + this.last; } var person = people[0], len = people.length, proto = Person.prototype; for (var i = 0; i < len; i++) { var person = people[i]; for (var p in proto) { if (typeof proto[p] == "function") { person[p] = proto[p]; } } } for (var i = 0; i < len; i++) { alert(people[i].fullName()); } This works fine, alerting "Thomas Lahn" and "Scott Sauyet" as expected. But if you use this fullName function instead: Person.prototype.fullName = function() { return this.first + " " + this.last + " (" + this.created + ")"; } It alerts the (possibly incorrect) "Thomas Lahn (undefined)" and "Scott Sauyet (undefined)". And if you used this version instead: Person.prototype.fullName = function() { return ((this.created.getTime() > new Date().getTime() - 1000 * 60 * 60) ? "NEW: " : "" ) + this.first + " " + this.last; } the script will generate an error. The problem, of course, is that the persistent data loaded from the JSON (or hardcoded in the example above) might well not contain transient data generated in the constructor. >> But I think it doesn't have to be a doubling of storage space > > It is not a considerable doubling of storage space, though. The Function > objects are not copied by the assignment, only the reference value is. > That is, after the assignment both properties refer to the *same* object. The doubling I was talking about had to do not with your method of copying functions but with methods using the constructor. I was trying to allay the OP's concern here: | Do I have to create a new Person and copy then all data to | the new created Person? That concern might not be an issue of memory at all, just convenience, but I wanted to point out that there are techniques which do not use as much memory as the naive one would. > [ ... ] > Either constructor-based approach would require more memory than my > suggestion, though, because only after the original object would have been > garbage-collected memory could be saved here. Absolutely. > They are also a lot less runtime-efficient. What factors contribute to this? -- Scott
From: Thomas 'PointedEars' Lahn on 25 Jan 2010 16:33 Scott Sauyet wrote: > function Person(firstName, lastName) { > if (/* test first format */) { > this.data = firstName; > } else { > this.data = {firstName: firstName, lastName: lastName}; > } > // ... > } > > This could then take advantage of the reviver parameter to JSON.parse > wherever it's avaialable. I have just read about the `reviver' _argument_, thanks for the hint: <http://msdn.microsoft.com/en-us/library/cc836466%28VS.85%29.aspx> <https://developer.mozilla.org/En/Using_JSON_in_Firefox#Converting_objects_into_JSON> However, AIUI, `Person' as you define it above cannot be used for that argument. First of all, according to the MSDN Library the reviver function is "called for each member of the [deserialized] object in post-order"; one would need to make sure that it only operates on those "members" that hold person data. Second, `this' does not always refer to the passed object in the reviver function; in JavaScript 1.8.1, JSON.parse('{"foo": {"bar": "baz"}}', function(k, v) { console.log("debug:", this.foo, k, v); return v; }); displays: | debug: undefined bar baz | debug: Object { bar="baz"} foo Object { bar="baz"} | debug: undefined Object { foo=Object} Third, the reviver function needs to return something; constructors usually return nothing, i.e. they implicitly return `undefined', by which the property would be deleted here. And even though if a function returns a primitive value it is ignored if the function is called as a constructor (and so we could return `lastName' which would be the value of the "member" if the constructor was called as a reviver function), we have still no reliable way to refer to the object in the reviver function. Fourth, the reviver function is passed two arguments, the property name and the property value. PointedEars -- Use any version of Microsoft Frontpage to create your site. (This won't prevent people from viewing your source, but no one will want to steal it.) -- from <http://www.vortex-webdesign.com/help/hidesource.htm> (404-comp.)
From: Thomas 'PointedEars' Lahn on 25 Jan 2010 16:59 Scott Sauyet wrote: > Thomas 'PointedEars' Lahn wrote: >> Scott Sauyet wrote: >>> Thomas 'PointedEars' Lahn wrote: >>>> In a case as well known as this it is rather easy to do in a >>>> cross-browser manner in several ways: >>>> >>>> var proto = Person.prototype; >>>> >>>> for (var p in proto) >>>> { >>>> var o = foundPersons[i]; >>>> >>>> if (typeof proto[p] == "function") >>>> { >>>> o[p] = proto[p]; >>>> } >>>> } >>> >>> This works as long as none of the functions defined for Person rely on >>> properties created during construction, right? >> [...] >>> That is, the following might not work (in any of the methods we've >>> been discussing): >>> >>> function Person(first, last, ...) >>> this.first = first; >>> this.last = last; >>> this.created = new Date(); >>> // ... >>> } >>> >>> Person.prototype.toString = function() { >>> return this.firstName + " " + this.lastName + " (" + >>> this.created + ")"; >>> } >> >> There is no good reason why the approach above it should not work with >> this code. [ ... ] > > I guess it depends upon what you mean by "work". But perhaps I wasn't > being clear. I believe that for the object derived from this: > > {"firstName": "Scott", "lastName": "Sauyet"} > > the fullName function will return "Scott Sauyet (undefined)", which > may or may not be acceptable. Yes, but I think it goes without saying that a function should only be transferred for use as a method if it fits the purpose at hand. That includes that all the properties of the calling object that the method reads from when called are there on the calling object, and that all the properties of the calling object that the method writes to when called are not read-only or otherwise used on the calling object. >>> But I think it doesn't have to be a doubling of storage space >> >> It is not a considerable doubling of storage space, though. The >> Function objects are not copied by the assignment, only the reference >> value is. That is, after the assignment both properties refer to the >> *same* object. > > The doubling I was talking about had to do not with your method of > copying functions but with methods using the constructor. I beg your pardon? (And the functions are still _not_ copied.) > I was trying to allay the OP's concern here: > > | Do I have to create a new Person and copy then all data to > | the new created Person? > > That concern might not be an issue of memory at all, just convenience, > but I wanted to point out that there are techniques which do not use > as much memory as the naive one would. I beg your pardon? Your constructor-based approach (IIUC) does nothing else than what the OP described, and it must fail to save memory as it creates a new object, which allocates more memory. >> [Constructor-based approaches] are also a lot less runtime-efficient. > > What factors contribute to this? Mostly object creation and method calls, as compared to operations. We can also safely assume that if the prototype method is called it takes slightly longer for it to be looked up in the prototype chain of the instance. That is not to say these approaches you suggested are nonsense; they are just not as efficient, and they might easily be overkill here. PointedEars -- Prototype.js was written by people who don't know javascript for people who don't know javascript. People who don't know javascript are not the best source of advice on designing systems that use javascript. -- Richard Cornford, cljs, <f806at$ail$1$8300dec7(a)news.demon.co.uk>
From: Scott Sauyet on 25 Jan 2010 20:08 On Jan 25, 4:33 pm, Thomas 'PointedEars' Lahn <PointedE...(a)web.de> wrote: > Scott Sauyet wrote: >> This could then take advantage of the reviver parameter to JSON.parse >> wherever it's avaialable. > > I have just read about the `reviver' _argument_, thanks for the hint: > > <http://msdn.microsoft.com/en-us/library/cc836466%28VS.85%29.aspx> > <https://developer.mozilla.org/En/Using_JSON_in_Firefox#Converting_objects_into_JSON> > > However, AIUI, `Person' as you define it above cannot be used for that > argument. > > First of all, according to the MSDN Library the reviver function is "called > for each member of the [deserialized] object in post-order"; one would need > to make sure that it only operates on those "members" that hold person > data. Well, you'd have to return the original value for Strings, Numbers, etc. But there is a pattern that I've seen a few places ... searching, searching, ah yes... such as JSON.org [1]: | myData = JSON.parse(text, function (key, value) { | var type; | if (value && typeof value === 'object') { | type = value.type; | if (typeof type === 'string' && | typeof window[type] === 'function') { | return new (window[type])(value); | } | } | return value; | }); You could use this by adding "type": var people = [{type: "Person", first: "Thomas", last: "Lahn"}]; > Second, `this' does not always refer to the passed object in the reviver > function; in JavaScript 1.8.1, > > JSON.parse('{"foo": {"bar": "baz"}}', > function(k, v) { > console.log("debug:", this.foo, k, v); > return v; > }); > > displays: > > | debug: undefined bar baz > | debug: Object { bar="baz"} foo Object { bar="baz"} > | debug: undefined Object { foo=Object} Okay, I didn't realize this, but I'm not sure it matters. I hadn't thought of using "this" inside the function. > Third, the reviver function needs to return something; constructors usually > return nothing, i.e. they implicitly return `undefined', by which the > property would be deleted here. And even though if a function returns a > primitive value it is ignored if the function is called as a constructor > (and so we could return `lastName' which would be the value of the "member" > if the constructor was called as a reviver function), we have still no > reliable way to refer to the object in the reviver function. .... but they can be used with "new"... > Fourth, the reviver function is passed two arguments, the property name and > the property value. Right, but I don't see the issue. I think this would work: function Person(data) { this.first = data.first; this.last = data.last; this.created = new Date(); } Person.prototype.fullName = function() { return this.first + " " + this.last + " (" + this.created + ")"; } var text = '[{"type":"Person","first":"Thomas", ' + '"last":"Lahn"},' + '{"type":"Person","first": "Scott", ' + '"last": "Sauyet"}]', people = JSON.parse(text, function (key, value) { var type; if (value && typeof value === 'object') { type = value.type; if (typeof type === 'string' && typeof window[type] === 'function') { return new (window[type])(value); } } return value; }), len = people.length; for (var i = 0; i < len; i++) { alert(people[i].fullName()); } This of course doesn't address the legitimate concerns you raise about possible performance problems. -- Scott ____________________ [1] http://www.json.org/js.html
|
Next
|
Last
Pages: 1 2 3 4 5 Prev: get or post? Next: appendChild that fails in Safari, but succeeds in FF, Camino |