extend prototype chain?

K

Kees

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 object. This object (of course) does not know the
fullName() method. foundPersons is a object not a Person.
It would be nice if I could change foundPersons.prototype to
Person.prototype. I've tried foundPersons.prototype=new Person().
But no luck: foundPersons.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
 
S

Scott Sauyet

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 object. This object (of course) does not know the
fullName() method. foundPersons is a object not a Person.
It would be nice if I could change foundPersons.prototype to
Person.prototype. I've tried foundPersons.prototype=new Person().
But no luck: foundPersons.fullName is still not accessible.


There are more experienced people here who might contradict me, but I
think that you're not going to be able to do this simply in a cross-
browser manner. In Firefox, you could do

foundPersons.__proto__ = Person.prototype

Do I have to create a new Person and copy then all data to the new
created Person?

I think so. But might you be able to have the constructor Person
receive something like foundPersons as its parameter? Will you be
constructing Persons in other ways? If you can do so, this probably
isn't too bad:

var person = new Person(foundPersons);

Good luck,

-- Scott
 
T

Thomas 'PointedEars' Lahn

Scott said:
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 object. This object (of course) does not know the
fullName() method. foundPersons is a object not a Person.
It would be nice if I could change foundPersons.prototype to
Person.prototype. I've tried foundPersons.prototype=new Person().
But no luck: foundPersons.fullName is still not accessible.


It does not work because instances have no `prototype' property that has
any meaning with regard to the prototype chain; their constructors have,
from which they do not inherit, but assigning to that property only makes
sense *before* constructing the object.
There are more experienced people here who might contradict me, but I
think that you're not going to be able to do this simply in a cross-
browser manner. In Firefox, you could do

foundPersons.__proto__ = Person.prototype


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;

if (typeof proto[p] == "function")
{
o[p] = proto[p];
}
}

Of course, that does not change the prototype object of the instance, but
it makes the user-defined methods of the other prototype object available
on the instance. Another approach is, of course, for example

Person.prototype.fullName.call(foundPersons)

but that is not as compatible, and not as efficient if the method needs to
be called more than one time.

Since we must assume the prototype object of the instances here is the
original Object.prototype object, one might even want to augment
Object.prototype temporarily:

Object.prototype.fullName = Person.prototype.fullName;
// ...
foundPersons.fullName();
// ...
delete Object.prototype.fullName;
I think so.

No, that is _not_ necessary here.


PointedEars
 
S

Scott Sauyet

Scott said:
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 object. This object (of course) does not know the
fullName() method. foundPersons is a object not a Person.
It would be nice if I could change  foundPersons.prototype to
Person.prototype. I've tried foundPersons.prototype=new Person().
But no luck: foundPersons.fullName is still not accessible.


It does not work because instances have no `prototype' property that has
any meaning with regard to the prototype chain; their constructors have,
from which they do not inherit, but assigning to that property only makes
sense *before* constructing the object.
There are more experienced people here who might contradict me, but I
think that you're not going to be able to do this simply in a cross-
browser manner.  In Firefox, you could do
    foundPersons.__proto__ = Person.prototype


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;

    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 + ")";
}


No, that is _not_ necessary here.

No, as you said, you can copy the functions to the instance, assuming
that you don't have a situation like I've defined above. But I think
it doesn't have to be a doubling of storage space if you have the
luxury to structure Person this way:

function Person(data) {
this.data = data;
// ....
}

Person.prototype.fullName = function() {
return this.data.firstName + " " + this.data.lastName;
}

and if you want to chase down argument types, you can overload the
constructor to allow this as well:

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.

-- Scott
 
T

Thomas 'PointedEars' Lahn

Scott said:
Thomas said:
Scott said:
Then I traverse the JSONDecodedObject.foundPersons and I retrieve
the foundPersons object. This object (of course) does not know the
fullName() method. foundPersons is a object not a Person. ^^^^^^^^^^^^^^^^^^^^^
It would be nice if I could change foundPersons.prototype to
Person.prototype. I've tried foundPersons.prototype=new Person().
But no luck: foundPersons.fullName is still not accessible. [...]
There are more experienced people here who might contradict me, but I
think that you're not going to be able to do this simply in a cross-
browser manner. In Firefox, you could do

foundPersons.__proto__ = Person.prototype


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;

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?
Wrong.

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. The OP is apparently getting an (HTTP response[?] with a) JSON
string (in the message body) like

'{"first": "foo", "last": "bar"}'

which (by some unknown code) is converted to an object representable as

{"first": "foo", "last": "bar"}

which they refer to as `JSONDecodedObject.foundPersons' in the problem
description. If the prototype method is added as property of that object,
then, when called, `this' refers to that object and `this.first' (or
`this.last') to its property named `first' (and `last', respectively).
No, as you said, you can copy the functions to the instance, assuming
that you don't have a situation like I've defined above.

The situation you have defined above makes no difference to the feasibility
of the approach. The prototype object of an instance does not inherit from
the instance, especially not if the instance has not been created yet (the
`Person' constructor has _not_ been used here, hence the need for the
solution). So any (future) instance properties cannot possibly show up
when iterating over the enumerable properties of the prototype 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.
if you have the luxury to structure Person this way:

function Person(data) {
this.data = data;
// ....
}

Person.prototype.fullName = function() {
return this.data.firstName + " " + this.data.lastName;
}

and if you want to chase down argument types,

Define "chase down argument types".
you can overload the constructor to allow this as well:

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.

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. They are also a lot less
runtime-efficient.

Please trim your quotes to the relevant minimum.


PointedEars
 
S

Scott Sauyet

Scott said:
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;

  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;
for (var p in proto) {
if (typeof proto[p] == "function") {
person[p] = proto[p];
}
}
}
for (var i = 0; i < len; i++) {
alert(people.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.

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
 
T

Thomas 'PointedEars' Lahn

Scott said:
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(VS.85).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
 
T

Thomas 'PointedEars' Lahn

Scott said:
Thomas said:
Scott said:
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;

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.
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
 
S

Scott Sauyet

Scott said:
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(VS.85).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.fullName());
}

This of course doesn't address the legitimate concerns you raise about
possible performance problems.

-- Scott
____________________
[1] http://www.json.org/js.html
 
S

Scott Sauyet

Scott said:
Thomas 'PointedEars' Lahn wrote: [ much relevant discussion deleted ... see earlier messages ]
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.

If you are going to use that technique, obviously you have to live
with these limitations. But the OP was looking to copy the entire
Person.prototype to the new Object, which I took to mean that these
Objects should be substitutable for an object created from Person.

The technique I suggested is more general. But it could well be
overkill, and if there are performance concerns, then copying the
function references is likely the best bet.
[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.  Wecan
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.

Thank you. I really need to learn more about performance concerns in
the various ES implementations.

-- Scott
 
T

Thomas 'PointedEars' Lahn

Scott said:
Thomas said:
I have just read about the `reviver' _argument_, thanks for the hint:

<http://msdn.microsoft.com/en-us/library/cc836466(VS.85).aspx>
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);

OMG. Apparently Crockford is getting serious with his "window for Global
Object to avoid implied globals" nonsense he babbled months ago.

Avoid this pattern at all costs; `window' refers to a host object, _not_
the built-in Global Object. Refer to the built-in Global Object by `this'
where possible (as specified and documented), by user-defined
variable/property where necessary.
| }
| }
| return value;
| });

You could use this by adding "type":

var people = [{type: "Person", first: "Thomas", last: "Lahn"}];

Possible. However, we do not need any reference to the constructor then
(irresponsibly by `window[type]' or responsibly by refToGlobal[type]), for
we have the identifier of the constructor already.

In addition, this only works with constructors that are properties of the
Global Object; if we lose the needless reference check, it can work with
other constructors, too (applying eval() if necessary).
Okay, I didn't realize this, but I'm not sure it matters. I hadn't
thought of using "this" inside the function.

You suggested to pass a reference to the constructor to JSON.parse().
In case you did not notice, the constructor uses `this' a lot.


PointedEars
 
J

Jorge

OMG.  Apparently Crockford is getting serious with his "window for Global
Object to avoid implied globals" nonsense he babbled months ago.

Avoid this pattern at all costs; `window' refers to a host object, _not_
the built-in Global Object.  Refer to the built-in Global Object by `this'
where possible (as specified and documented), by user-defined
variable/property where necessary.

window *is* already -in any decent browser- an alias of the global
object(1):

window= globalObject;

An alias -btw- not any worse than your suggested:

var global= globalObject;

So why do you insist -quixotically- in aliasing it a second time when
the browser already provides a proper alias ?. ISTM that you're the
one babbling -again-. You seem to have trouble getting this (and the
closures, too).

(1)for all intents and purposes, and leaving aside multiple -
perceived- globalObjects, frames and child windows.
 
M

Matthias Reuter

Jorge said:
window *is* already -in any decent browser- an alias of the global
object(1):

I was wondering about that passage in ECMA-262, Section 10.1.5:

"[...] for example, in the HTML document object model the window property
of the global object is
the global object itself."

That matches what you're saying and contradicts what several others are
saying. The question is, is this normative to the DOM or descriptive (e.g.
for the time it was written)?

Matt
 
R

Richard Cornford

Jorge said:
window *is* already -in any decent browser- an alias of the
global object(1):

I was wondering about that passage in ECMA-262, Section 10.1.5:

"[...] for example, in the HTML document object model the window
property of the global object is the global object itself."

That matches what you're saying and contradicts what several
others are saying. The question is, is this normative to the
DOM or descriptive (e.g. for the time it was written)?

It is descriptive (and only an example).

Richard.
 
J

Jorge

Jorge said:
window *is* already -in any decent browser- an alias of the global
object(1):

I was wondering about that passage in ECMA-262, Section 10.1.5:

"[...] for example, in the HTML document object model the window property 
of the global object is
the global object itself."

That matches what you're saying and contradicts what several others are  
saying. The question is, is this normative to the DOM or descriptive (e.g..  
for the time it was written)?

It may not be normative, it might be just another de-facto standard
(there are many more, as you know). Or is there any browser in which:
<script>deFacto= window === this;</script> isn't true ?
 
T

Thomas 'PointedEars' Lahn

Matthias said:
Jorge said:
window *is* already -in any decent browser- an alias of the global
object(1):

I was wondering about that passage in ECMA-262, Section 10.1.5:

"[...] for example, in the HTML document object model the window property
of the global object is the global object itself."

That matches what you're saying and contradicts what several others are
saying. The question is, is this normative to the DOM or descriptive
(e.g. for the time it was written)?

As Richard said. I would like to add an explanation as to why it is
"descriptive (and only an example)", hopefully for future reference:

As a rule of thumb, examples in specifications are *never* normative.

If you look closely, you can recognize the level of requirement in the part
that you have omitted (`[...]'):

| 15.1 The Global Object
|
| [...] Initially the global object has the following properties:
|
| [...]
| * Additional host defined properties. This may include a property whose
^^^
| value is the global object itself; [see quotation above]

That little word "may" is very important; so important, that in many other
specifications it is written in ALL-CAPS -- "MAY" -- so that it is not
overlooked in the prose. It means that there is the possibility of what is
described, but no requirement is made by the specification that it needs to
be made happen, and no recommendation is made to make it happen or to make
it not happen. IOW, it is completely neutral wording, by contrast to
"must/shall" (absolute requirement to be), "should" (recommendation in
favor), "should not" (recommendation against), "shall not/must not"
(absolute requirement not to be). See also RFC 2119, which defines these
terms common to many (if not all) technical specifications, and which is
therefore referred to by many specifications (for example, W3C
specifications). (Unfortunately, it is not referred to explicitly by the
Editions of the ECMAScript Language Specification; however, given general
use, and the way these words are used in the Specification, there can be no
doubt as to their meaning.)

Then you need to consider the scope of the Specification; no *programming
language* standard can provide a normative basis for the implementation of
a *host-defined (proprietary) feature* based on some *markup language*.

There is _not_ /the/ "HTML document model", but several ones. Even at the
time this wording was first used (1999/2000) there were at least three DOM
APIs: the NN4 DOM, the MSHTML DOM, and W3C DOM HTML Level 1; the first two
were not called "DOM" at the time (the prevalent term was "DHTML", cf. the
MSDN Library which still employs that term), and the latter one (obsolete
since 2003) did not specify the `window' property (nor do its successors to
date). As a rule of thumb, double-check all statements made by ECMA people
about HTML or the DOM, and by W3C Specifications about ECMAScript
implementations (for example, there are similar errors in examples in HTML
4.01).

Finally, a property can _not_ *be* an object; it can only _refer_ to one.
(Unfortunately, the Specification employs sloppy wording in several
normative places, while being rather unnecessarily pedantic in others
[especially in Edition 5]; a property can _not_ have an object for its
value, but only a reference to the object.)


On a personal note, Jorge has been told these and other basic things a
hundred times before already, but he would not listen (or try to
understand). The record shows he is likely only trolling; ignore him.


HTH

PointedEars
 
S

Scott Sauyet

Scott said:
Thomas 'PointedEars' Lahn wrote:
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 [ ... ]:
| 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"}];

Possible.  However, we do not need any reference to the constructor then
(irresponsibly by `window[type]' or responsibly by refToGlobal[type]), for
we have the identifier of the constructor already.

Obviously we can write the reviver however we choose. Although I've
seen this pattern in several places (possibly all derived from
JSON.org) we could hard-code something like the (untested) following:

var reviver = function (key, value) {
var type;
if (value && typeof value === 'object') {
type = value.type;
if (type == 'Person')
return new Person(value);
} else if (type == 'Address') {
return new Address(value);
} // etc.
}
return value;
};

In addition, this only works with constructors that are properties of the
Global Object; if we lose the needless reference check, it can work with
other constructors, too (applying eval() if necessary).


I'm not sure I understand. Would you be willing to elaborate?
Second, `this' does not always refer to the passed object in the reviver
function; in JavaScript 1.8.1, [ ... ]

Okay, I didn't realize this, but I'm not sure it matters.  I hadn't
thought of using "this" inside the function.

(I should have explicitly said "inside the reviver function".)
You suggested to pass a reference to the constructor to JSON.parse().
In case you did not notice, the constructor uses `this' a lot.

But if the constructor is used with "new" should the external value of
"this" make any difference?

-- Scott
 
T

Thomas 'PointedEars' Lahn

Scott said:
Thomas said:
Scott said:
Thomas 'PointedEars' Lahn wrote:
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 [ ... ]:

| myData = JSON.parse(text, function (key, value) {
| var type;

BTW, the separate declaration here does not make much sense. Since
variable instantiation is performed before execution, you can declare
and initialize the variable in one statement. Since you do not use the
variable outside of the block, there is also no reason to declare it
outside (if we assumed that there was block scoping).
| 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"}];

[...]
In addition, this only works with constructors that are properties of
the Global Object; if we lose the needless reference check, it can work
with other constructors, too (applying eval() if necessary).

I'm not sure I understand. Would you be willing to elaborate?

Sure. Let (for the sake of sanity) `_global' be a reference to the Global
Object, and `foo' holds the string value "Bar", then

_global[foo]

can only ever refer to

function Bar(/* ... */)
{
/* ... */
}

or

var Bar = function(/* ... */) {
/* ... */
};

if those are declared/defined so in the global execution context (only then
is `Bar' the name of a property of the Global Object).

It does not work for constructors declared/defined in a local execution
context --

function reviverWrapper(sConstructor)
{
/* ... */
}

function Foo()
{
function Bar(/* ... */)
{
/* ... */
}

JSON.parse(..., reviverWrapper("Bar"));
}

-- or for what has come to be called "namespaces":

var foo = {
Bar: function(/* ... */) {
/* ... */
}
};

However, if we would use eval() --

var myData = JSON.parse(text,
function (key, value) {
if (value && typeof value === 'object')
{
var type = value.type;
if (typeof type === 'string')
{
var C = eval(type);
if (typeof C === 'function')
{
return new C(value);
}
}
return value;
});

-- those two cases could be handled as well. (Of course, eval() has its
issues, too, but that is beside the point here.)
Second, `this' does not always refer to the passed object in the
reviver function; in JavaScript 1.8.1, [ ... ]
Okay, I didn't realize this, but I'm not sure it matters. I hadn't
thought of using "this" inside the function.

(I should have explicitly said "inside the reviver function".)
You suggested to pass a reference to the constructor to JSON.parse().
In case you did not notice, the constructor uses `this' a lot.

But if the constructor is used with "new" should the external value of
"this" make any difference?

I do not know what "external value of 'this'" you are talking about;
`this' has only meaning in the execution context in which it is used.

I understood that *at first* you meant

JSON.parse(..., Person);

Maybe I misunderstood back then.


PointedEars
 
S

Scott Sauyet

BTW, the separate declaration here does not make much sense.  Since
variable instantiation is performed before execution, you can declare
and initialize the variable in one statement.  Since you do not use the
variable outside of the block, there is also no reason to declare it
outside (if we assumed that there was block scoping).

Yes, I don't think I would use the code exactly as JSON.org supplied
it.
I'm not sure I understand.  Would you be willing to elaborate?

Sure.  Let (for the sake of sanity) `_global' be a reference to the Global
Object, and `foo' holds the string value "Bar" [ ... ]

Thank you for your very clear explanation. I thought you were headed
in a different direction.

You technique looks quite useful if we want a fairly generic reviver.
The alternative, of course it to explicitly supply those constructors
we need for a more specific reviver.


Second, `this' does not always refer to the passed object in the
reviver function; in JavaScript 1.8.1, [ ... ]
Okay, I didn't realize this, but I'm not sure it matters.  I hadn't
thought of using "this" inside the function.
(I should have explicitly said "inside the reviver function".)
But if the constructor is used with "new" should the external value of
"this" make any difference?

I do not know what "external value of 'this'" you are talking about;
`this' has only meaning in the execution context in which it is used.

I simply meant that the when running the constructor function the
execution context is not the same one used in the reviver function, so
the "this" references in the contructor function shold be as expected.

I understood that *at first* you meant

  JSON.parse(..., Person);

Maybe I misunderstood back then.

Yes, perhaps I wasn't clear about something earlier. I have a hard
time imagining a Person constructor also serving as a reviver
function.

Cheers,

-- Scott
 
J

Jorge

I was wondering about that passage in ECMA-262, Section 10.1.5:
"[...] for example, in the HTML document object model the window property
of the global object is the global object itself."
That matches what you're saying and contradicts what several others are
saying. The question is, is this normative to the DOM or descriptive
(e.g. for the time it was written)?

As Richard said.  I would like to add an explanation as to why it is
"descriptive (and only an example)", hopefully for future reference:

As a rule of thumb, examples in specifications are *never* normative.

If you look closely, you can recognize the level of requirement in the part
that you have omitted (`[...]'):

| 15.1 The Global Object
|
| [...] Initially the global object has the following properties:
|
| [...]
| * Additional host defined properties. This may include a property whose
                                             ^^^
|   value is the global object itself; [see quotation above]

That little word "may" is very important; so important, that in many other
specifications it is written in ALL-CAPS -- "MAY" -- so that it is not
overlooked in the prose.  It means that there is the possibility of what is
described, but no requirement is made by the specification that it needs to
be made happen, and no recommendation is made to make it happen or to make
it not happen.  IOW, it is completely neutral wording, by contrast to
"must/shall" (absolute requirement to be), "should" (recommendation in
favor), "should not" (recommendation against), "shall not/must not"
(absolute requirement not to be).  See also RFC 2119, which defines these
terms common to many (if not all) technical specifications, and which is
therefore referred to by many specifications (for example, W3C
specifications).  (Unfortunately, it is not referred to explicitly by the
Editions of the ECMAScript Language Specification; however, given general
use, and the way these words are used in the Specification, there can be no
doubt as to their meaning.)

To begin with, the ECMA specs are the wrong place to look for
"window", so it hardly matters whether there's a may or a may not or
whatever in a comment in an unrelated spec. The browsers' API when/if
spec'ed (not in this case) is spec'ed somewhere else.
Then you need to consider the scope of the Specification; no *programming
language* standard can provide a normative basis for the implementation of
a *host-defined (proprietary) feature* based on some *markup language*.

Blah blah.
There is _not_ /the/ "HTML document model", but several ones.  Even at the
time this wording was first used (1999/2000) there were at least three DOM
APIs: the NN4 DOM, the MSHTML DOM, and W3C DOM HTML Level 1; the first two
were not called "DOM" at the time (the prevalent term was "DHTML", cf. the
MSDN Library which still employs that term), and the latter one (obsolete
since 2003) did not specify the `window' property (nor do its successors to
date).  As a rule of thumb, double-check all statements made by ECMA people
about HTML or the DOM, and by W3C Specifications about ECMAScript
implementations (for example, there are similar errors in examples in HTML
4.01).

Yes, blah blah.
Finally, a property can _not_ *be* an object; it can only _refer_ to one. 
(Unfortunately, the Specification employs sloppy wording in several
normative places, while being rather unnecessarily pedantic in others
[especially in Edition 5]; a property can _not_ have an object for its
value, but only a reference to the object.)

Yes, and what does this have to do with anything?

And, what's wrong with global.window= global; ?
On a personal note, Jorge has been told these and other basic things a
hundred times before already, but he would not listen (or try to
understand).  The record shows he is likely only trolling; ignore him.

You're saying nonsenses -again- Pointy.
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top