0

I have an object like this:

var source = (function(undefined) {
    var o;
    function s(o) {
        if(!o || typeof o === 'object') {
            o = o || {};
            o.a = typeof o.a === 'string' ? o.a : 'A';
            o.b = typeof o.b === 'string' ? o.b : 'B';
            this.o = o;
        }
    }
    s.prototype.v = function() {
        // some function
    };
    s.prototype.x = function() {
        // some function
    };
    return s;
})();

I want to use another similar to extend the first.

var extender = (function(undefined) {
    var o;
    function e(o) {
        if(!o || typeof o === 'object') {
            o = o || {};
            o.c = typeof o.c === 'number' ? o.c : 3;
            o.d = typeof o.d === 'number' ? o.d : 4;
            this.o = o;
        }
    }
    e.prototype.y = function(m) {
        // some function
    };
    e.prototype.z = function(m) {
        // some function
    };
    return e;
})();

I can add this.s = new source(o); to the function and with some small changes to my second object can tie everything into the first but I was thinking there might be a better way.

transilvlad
  • 13,974
  • 13
  • 45
  • 80
  • Why on earth would you call your variable `undefined`? That's one of the most evil things you can do in Javascript. – Antimony Jun 23 '13 at 21:53
  • 3
    @Antimony: No; he's creating a local `undefined` variable in case someone else sets `window.undefined`. jQuery does this. – SLaks Jun 23 '13 at 21:56
  • Why would that be a problem? And wtf why should somebody set window.undefined? – Sascha Gehlich Jun 23 '13 at 22:26
  • No idea why someone would do it. But it is a good practice to avoid problems. The code I posted here is just a sample. The real code has some validations that require undefined to be UNDEFINED. – transilvlad Jun 23 '13 at 22:34
  • @SLaks Oh, that's a neat trick. – Antimony Jun 24 '13 at 02:03

3 Answers3

1

You can use:-

Object.create(o);//where o is object to inherit from

For cross browser:-

 if (typeof Object.create !== "function")
            Object.create = function(o) {
                function F() {}
                F.prototype = o;
                return new F();
 };
pvnarula
  • 2,771
  • 18
  • 22
  • Nope. Object.create is new object function for creating a new object from the object passed to it. Modern browsers have support of this. The "if" is for the browsers not having this support. – pvnarula Jun 23 '13 at 21:58
  • Is there a solution for all browsers? Plus I need to tap into the source object from inside the main function of the second. I need to be able to pass the initialization parameter to the extender and from ti to the source. – transilvlad Jun 23 '13 at 22:04
  • The "if" is for cross browser implementation. You can send arguments to Object.create(); For more info:- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create – pvnarula Jun 23 '13 at 22:05
  • What do I do in the `else` case? – transilvlad Jun 23 '13 at 22:13
  • Nothing. You don't need any else. – pvnarula Jun 23 '13 at 22:14
  • I got it. I think the late hour is getting to me. – transilvlad Jun 23 '13 at 22:23
  • It works but, here's the thing, the source is loaded in extender before the extender is instantiated thus the options passed to the extender do not get passed to the source. If you know what I mean? – transilvlad Jun 24 '13 at 12:19
  • I am not following you. Could you please elaborate? – pvnarula Jun 24 '13 at 12:40
  • As you can see the object are designed to be instanced with an object parameter that contains the configuration options. When I instantiate the extender object and pass it the configuration, it needs to check the configuration for what it is expecting, then instantiate the source object and pass the configuration to it. The result should be an object containing the prototypes of both with a single o variable containing both configurations. – transilvlad Jun 24 '13 at 12:55
1

To combine the snippets from the other two answers to the correct solution, you probably want

var extender = (function(undefined) {
    // var o; - That's useless, `o` is a parameter of the constructor
    function e(o) {
        source.call(this, o); // invoke the `source` constructor on this instance
        o = this.o; // and get the created `o` object
        // …to extend it:
        o.c = typeof o.c === 'number' ? o.c : 3;
        o.d = typeof o.d === 'number' ? o.d : 4;
    }

    // make the prototype a new object inheriting `v` and `x`:
    e.prototype = Object.create(source.prototype);
    // then extend it
    e.prototype.y = function(m) {
        …
    };
    e.prototype.z = function(m) {
        …
    };
    return e;
})();

See docs for Object.create and call.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Uh, I didn't. Gonna comment on it :-) – Bergi Jun 24 '13 at 18:15
  • @tntu: No it's not. It would create a whole *instance*, and invoke the constructor - yet without any parameters (which would not be meaningful). You only want the inheritance, creating instance things (in the constructor) which are shared by all `extender` objects can create serious issues. – Bergi Jun 24 '13 at 18:24
  • Then both would point to the same object and you would create `y` and `z` on `source.prototype` basically. There's nothing wrong with using the [common and old shim for `Object.create`](http://stackoverflow.com/q/10141086) to make it work smoothly in older browsers – Bergi Jun 24 '13 at 18:29
  • Thank you for the effort. You win :) Feel free to remove you comments to leave this question tidy. – transilvlad Jun 24 '13 at 19:19
  • @tntu: Why "tidy"? I know that my post was not really comprehensive, and many things not well explained. The responses on your extra questions are part of my answer, and can still help others to understand the topic (which might work better if the questions were still there :-) – Bergi Jun 24 '13 at 21:05
0

Check out the following two lines:

e.prototype = source.prototype;

source.call(this, o);

They are the solution.

Of course I had to make some adjustments to the config in order to ensure both operate.

var source = (function(undefined) {
    function s(o) {
        // config
        this.o = {};
        if(!o || typeof o === 'object') {
            o = o || {};
            this.o.a = typeof o.a === 'string' ? o.a : 'A';
            this.o.b = typeof o.b === 'string' ? o.b : 'B';
        }
    }
    s.prototype.x = function(m) {
        console.log(m);
    };
    return s;
})();
var extender = (function(undefined) {
    function e(o) {
        // instanciate parent
        source.call(this, o);
        // child config
        if(!o || typeof o === 'object') {
            o = o || {};
            e = this.o;
            e.c = typeof o.c === 'number' ? o.c : 1;
            e.d = typeof o.d === 'number' ? o.d : 2;
        }
    }
    // inherit
    e.prototype = source.prototype;
    // extend
    e.prototype.y = function(m) {
        console.log(m);
    };
    return e;
})();
var e = new extender({d:3});
console.log(e);
transilvlad
  • 13,974
  • 13
  • 45
  • 80
  • I don't think `source.call(o)` is a solution. Did you notice it creates the `o` property (with the `a` and `b` subproperties) on the `o` parameter which you passed, instead of the result object (`this` inside the `e` constructor)? – Bergi Jun 24 '13 at 18:17
  • Also notice that you [should not use `new`](http://stackoverflow.com/q/12592913/1048572) for the prototype object creation. In your case you might even get [issues with nested objects](http://stackoverflow.com/q/10131052) – Bergi Jun 24 '13 at 18:21
  • Try to log `source.prototype.y` and be surprised :-) – Bergi Jun 24 '13 at 18:33
  • You want `y` to be only on `extender.prototype`, but `v` and `x` on both? If you assign them to each other, there will be only one object - and all properties be everywhere. – Bergi Jun 24 '13 at 18:39
  • That is what I want. It is not a problem. Thank you for explaining. Now I know the difference between yours and mine. You could say yours inherits and mine extends. – transilvlad Jun 24 '13 at 18:47