1

I would like to be able to create a class that looks like the object that was passed to it, and add new methods to this object.

Here is what i have so far:

Node = function(data) {
  Node = data;
  Node.constructor.prototype = new data.constructor();
  Node.constructor.prototype.testProp = "Hello!";
  return Node;
};

node = Node('abc');
console.log(node); // abc
console.log(node.testProp); // Hello!
var testArray = [];
testArray.push(node);
console.log(testArray); // ['abc']

Whats the problem with this implementation?

The Node class looks like a String in this sample but every string now has a testProp property.

console.log(node.testProp) // 'Hello!'
console.log("something".testProp) // 'Hello!'

My question:

How i should implement a class that would behave like the object that was passed in the constructor without affecting all the other objects of the same class?

Why?

The reason why i am asking this is that i want the element data (String, Number, Array, Object, etc) to be accessible without using any methods or props for example console.log(Node.value);, instead i just want to use console.log(Node);

Thanks!

Kauê Gimenes
  • 1,278
  • 1
  • 13
  • 30
  • 2
    Well, in your example you are passing a *primitive* value, not an object. You cannot "extend" a primitive value without affecting all primitives of the same type. If you work with actual objects, you probably just want to assign the new properties *directly* to it, not its prototype. But I guess I don't really understand what "class that looks like the object" means. Do you want to create subclasses dynamically? – Felix Kling Mar 19 '15 at 00:14
  • @FelixKling Thanks for the clarification Felix, you see any workaround that would make this implementation possible for primitive values? – Kauê Gimenes Mar 19 '15 at 00:22
  • @FelixKling What i want is to be able to console.log(node) and the output to be a string for example, and the same behave in an array. – Kauê Gimenes Mar 19 '15 at 00:23
  • 2
    If you want to control how any object is converted *to* a primitive value, you can overwrite its `toString` and `valueOf` methods. See http://stackoverflow.com/questions/27989285/why-does-false-evaluate-to-false-while-false-evaluates-to-true/27989332#27989332 for an example. – Felix Kling Mar 19 '15 at 00:24
  • @FelixKling Ohh, nice but take a look at this example https://gist.github.com/NV/282770 if you console.log(m) its actualy a object, even that its overwriting the valueOf and toString. – Kauê Gimenes Mar 19 '15 at 01:12
  • Yep, `console.log` doesn't convert the argument to a primitive. I'm just making suggestions based on what I understand you are trying to do. But as I said, it's not very clear to me. If you want to extend a specific primitive value, that's not possible. – Felix Kling Mar 19 '15 at 01:14
  • @FelixKling I want to be able to console.log(node) as a primitive. And i belive jQuery does something similar so this must be possible in some way. – Kauê Gimenes Mar 19 '15 at 01:42
  • Nope, jQuery definitely doesn't. – Felix Kling Mar 19 '15 at 01:43
  • @FelixKling If you run this console.log($('p')[0]) the result is the html of the element as a string. Thats similar to what i want. – Kauê Gimenes Mar 19 '15 at 02:12
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/73287/discussion-between-kaue-gimenes-and-felix-kling). – Kauê Gimenes Mar 19 '15 at 02:12
  • 2
    That is just the console showing the HTML representation of the DOM element. That's a browser specific behavior and has nothing to do with JavaScript or jQuery. This works with every DOM element, just try `console.log(document.body)`. – Felix Kling Mar 19 '15 at 04:08
  • @FelixKling Yeah, thats true. I was reading about getters and setters in javascript and found out that is possible to change the get & set for a property. But its possible to change the get & set for the class itself? Maybe thats a possible solution to my problem. – Kauê Gimenes Mar 19 '15 at 04:24
  • Only properties can be getters and setters. Values don't have getters or setters. Maybe this helps to understand the issue: http://stackoverflow.com/q/22834047/218196 – Felix Kling Mar 19 '15 at 04:36
  • @FelixKling Hmm thanks for the clarification again =). I have another question, in my sample why i am not able to use my class with node = new Node('abc') instead of node = Node('abc')? – Kauê Gimenes Mar 19 '15 at 12:21
  • Why would you need this at all? I think you are trying to abuse the dynamic nature of JavaScript. What you need is just clone of the given object, which does not fall under constructor's responsibility or definition. Why don't you just create a clone() method without going through the constructor? constructor is supposed to initialize an instance (that already is given), so it can't really return different types based on the argument. – Tengiz Mar 27 '15 at 14:16
  • @Tengiz My class holds a value like an array, but i don't want the user to access the value using class.value, instead i just want the user to console.log(class). – Kauê Gimenes Mar 28 '15 at 00:01
  • If that's all you want, you can make array with value to be private (by using closure), and it won't be accessed by the user. And you can ask user to call GetValue() method for console.log. Basically, point is that these are more conventional approaches with such situations. You don't want to design an object that behaves strange. – Tengiz Mar 30 '15 at 13:19

2 Answers2

3

The following does not work with primitive types (like string and number) but works with objects:

node = function(data) {//do not capitalize non constructor functions
  ret = Object.create(data);
  ret.testProp = "Hello!";
  return ret;
};

var proto = {name:'hello'};
test = node(proto);
console.log(test); //{testProp: "Hello!", name: "hello"}
console.log(test.testProp); // Hello!

Note that if you mutate proto you mutate test:

proto.name='changed';
console.log(test);//{testProp: "Hello!", name: "change"}
HMR
  • 37,593
  • 24
  • 91
  • 160
2

getter and setter on global

This solution removes the need for the "." but it only works on globals.

var test = {value: "Hello World!"};

Object.defineProperty(window,'node_a', {
  get: function() {return test.value;},
  set: function(newValue) {test.value = newValue;},
  enumerable: true,
  configurable: true
});

function nodeValue() {
  console.log(node_a);
  node_a = "Foo Bar";
  console.log('test.value=' + test.value);
  console.log(node_a);
}

nodeValue();

Output:

Hello World!
test.value=Foo Bar
Foo Bar

toString and valueOf

You can convert your object to a string or number by creating toString and valueOf functions. This will get you close but we still have serialization of the value when it's not acted on by a string.

function Node(data) {
  this.data = data;
  this.test = 'world!';
}

Node.prototype.toString = function() {
  return this.data;
};

Node.prototype.valueOf = function() {
  return this.data;
}

var n = new Node('Hello');

console.log(n);
console.log(n+"");
console.log(n.test);

Output

 Node { data="Hello", test="world!", toString=function(), more...}
Hello
world!
wolfhammer
  • 2,641
  • 1
  • 12
  • 11