Search code examples
javascriptconstructorprototype

Why it works in its own space?


function Member(){
  this.x="hello";
};
var test = new Member();
Member.prototype.x = "test";
console.log(test.x);
//hello

I originally wrote like this for making a constructor expecting its output would be "test". But it didn't work, the result was "hello". But when I made the constructor to the below one, it worked.

function Member(){
  Member.x="hello";
};

I want to know how it was worked. Thanks.


Solution

  • When you perform new Member(), a couple of things happen, MDN described these steps quite nicely:

    1. Creates a blank, plain JavaScript object. For convenience, let's call it newInstance.

    2. Points newInstance's [[Prototype]] to the constructor function's prototype property, if the prototype is an Object. Otherwise, newInstance stays as a plain object with Object.prototype as its [[Prototype]]. ...

    3. Executes the constructor function with the given arguments, binding newInstance as the this context (i.e. all references to this in the constructor function now refer to newInstance).

    4. If the constructor function returns a non-primitive, this return value becomes the result of the whole new expression. Otherwise, if the constructor function doesn't return anything or returns a primitive, newInstance is returned instead. (Normally constructors don't return a value, but they can choose to do so to override the normal object creation process.)

    That means that in your examples:

    • test is the this object created in your Member constructor function when called with new (ie: the "instance").
    • test's [[Prototype]]1 points to the object Member.prototype. Meaning that all properties on the object Member.prototype can be access via test (if not "shadowed" on test)

    When you use test.x, we first look for x on the instance created by new Member() (ie: the this object that was created in your function and returned). If x cannot be found on the instance itself, then the prototype (object stored at the __proto__ property) is checked for x (and then if it can't be found on the prototype we then check the prototype's prototype and so on).

    In your first example, since we're able to find x on the object instance itself with the value of "hello" it logs "hello" and doesn't check the prototype (the x on the instance "shadows" the x on the prototype).

    In your second example, since your function doesn't add any properties to this, the instance returned by new Member() doesn't have an x property like the first example did (its an empty object {}), and so we check the prototype (which from step 2 above we see is Member.prototype), which does have an x property set to "test", and so it logs "test". Performing Member.x adds x as a property to your function object, but doesn't impact the instance created by using new.


    1 Note that as RobG pointed out, the use of __proto__ is deprecated. The spec uses an internal slot, [[Prototype]], on the constructed object instance and is used to point to the constructor function's .prototype property. Avoid using __proto__ and instead use methods such as Object.getPrototypeOf(), Object.create() or Object.setPrototypeOf() when you need to access / set the object for the [[Prototype]] slot (although, the use of setPrototypeOf() should still be used sparingly due to performance reasons)