1

I'm trying to create an Exception class in JavaScript and I'm having a slight problem with the prototyping although I have been using JavaScript for a a long time I have never really used prototyping properly

Right so here is my code,

// load the Object Prototype
Exception = Object;

Exception.prototype = new function () {
    // set defaults
    this.name = "Exception";
    this.message = "";
    this.level = "Unrecoverable";
    this.html = "No HTML provided";

    // code so that the console can get the name from this.name insted of using [object Object]
    this.getName = function(){
        return this.name;
    }

    // this is the exec of the object prototype the code that is executed when the new Exception call is made
    this.exec = function(msg, name, lvl, html){
        // create a return variable
        var ret;
        // check that msg is defined and is not empty
        if(typeof(msg) == "undefined" || msg == ""){
            throw new Exception("Can't throw exception without a msg");
        }

        // set up this Exception Object values
        this.name = (typeof(name) == "undefined")? this.name : name;
        this.level = (typeof(lvl) == "undefined")? this.level : lvl;
        this.message = msg;
        this.html = (typeof(this.html) == "undefined")? this.html : html;

        // delete the getName function so it does not get set though to the console
        delete this.getName;
        // save the Exception object to our return variable
        ret = this;
        // re add the getName object to the Exception Object so that it can be called from the console
        this.getName = function(){
            return this.name;
        }
        // return the saved Exception object without the getName method
        return ret;
    }
}

but for some reason in the console it's returning the String given as the argument for msg

here is the console output i recive

throw new Exception("test");
test
    0: "t"
    1: "e"
    2: "s"
    3: "t"
    length: 4
    __proto__: String

any help would be greatly appreciated

MikeM
  • 13,156
  • 2
  • 34
  • 47
Barkermn01
  • 6,781
  • 33
  • 83
  • 3
    There are a lot of funky things going on in your code. I recommend to start fresh and read [MDN - Introduction to Object-Oriented JavaScript](https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript) first. – Felix Kling Jan 12 '13 at 15:32
  • 2
    JavaScript has no classes. `Exception = Object` doesn't load a prototype, it makes `Exception` another name for `Object`. I don't know what you think `new function () ...` is doing, but you're overwriting `Object.prototype` there. WTF? – melpomene Jan 12 '13 at 15:32
  • no im not as if i did that jQuery would stop working, as it did when i tryoed to use `Object.prototype.getName = function(){ return this.name }` – Barkermn01 Jan 12 '13 at 15:33
  • 2
    `Object.prototype` does not seem to be writable (at least in FF), but you are at least *trying* to overwrite it. – Felix Kling Jan 12 '13 at 15:38
  • now Im really confused is JavaScript a Pointer based or not? `var test = "test"; var test2 = test; test2 = "test2", console.log([test, test2])` there different if it was pointer based they would match as the var test2 would be a memory pointer to the memory that holds var test but if i change test2 it does not change test1 there for they can't be the same memory being affect (for other pointer based is the same as referenced based, pointer to memory is a memory reference address) – Barkermn01 Jan 12 '13 at 15:51
  • @MartinBarker You're barely comprehensible but you're not doing `x = y; x = somethingelse;` in your code, you're doing `x = y; x.prop = somethingelse;`. I'm not sure what this has to do with pointers, though. – melpomene Jan 12 '13 at 15:56
  • When dealing with objects you are passing/assigning references but primitive values are just copied. `Object` is a function and therefore an object. See http://jsfiddle.net/UKueS/. – Felix Kling Jan 12 '13 at 16:00
  • @FelixKling "primitive values" have no observable identity so it makes no sense to talk about copying. – melpomene Jan 12 '13 at 16:03
  • @melpomene: How would you call it then? Primitive values are still stored in memory, so they can be identified *somehow* (at least that's how I see it). – Felix Kling Jan 12 '13 at 16:04
  • @FelixKling What do you mean by "stored in memory" (and "identified")? – melpomene Jan 12 '13 at 16:05
  • @melpomene: Any kind of data is stored in memory (either on the stack or the heap) and there is some sort of identification to access the place in memory where a datum is stored. That's what I mean. – Felix Kling Jan 12 '13 at 16:07
  • `x = y` ether copies or pointers that's it all you can do with memory depending on the lang C# classes are pointers if i had `class Test{ public String name = "test"} Test test = new Test(); Test test2 = test` if I changed test2.name it would change test.name as well but in a copy based though same code if i changed test2 it would not change test as it copies the memory that is how they work so witch one is JS pointer or copy as pointer would mean im changing Object, just trying to understand so i can try and fix if i don't understand it how can i fix, – Barkermn01 Jan 12 '13 at 16:08
  • @FelixKling I don't think you know what you're talking about. – melpomene Jan 12 '13 at 16:10
  • @melpomene: Then please explain it to me instead of pointing me out. And you haven't answered my question yet. – Felix Kling Jan 12 '13 at 16:10
  • The argument you just had is my point it has to work one of two ways thats what im trying to work out witch one is it, @FelixKling said it has to be stored in memory no matter what so why differ between copy and ref, that would be stupid as your would have to have two managements one for copied and one for reffed and then how JS would know witch one is what as its not type safe anway – Barkermn01 Jan 12 '13 at 16:11
  • @FelixKling If you're referring to "How would you call it then?", I don't understand what the "it" in question is. – melpomene Jan 12 '13 at 16:11
  • @melpomene: The process of that is happening when you pass a primitive value to a function. Or assigning a variable holding a primitive value to another variable. – Felix Kling Jan 12 '13 at 16:13
  • @FelixKling I'd call it "assignment". – melpomene Jan 12 '13 at 16:14
  • @melpomene: And what is happening during that assignment to objects and primitive values? – Felix Kling Jan 12 '13 at 16:15
  • 1
    @FelixKling There is no difference between objects and primitive values. After `x = y`, `x` has the same value that `y` had before. – melpomene Jan 12 '13 at 16:16
  • 1
    @MartinBarker: I don't know C# so I cannot give you an anology. All I can say is that primitive values are copied (duplicated) whereas with object, the value that an variable has is actually a reference to that object. On assignment, that reference is duplicated but not the object it points to. Does this help? I think it's the same in Java, but I'm not sure. – Felix Kling Jan 12 '13 at 16:17
  • that is what im trying to work out!!!! some one go look at a Post Room computer if you don't understand, This is Copy based `Pidgin hole 2 has the value "test" inside it, pidgin hole 3 then copies the value of pidgin hole 2 into its self then change pidgin hole 3 to have "hello"` in this scenario pidgin hole 2 would have "test" and pidgin hole 3 would have "hello" This is referenced based `Pidgin hole 2 has the value "test" pidgin hole 3 references pidgin hole 2 I then change pidgin hole 3 to "hello"` in this scenario both pidgin hole 2 and 3 would have "hello" witch one is JavaScript? – Barkermn01 Jan 12 '13 at 16:21
  • 1
    @MartinBarker: The first one. – Felix Kling Jan 12 '13 at 16:22
  • Thanks and the last comment was the best way i could simplify what i was trying to get out without being more confusing – Barkermn01 Jan 12 '13 at 16:22
  • @melpomene - Sorry for interrupting but this is such a fascinating debate that I'm compelled to share my own views on this - what I think Felix Kling is trying to convey is that primitives in JavaScript are copied by value, while objects are copied by reference. Copying by value means copying the actual bytes that constitute the value to another memory location. Copying by reference means copying the memory address pointing to the actual value to another memory location. Here I'm using the term memory location to refer to a variable in JavaScript. Hope that clears things up. Happy New Year! =) – Aadit M Shah Jan 12 '13 at 16:47
  • @AaditMShah That concept doesn't exist in JavaScript. All values are copied the same way (which you can call "by value" or "by reference"). There are no memory addresses in JavaScript. – melpomene Jan 12 '13 at 16:49
  • @AaditMShah: Thanks for helping out :) That's exactly what I meant. – Felix Kling Jan 12 '13 at 16:49
  • @melpomene: 1) So now you are using the term "copy" too, although you said it's not applicable since primitive values don't have an "identity". What is it now? 2) Where is data stored then? Is it flying in the air? It most be stored *somewhere*, so where? You always keep saying "it's not like that" but you don't explain or provide a reference to how it actually is. I have to start believing that you are just trolling. – Felix Kling Jan 12 '13 at 16:52
  • 1
    And again with the stop being stupid stuff Everything on a computer that is used has to be read from memory no matter what thats what the Mach 1 computer was the ability to run application code from memory as well as save information to memory, no matter what is is a number `300` or a function `console.log("hello")` it has to be saved in the ram and the only way to read that data is to know where in the ram it is saved – Barkermn01 Jan 12 '13 at 16:54
  • 2
    @melpomene There are no memory address exposed to the programmer, but there are memory addresses in JavaScript. However only the JavaScript engine can access and manipulate them. In fact every variable in every programming language ever created has memory addresses associated with it because __variables are stored in main memory__. Just because you can't access and manipulate them doesn't mean that they don't exist. That's like an ostrich burying it's head in the sand and pretending that the world doesn't exist. What we're talking about is an implementation detail however. It's low level stuff – Aadit M Shah Jan 12 '13 at 16:55
  • http://en.wikipedia.org/wiki/Pigeonhole_principle this is what we're on about every thing must be in a pideonhole – Barkermn01 Jan 12 '13 at 16:58
  • @AaditMShah Programming languages are mathematical models. I can run code in my head or with pen and paper. I don't need concepts like memory addresses or stack/heap to do that. If I can't access and manipulate something, then it does in fact not exist as far as my model is concerned. "Variables are stored in main memory" only in a particular implementation you're thinking of. It's not an inherent property of the language. – melpomene Jan 12 '13 at 17:05
  • @MartinBarker I've stopped trying to decipher your comments. They're incomprehensible to me (try using more punctuation maybe?). – melpomene Jan 12 '13 at 17:06
  • @FelixKling No, data doesn't have to be stored in any particular location. All that's required is that after `x = FOO`, you will get `FOO` back when you read from `x` - somehow. The rest is implementation details. – melpomene Jan 12 '13 at 17:09
  • @MartinBarker - JavaScript is not pointer based. It's object oriented and it has references, but it has no pointers. A pointer is a variable whose value is the memory address of another variable. JavaScript doesn't allow you to get the memory address of a variable. Hence they are no pointers in JavaScript. A reference however is an alias (another name) of another variable. So if `A` is a reference of `B` then they are both the same. If I change something of `A` then the change will be reflected on `B` by automatically because `A` and `B` are the same thing in memory. They occupy the same space – Aadit M Shah Jan 12 '13 at 17:09
  • @melpomene: Ah, now I see the confusion... yes, that's exactly what we are talking about: How JavaScript is *implemented* on today's most prevalent computer architecture. Sorry if that was not clear. – Felix Kling Jan 12 '13 at 17:10
  • All common computers work the same as this http://en.wikipedia.org/wiki/Manchester_Mark_1 and by common i'm removing quantum computers form this and i dont think you get JS on a quantum computer lol – Barkermn01 Jan 12 '13 at 17:15
  • @melpomene - Precisely. I'm talking about copying by value and copying by reference in terms of the implementation. However this implementation detail does have a profound impact on the language itself. Compare [copying by value](http://jsfiddle.net/hZzxr/) and [copying by reference](http://jsfiddle.net/hZzxr/1/). – Aadit M Shah Jan 12 '13 at 17:16
  • @AaditMShah If it has an impact on the language, it's not an implementation detail, it's part of the language. The reason `y.z` is undefined in your first example is that the comment *set a property on x* is wrong: `x.z` is also undefined. That's because primitive values don't have properties. `(5).z` "autoboxes" 5 into a `Number` object, accesses its `z` property, then throws the object away. The number itself (still stored in `x` and `y`) is unchanged. Primitive values have no observable identity because they have no mutable structure. – melpomene Jan 12 '13 at 17:25
  • 1
    @MartinBarker - Wow it took me a while to understand what you were trying to say, but I finally got it. Say you have a pigeon in hole `1` whose name is `A`. Here the pigeon is the `value`, the hole number `1` is the memory address and the name of the pigeon `A` is the variable name. Now if I take the DNA of pigeon `A` and create a new (but identical) pigeon `B` and put him in hole `2` then I have two identical (same value) but separate (different memory addresses) pigeons. This is called `copy by value` and this is what happens when you copy a primitive value in JavaScript. – Aadit M Shah Jan 12 '13 at 17:27
  • @melpomene - Ah, yes. It is `undefined`. My bad. Then it's truly only an implementation detail. – Aadit M Shah Jan 12 '13 at 17:38
  • 1
    @melpomene: If you are more interested in the theory, you might find http://cs.stackexchange.com/ interesting. After clarifying what we were talking about, I completely agree with you, but I don't think your comments were relevant to the problem. Have a good day! – Felix Kling Jan 12 '13 at 17:41
  • @melpomene - I understand now what you were trying to say. We may say that a value is either copied by value or by reference, but in the implementation it's simply assigning one variable to another - it doesn't matter whether it's copied by reference or by value. Am I correct? – Aadit M Shah Jan 12 '13 at 17:42

4 Answers4

2

Judging from your code I think this is what you want to do:

Exception.prototype = new Error;
Exception.prototype.constructor = Exception;

function Exception(message, name, level, html) {
    if (typeof message !== "string")
        throw new Exception("Expected an error message.");

    this.message = message;
    this.name = name || "Exception";
    this.level = level || "Unrecoverable";
    this.html = html || "No HTML provided";
}

See the MDN docs for more information.

Edit: If you're having problems with prototypal inheritance then I suggest you spend some time learning about it: https://stackoverflow.com/a/8096017/783743

Community
  • 1
  • 1
Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • Thanks this is all i was trying to do and i did say i dont use prototype that much – Barkermn01 Jan 12 '13 at 16:27
  • @MartinBarker - If you find prototypal inheritance in JavaScript to be too much of a brain bender then there are lots of classical OOP simulation libraries available to help you out. For example take a look at [javascript/augment](https://github.com/javascript/augment). It's the smallest library out there, and it looks a lot like classes in other languages. – Aadit M Shah Jan 12 '13 at 16:33
  • Doesn't handle `message` properly and the call to `Error` is redundant. – MikeM Jan 12 '13 at 17:13
  • @MikeM: `message` is handled by the parent "class". That's what the call to `Error` is for. We are extending `Error` after all. – Felix Kling Jan 12 '13 at 17:16
  • 1
    @Felix Kling. Try the following: `try { throw new Exception("test") } catch ( err ) { console.log( err.message) }` – MikeM Jan 12 '13 at 17:20
  • 1
    @MikeM: Strange... that might be because `Error` is a host object and does not behave as expected. I had a look at some of my code and I also set the `message` property. But it would have been more helpful to point that out from the beginning instead of saying "this is wrong" ;) – Felix Kling Jan 12 '13 at 17:26
  • @FelixKling - That's a problem with a lot of host objects. Try subclassing an array and see the `length` property. I think it's because native constructors are actually factory functions which explicitly return an instance. So using them as a mixin is pointless. – Aadit M Shah Jan 12 '13 at 17:49
  • @MikeM - I updated my answer to reflect yours. Hope you don't mind. =) – Aadit M Shah Jan 12 '13 at 17:50
  • @MartinBarker - I'm just curious, why are you trying to create your own "exceptions" when JavaScript already has a perfectly legible `Error` constructor? – Aadit M Shah Jan 12 '13 at 17:53
  • Since according to this: http://www.lshift.net/blog/2006/07/24/subclassing-in-javascript-part-1 JavaScript isn't a prototype language but also doesn't have the mechanism for class based you're stuck trying to sort of copy Error properties and methods in Exception in the constructor of Exception. I've updated my answer to show one way of doing it. – HMR Jan 13 '13 at 03:08
  • The Reason for this is i'm building a API system using Javascript into a simulated windows desktop in the browser but i going though a caching engine so if an app has a problem i can throw an `exception in app name...` but i wanted more than that info to come though hence the Exception Class – Barkermn01 Jan 13 '13 at 12:21
  • The best way to do it is to create a function for that returns an exception with added properties. you can add object properties to the Error: ver myE=new Error(message);myE.addedData={myArray:[]:mystring:"",...} – HMR Jan 14 '13 at 04:25
1

This is an example of an Exception 'class' that meets your requirements:

/*
  This is a constructor function. It creates a new Exception object.
  Use it with the 'new' keyword , e.g. var e = new Exception("Another error").
 */
function Exception( message, name, level, html ) {   

    if ( !message ) {
        throw 'No exceptions without a message!';
    }  

    /*
      These properties may have different values for each instance so set
      them here in the constructor rather than adding them to the prototype.
     */
    this.message = message;
    this.name = name || "Exception";
    this.level = level || "Unrecoverable"; 
    this.html = html || "No HTML provided"; 
 }

/*     
  Overwrite the toString method inherited from Object.prototype
  by adding a toString method that shows the name and message.
  This method is the same for each instance so add it to the
  prototype object instead of having to create and add it to
  'this' in the constructor every time an instance is created.
 */
Exception.prototype.toString = function () {
    return this.name + ': ' + this.message; 
}

Try it out

try {
    throw new Exception("test");
} catch (e) {
    console.log( e instanceof Exception);    // true
    console.log( e instanceof Error);        // false
    console.log( e );       
    // Exception {message: "test", name: "Exception", level: "Unrecoverable"...
}

throw new Exception("test");                 // Uncaught Exception: test   


Instead of adding a toString method to Exception.prototype as above, an alternative way to ensure that the toString method of Exception objects returns their name and message is to inherit from the built-in Error object's prototype.

Exception.prototype = Error.prototype;

This also makes the default name of an Exception object 'Error' if it is not set in the Exception constructor - but that is not required in our case.

It would also mean that any properties added to Error.prototype would be present.

var e = new Exception("Another");   

Error.prototype.about = 'This is an Error';

console.log( e.about );                  // 'This is an Error'
console.log( e instanceof Exception);    // true
console.log( e instanceof Error);        // true

Or, instead of inheriting from Error.prototype we could inherit from an Error instance as in Aadit M Shah's answer.

Exception.prototype = new Error();

At the cost of creating a new Error object this avoids, as they're not now pointing to the same object, a potential issue of changes to Exception.prototype affecting Error.prototype. Changes to Error.prototype will still affect Exception.prototype as they are inherited via the Error object.

The simplest approach which seems to meet your needs may be to avoid inheriting from an Error object or Error.prototype at all, and to just add your own toString method to Exception.prototype as shown in the first example.

MikeM
  • 13,156
  • 2
  • 34
  • 47
  • the only thing i was trying to do was get the object not to say [object Object] it was to say this.name instead of [object Object] – Barkermn01 Jan 12 '13 at 17:16
  • @Martin: No, MDN is also a general JS reference (and a good one). FF only features are usually clearly marked. Every implementation is different. So if you want a normative definition you'd have to read the specification (http://es5.github.com/) but even then you cannot be sure that browsers follow it exactly. That's the sad story of JavaScript. – Felix Kling Jan 12 '13 at 17:18
0

Had to edit this because some of the info I posted before is not true or very helpful.

I said before that JavaScript does not have private methods or properties; this is untrue. Private methods can be accomplished through the use of closures: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures According to the documentation it comes with a performance penalty.

var Counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }  
})();

To "subclass" an existing object you have to use prototype as JavaScript is not a class based language. Here is an example exposing the disadvantages of prototype in JavaScript. What the following excellent document fails to mention or at least give proper warning is that "child" objects share a lazy copied instance of a "parent" object. To be more precise if two Object have the same prototype (share the same parent) they share the same INSTANCE of that parents object properties. https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model Some sample code from there:

function Employee () {
  this.name = "";
  this.dept = "general";
  this.objectVal=[];
}
function Manager () {
  this.reports = [];
}
Manager.prototype = new Employee;

var bob=new Manager();
bob.name="bob";
bob.objectVal.push("should not be in jane");
var jane=new Manager();
jane.name="jane";
console.log(jane.name);//correct
console.log(jane.objectVal);//incorrect shows ["should not be in jane"]

Simple values will be correct but object values will not be. Later in that document there is a solution given for this problem, namely:

function Manager () {
  Employee.call(this);
  this.reports = [];
}
Manager.prototype = new Employee;

What Employee.call(this) in Manager does is that it calls the Employee function with the this context of Manager. So the lines this.name, this.dept become a direct property to Manager, not through prototyping but through the fact that they are initialized in the Manager's this context.

The line Manager.prototype = new Employee; could be omitted but then (bob instanceof Employee) would evaluate to false.

So when creating your own classes you could put all properties that need to be "uniquely inherited by child instances" and are object values in the "parent" with the this.something syntax.

Methods of the "parent" can be (and according to some docs should be) specified with the prototype syntax:

//var Employee=function(){....}
Employee.prototype.EmployeeMethod=function(){}
//or
//var Employee=function(){....}
Employee.prototype={EmployeeMethod:function(){},AnotherOne:function(){},....};

If you'd like to "subclass" Error (have Error as Exceptions prototype) then the problem is that you didn't write Error, I have not been able to use Error as a prototype and use it's properties:

var Exception=function(msg){
// both don't work
//    Error.call(Exception.prototype,msg);
    Error.call(this,msg);
}
Exception.prototype=new Error;

var e=new Exception("my message");
console.log(e.message);//empty string

Not sure why this is, calling Error.call(this,msg) would set Error's properties to Exceptions properties (copies them to Exception) if they were in the Error functions body defined as this.someProperty. Calling Error.call(Exception.prototype,msg) would not copy the properties to Exception but Exception would still have those properties through the prototype chain.

The following link is to an article where you can find an elegant solution to subclassing although I didn't fully understand the code (solution is on part 2): http://www.lshift.net/blog/2006/07/24/subclassing-in-javascript-part-1

HMR
  • 37,593
  • 24
  • 91
  • 160
  • Oh ok... sorry then. Will delete my comment. – Felix Kling Jan 12 '13 at 16:27
  • the reason for the this.getName add and remove is that if the object is passed to the console it will show the method i did not want that i wanted it so that the code was there but hidden from the console bit like how native code on the Prototype shows `function(){ [native code] }` but completely hidden – Barkermn01 Jan 12 '13 at 16:35
  • @MartinBarker: I think in that case you have to overwrite the `toString` method. – Felix Kling Jan 12 '13 at 16:49
  • @HMR - What's the point of using a `for` loop to copy the properties of `new Error(msg)` onto `this` when you're going to overwrite them anyway? – Aadit M Shah Jan 13 '13 at 04:29
  • As to answer the OP; you don't have to subclass Error, you can throw whatever object you'd like to catch and would be helpful to you. Throw a new Error if you're not going to catch it so the browser can handle it properly (as an error that hasn't been caught) – HMR Jan 13 '13 at 04:44
  • the reason for not throwing a standard object like this does caused the problem's that inside the console it just shows [object Object] as and exception you then have to open it to know what happened did not want that i want it to say the Exception Name inside the console – Barkermn01 Jan 13 '13 at 11:35
  • So you'd like the (uncaught) error to give you more information. I like to use Firefox and Firebug for that as any object logged with console.log is clickable and can be inspected. You can just throw an error and see if that shows more information: var myE=new Error("my message");myE.extraInfo=extraInfo;throw(myE); The [Object:Object] you talk about is Internet Explorer, I rarely use it to program (using IE8 only to test) Usually check out mdn (Mozilla Developer Network) for browser compatibility info for certain functions. – HMR Jan 14 '13 at 03:16
0

To address the OP's reason for "subclassing" Error in the first place; there are several 3rd party JavaScript consoles out there for IE.

I've tried Firebug lite and it'll do what you need. No more [object:Object] in your console when logging something:

https://getfirebug.com/firebuglite

HMR
  • 37,593
  • 24
  • 91
  • 160