Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between using a function to initialise a JavaScript object with and without return statement?

Tags:

javascript

I have two functions to initialise objects. init and differentInit.

function init(){
    return {a:5};
}

function differentInit(){
    this.a =5;
}

obj = init();
newobj = new differentInit()

how are the objects obj and newobj different?
This is what the JavaScript interpreter shows as the contents of the two objects.

>obj
<·Object {a: 5}
>newobj
<·differentInit {a: 5}

EDIT: As the answers (which are really good) have pointed out that newobj has an additional prototype as differentInit.prototype. I had thought an object can only be created with an additional prototype using the Object.create() method. I don't know if this a far too generic question, but how many other ways are there to create objects with prototypes? And can the method used in this question be considered a good way to create an object with prototypes?

like image 650
Jeff P Chacko Avatar asked Dec 15 '22 08:12

Jeff P Chacko


2 Answers

how are the objects obj and newobj different?

Given the functions you've shown, the only real difference is that the second one has an extra prototype (with a different constructor property) between it and Object.prototype. The first one directly inherits from Object.prototype:

E.g., obj:

                                  +----------+
Object.prototype-------------+--->| toString |
                             |    | valueOf  |
                             |    | ...      |
                             |    +----------+
        +---------------+    |
obj---->| [[Prototype]] |----+
        | a: 5          |
        +---------------+

vs. newObj:

                                                           +----------+
Object.prototype--------------------------------------+--->| toString |
                                                      |    | valueOf  |
                                                      |    | ...      |
                                                      |    +----------+
                                  +---------------+   |
differentInit.prototype------+--->| [[Prototype]] |---+
                             |    +---------------+
                             |
                             |
        +---------------+    |
newObj->| [[Prototype]] |----+
        | a: 5          |
        +---------------+

(Note that as Oriol points out, the objects that Object.prototype and differentInit.prototype point to both have a constructor property that points to Object and differentInit respectively. So obj.constructor will be Object and newObj.constructor will be differentInit. I'd've added it to the diagrams, but it complicates them unnecessarily...)

Unless you add something to differentInit.prototype, it doesn't make any significant practical difference.


Since you've tagged your question json, I'll just note that there's no JSON being used anywhere in your question. JSON is a textual notation for data exchange. If you're dealing with program source code, and not dealing with a string, you're not dealing with JSON.


I always thought an object can only be created with a prototype using the Object.create() method.

In JavaScript, all objects have prototypes unless they're created with Object.create(null) (which gives them null in their [[Prototype]] internal slot, e.g., they have no prototype).

...how many other ways are there to create objects with prototypes?

You can create objects with prototypes by:

  1. Using an object initializer, e.g. obj = {}, which creates it using Object.prototype as its prototype.

  2. By using the new operator with a function; the object that the function's prototype property refers to is assigned as the prototype of the object that new creates. All functions created with the function (or class) keyword get a prototype property with a blank object on it; that can then be added to, or replaced. (If the function's prototype is null, new uses Object.prototype instead.)

    Note that the function you use with new can either be any function defined using the function keyword:

    // Using `function`
    function Foo() {
    }
    Foo.prototype.bar = function() {
        console.log("Foo instance says 'bar'!");
    };
    var f = new Foo();
    f.bar();
    

    or as of ES2015, a function defined with the class keyword:

    class Foo {
        bar() {
            console.log("Foo instance says 'bar'!");
        }
    }
    var f = new Foo();
    f.bar();
    

    Barring a couple of minor details, the two snippets above do the same thing: They define a function called Foo, which you are expected to call via new, which has Foo.prototype with bar on it.

  3. Using Object.create(p), where p is assigned as the resulting object's prototype. Unlike #2 above, p can be null to create an object that doesn't have a prototype. This was added in ES5, although it can be shimmed (other than the null bit). (There's an optional second argument that can't be shimmed.)

You can set an object's prototype after creation in two ways:

  1. Using Reflect.setPrototypeOf(target, proto) or the very similar Object.setPrototypeOf(target, proto), which are new in ES2015. (The only difference between them is that Reflect.setPrototypeOf will throw if you pass it a non-object as target; Object.setPrototypeOf will just return what you gave it unchanged in that case. That, and some JavaScript engines don't have the Reflect object yet, but will soon.)

  2. (Backward compat only) Using the __proto__ property, provided that the object inherits (directly or indirectly) from Object.prototype. Notes:

    A. This is specified for JavaScript engines in web browsers only.

    B. It was not part of the specification before ES2015.

    C. Since it wasn't in the spec until ES2015, is only defined on web browsers, and will fail if the object doesn't inherit from Object.prototype, in the very rare case you need to dynamically set an object's prototype after creating it, use Reflect.setPrototypeOf instead.

And can the method used in this question be considered a good way to create an object with prototypes?

If by "the method" you mean new differentInit, yes, that's a completely normal way to create an object with a specific prototype. Typically you use that form when you're going to create several objects (a "class" of objects in the general sense of the word; e.g., objects that have a majority of traits in common). It's also perfectly normal and fine to use Object.create for that use case (some people don't like to use the new operator at all). If you're just doing a one-off where you want a specific object to inherit from another specific object, Object.create is the way to go.

like image 139
T.J. Crowder Avatar answered Dec 17 '22 01:12

T.J. Crowder


The most practical differences are

obj.constructor === Object
newobj.constructor === differentInit
Object.getPrototypeOf(obj) === Object.prototype;
Object.getPrototypeOf(newobj) === differentInit.prototype;
obj instanceof differentInit === false
newobj instanceof differentInit === true

Otherwise (unless you modify differentInit.prototype), they are basically the same.

like image 39
Oriol Avatar answered Dec 17 '22 00:12

Oriol