Prototype behavior

R

Roberto Sileoni

While i was trying to understand prototype behavior,
i wrote this sample code:

function MyString() {
}

MyString.prototype = new String()

var a=new MyString("aaaaa");
alert(a.valueOf());

When i run this code on Firefox it raise an error like this:
String.prototype.valueOf called on incompatible object.

Can someone help me to understand what happens and why this code
does not work.

Thanks a lot and sorry for my english.
 
M

Michael Winter

On 25/05/2005 00:39, Roberto Sileoni wrote:

[snip]
function MyString() {
}

MyString.prototype = new String()

var a=new MyString("aaaaa");
alert(a.valueOf());

When i run this code on Firefox it raise an error like this:
String.prototype.valueOf called on incompatible object.

Certain predefined methods are considered generic; they can be used with
any object. For example, calling charAt (another String method) on
MyString should pose no problems as charAt will convert the MyString
instance to a string value, and then return the character at the given
offset.

However, some methods are not transferable; if they are called from an
object that is not of the correct type, the ECMAScript engine with throw
a TypeError exception. The String.prototype.valueOf method is one such
non-transferable method.

User-defined objects, such as MyString, will always be Object instances.
You can give them prototypes of other built-in types, but they will
still be Objects.

[snip]

Does that help at all?

Mike
 
G

Grant Wagner

Michael Winter said:
On 25/05/2005 00:39, Roberto Sileoni wrote:

[snip]
function MyString() {
}

MyString.prototype = new String()

var a=new MyString("aaaaa");
alert(a.valueOf());

When i run this code on Firefox it raise an error like this:
String.prototype.valueOf called on incompatible object.

Certain predefined methods are considered generic; they can be used
with any object. For example, calling charAt (another String method)
on MyString should pose no problems as charAt will convert the
MyString instance to a string value, and then return the character at
the given offset.

Nope:

function MyString() { }
MyString.prototype = new String();
var a = new MyString("aaaaa");
alert(a.charAt(1)); // Line 14

Error: String.prototype.toString called on incompatible Object
Source File: file:///c:/DOCUME~1/grantw/LOCALS~1/Temp/hs~new.htm
Line: 14

The only solution to this is to wrap a String object inside your object
and expose the methods you want to allow on your object:

function MyString(newString) {
var s = newString;
this.charAt = function(n) { return s.charAt(n); }
}
var a = new MyString("aaaaa");
alert(a.charAt(1));

But if you're going to that, you might as well just write a factory
"class":

var MyStringFactory = new function() {
this.getInstance = function(newString) {
var s = new String(newString);
s.repeat = function(n) {
return (new Array(n + 1)).join(this);
}
s.trimRight = function() {
return this.replace(/\s$/, '');
}
s.trimLeft = function() {
return this.replace(/^\s/, '');
}
s.trim = function() {
return this.trimRight().trimLeft();
}
return s;
}
}();
var a = MyStringFactory.getInstance("abc");
alert(a.valueOf() + ';' + a.repeat(5));

This is probably something like how I'd do it. You can now "extend" the
String class however you want without sacrificing any built-in
functionality. Instead of making an object that "looks like" a String,
you're creating a String with additional properties/methods.
 
M

Michael Winter

[snip]
Certain predefined methods are considered generic; they can be used
with any object. [...]

Nope:

function MyString() { }
MyString.prototype = new String();
var a = new MyString("aaaaa");
alert(a.charAt(1)); // Line 14

You aren't providing any mechanism to get at that value passed to the
constructor. The Object.prototype.toString method that would be used by
String.prototype.charAt no longer exists on that object. Instead, it
would use String.prototype.toString, which, like
String.prototype.valueOf, is a non-transferable method.

If you change the constructor to:

function MyString(value) {
this.toString = function() {return value;};
}

then the charAt method works just fine.

[snip]

Mike
 
G

Grant Wagner

Michael Winter said:
[snip]
Certain predefined methods are considered generic; they can be used
with any object. [...]

Nope:

function MyString() { }
MyString.prototype = new String();
var a = new MyString("aaaaa");
alert(a.charAt(1)); // Line 14

You aren't providing any mechanism to get at that value passed to the
constructor. The Object.prototype.toString method that would be used
by String.prototype.charAt no longer exists on that object. Instead,
it would use String.prototype.toString, which, like
String.prototype.valueOf, is a non-transferable method.

If you change the constructor to:

function MyString(value) {
this.toString = function() {return value;};
}

then the charAt method works just fine.

Hmm. Well in that case:

function MyString(value) {
if (value == null) {value = 'blah';}
this.toString = function() {return value;}
this.valueOf = function() {return value.valueOf();}
}
MyString.prototype = new String();
var s = new MyString(0);
alert(s.valueOf());

However, you still need to wrap any other methods and properties that
won't work properly (like I did with valueOf()). It also just occurred
to me that in my "factory class" example you don't need
a -MyStringFactory- at all:

String.getInstance = function(newString) {
var s = new String(newString);
// ...
}
var s = String.getInstance("abc");


Decisions, decisions. I think I still lean towards having a factory
method on the String object rather than creating a new custom object
type but I'll have to think about it a bit.
 

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
474,431
Messages
2,571,677
Members
48,796
Latest member
Greg L.

Latest Threads

Top