Object oriented javascript question

J

Jeremy

Hi, I'm trying to figure out how to modify the property of an object
from within an object descended from that parent object. Here's the
code:

//new object
function A() {
this.first = "one";
this.second = "two";
this.third = "three";
}

//add the 'typing' object to our object
A.prototype.myType = {
firstType : function() {

},
secondType : function() {
//how do I alter the value of function A's "second" property from
here?
// 'this' is scoped to the current object
},
thirdType : function() {

}
}

B = new A();

var typeIt = "secondType";

if (typeIt in B.myType) {
B.myType['typeIt'];
}

I want to change the value of A.second (well, once I invoke it as "B",
it will be B.second) to be something other than "two." The 'this'
keyword within A.myType references the myType object. How do I
reference this instance of A, however?

Thanks, in advance.
 
R

RobG

Hi, I'm trying to figure out how to modify the property of an object
from within an object descended from that parent object. Here's the
code:

//new object
function A() {
this.first = "one";
this.second = "two";
this.third = "three";
}

When A is called as a constructor, its this keyword is set as a
reference to a newly created object. Objects constructed using new A()
(which might be called instances of A) will each have properties
first, second and third with the values assigned above.

//add the 'typing' object to our object
A.prototype.myType = {

That doesn't add it to "our object", it creates a property called
myType on A.prototype and assigns it a reference to a new object.
Objects created from constructor A when it has this prototype object
will inherit the myType property.

firstType : function() {

},
secondType : function() {
//how do I alter the value of function A's "second" property from
here?

Function A doesn't have a "second" property, so it is impossible to
modify it. It will create a "second" property of instances when called
with the new keyword.

What you have done is added a myType property to A.prototype and
assigned it a reference to an object with a secondType property that
is a function. To call it from an instance of A you'd need:

a_Instance.myType.secondType()


in which case, secondTypes's this keyword will be a reference to
myType (because that's how it's called). The simple solution is to add
the secondType function directly to A.prototype:

A.prototype.secondType = function(){...
A.prototype.thirdType = function(){...


Alternatively, replace A.prototype with the myType object. Since the
functions are setting values, I'd call them "set...":

A.prototype = {
setFirstType: function(value){
this.first = value;
},
setSecondType: function(value){
this.second = value;
},
setThirdType: function(value) {
this.third = value;
}
};

Note that if you adopt the second approach, it will only affect
instances of A created after the assignment. Instances created before
then will use the previous A.prototype.

// 'this' is scoped to the current object

The value of a function's this keyword has nothing to do with scope.
Its value is completely under the control of the caller.

},
thirdType : function() {
}

}

B = new A();

By convention, variable names starting with a capital letter are
reserved for constructors and constants (which are usually all
capitals).

var typeIt = "secondType";

if (typeIt in B.myType) {
B.myType['typeIt'];
}

I want to change the value of A.second (well, once I invoke it as "B",
it will be B.second)

There is no A.second, you want to change B.second.

to be something other than "two." The 'this'
keyword within A.myType references the myType object. How do I
reference this instance of A, however?

There is no A.myType, nor is the myType property on A's prototype
chain. Property resolution proceeds through the internal [[prototype]]
property, which is (usually) a completely different set of objects to
the public prototype property.

Here's a full example:

function A() {
this.second = 'second';
}

A.prototype = {
setFirstType: function(value){
this.first = value;
},
setSecondType: function(value){
this.second = value;
},
setThirdType: function(value) {
this.third = value;
}
};

var anA = new A();

alert(anA.second); // shows 'second'

anA.setSecondType('new second type');

alert(anA.second); // shows 'new second type'


Of course you are changing public properties of instances, next you'll
want to know how to keep them private and only change them using
privileged functions (getters and setters). That's been covered here
too (and in various blogs). Come back when you're ready. :)
 
T

Thomas 'PointedEars' Lahn

RobG said:
Function A doesn't have a "second" property, so it is impossible to
modify it. It will create a "second" property of instances when called
with the new keyword.

What you have done is added a myType property to A.prototype and
assigned it a reference to an object with a secondType property that
is a function. To call it from an instance of A you'd need:

a_Instance.myType.secondType()

in which case, secondTypes's this keyword will be a reference to
myType (because that's how it's called). The simple solution is to add
the secondType function directly to A.prototype:

A.prototype.secondType = function(){...
A.prototype.thirdType = function(){...

Alternatively, replace A.prototype with the myType object. Since the
functions are setting values, I'd call them "set...":

A.prototype = {
setFirstType: function(value){
this.first = value;
},
setSecondType: function(value){
this.second = value;
},
setThirdType: function(value) {
this.third = value;
}
};

Note that if you adopt the second approach, it will only affect
instances of A created after the assignment. Instances created before
then will use the previous A.prototype.

Further, since the value of the `prototype' property would be a reference to
a newly created Object instance (through the Object initializer, `{'...`}'),
the inherited `constructor' property of the latter instances would be
`Object', not `A' anymore. This can be confusing. It is possible to add a
corresponding user-defined `constructor' property to `A.prototype', but by
contrast to the built-in `constructor' property it will be enumerable by
default. ECMAScript Edition 5 specifies the Object.defineProperty() and
Object.defineProperties() methods to define a non-enumerable property; they
are implemented in Google V8 since version 2.1 (Chrome 5.0.342) and Apple
JavaScriptCore since version 533.16 (Safari 4.0.4).

As an alternative, the original prototype object can be kept and just
augmented with properties. A way to do this is for-in iteration:

var o = a_Instance.myType;
for (var p in o)
{
A.prototype[p] = o[p];
}

Note that a *shallow* copy of all *enumerable* properties, including
inherited ones, of the object referred to by `o' will be created. If only
own enumerable properties should be copied, the o.hasOwnProperty() method
can be used.
var typeIt = "secondType";

if (typeIt in B.myType) {
B.myType['typeIt'];
}

I want to change the value of A.second (well, once I invoke it as "B",
it will be B.second)

There is no A.second, you want to change B.second.

And remove the apostrophes around `typeIt', else the name of the accessed
property would be `typeIt', not `secondType'. But it would still have no
overly useful effect.


PointedEars
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas said:
RobG wrote: [...]
ECMAScript Edition 5 specifies the Object.defineProperty() and
Object.defineProperties() methods to define a non-enumerable property;
they are implemented in Google V8 since version 2.1 (Chrome 5.0.342) and
Apple JavaScriptCore since version 533.16 (Safari 4.0.4).
^^^^^

That should be Safari 5, not 4.0.4 (build number is right, though).

Also see <http://kangax.github.com/es5-compat-table/>

No. As a matter of fact, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US)
AppleWebKit/533.16 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10"
supports both Object.defineProperty() and Object.defineProperties().


PointedEars
 
T

Thomas 'PointedEars' Lahn

kangax said:
Thomas said:
kangax said:
Thomas 'PointedEars' Lahn wrote:
RobG wrote:
[...]
ECMAScript Edition 5 specifies the Object.defineProperty() and
Object.defineProperties() methods to define a non-enumerable property;
they are implemented in Google V8 since version 2.1 (Chrome 5.0.342)
and Apple JavaScriptCore since version 533.16 (Safari 4.0.4).
^^^^^

That should be Safari 5, not 4.0.4 (build number is right, though).

Also see<http://kangax.github.com/es5-compat-table/>

No. As a matter of fact, "Mozilla/5.0 (Windows; U; Windows NT 5.1;
en-US) AppleWebKit/533.16 (KHTML, like Gecko) Version/4.0.4
Safari/531.21.10" supports both Object.defineProperty() and
Object.defineProperties().

Interesting...

My Safari 4.0.5 [1] on Mac OS X has neither `Object.defineProperty` nor
`Object.defineProperties`; Safari 5 [2] has both.

[1] Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us)
AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7

[2] Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-us)
AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16

I don't understand how your 4.0.4 ended up with 533.x webkit build.

JFTR: I had the older version before, but thought it was an error to be
corrected. (So I did. D'oh.)

Now that you pointed out the version mismatch too, I came to suspect that it
was because I had installed Safari 5.0 in the same Wine subtree. Since I
suddenly had problems running any Safari browser tonight (which might have
to do with a winecfg on the wrong subtree), I have just reinstalled version
4.0.4 again into a clean subtree, and you are right:

"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/531.21.8
(KHTML, like Gecko) Version/4.0.4 Safari/531.21.10" does not support
Object.defineProperty() or Object.defineProperties(). (See the tests
below.)

Thank you. (Note to self: Always install a Safari version into its own Wine
subtree.)

It would appear that I have to double-check all JavaScriptCore test results
for the ECMAScript Support Matrix¹ so far. BTW, those are tests for
functionality, and will be available with the next revision of the Matrix.
As a preview, for those two methods they currently are (PHP code cleaned,
pretty-printed):

var o = new Object(),
b = isMethod(Object, 'defineProperty')
&& Object.defineProperty(o, 'a', {
value: {b: 'c'},
writable: false,
configurable: false,
enumerable: false
})
&& (typeof o.a == 'object') && o.a
&& (o.a.b == 'c')
&& (o.a = 42) && (o.a != 42);
delete o.a;
b = b && (typeof o.a != 'undefined');
if (b)
{
var found = false;
for (var p in o)
{
if (p == 'a')
{
found = true;
break;
}
}
}
b && !found;

and

var o = new Object(),
b = isMethod(Object, 'defineProperties')
&& Object.defineProperties(o, {
a: {
value: {b: 'c'},
writable: false,
configurable: false,
enumerable: false
},
b: {
value: {c: 'd'},
writable: false,
configurable: false,
enumerable: false
}
})
&& (typeof o.a == 'object') && o.a
&& (o.a.b == 'c')
&& (o.a = 42) && (o.a != 42)
&& (typeof o.b == 'object') && o.b
&& (o.b.c == 'd')
&& (o.b = 42) && (o.b != 42);
delete o.a;
delete o.b;
b = b && (typeof o.a != 'undefined') && (typeof o.b != 'undefined');
if (b)
{
var found = false;
for (var p in o)
{
if (p == 'a' || p == 'b')
{
found = true;
break;
}
}
}
b && !found;

The result of either program determines if the corresponding feature is
considered to be supported by an implementation. A true-value indicates
that it is supported, a false-value that it is not.

AFAICS, the tests are only incomplete in that they do not test that the
attributes of a property that does not have the [[Configurable]] attribute
set must not be possible to redefine with Object.defineProperty() (except of
its value).

Suggestions welcome.


PointedEars
___________
¹ <http://PointedEars.de/es-matrix>
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top