5
var Foo = function(){};

Object.defineProperty(Foo.prototype,'x',{
    get(){
        return 3;
    }
});

var foo = new Foo();

console.dir(foo);

The result I am looking for should be

Foo {
    __proto__:{
        constructor: ƒ (),
        x: 3,
        __proto__: Object
    }
}

But the real result is

Foo {
    x: 3,
    __proto__:{
        constructor: ƒ (),
        x: 3,
        __proto__: Object
    }
}

Why is the x attribute already appearing at the outermost layer?

yegao
  • 51
  • 2

1 Answers1

-2

What Happened (Step-by-step)

var Foo = function(){};

A new function is defined, named - Foo. If we use console.dir(Foo) we will see that Foo has 2 special members prototype and __proto__

Object.defineProperty(Foo.prototype,'x',{
    get(){
        return 3;
    }
}); 

The prototype of Foo is updated. About defineProperty (From MDN):

The static method Object.defineProperty() defines a new property directly on an object, or modifies an existing property on an object, and returns the object.

So the prototype object is now modified with a new member named x. The prototype here acts as a c'tor and will "kick-off" when a new instance of Foo will be created

var foo = new Foo();

A new instance of Foo is created. the c'tor call is using Foo prototype and x getter is applied

Alternatives

Extending Foo prototype, thus making sure it will effect only objects created from Foo (as a class)

Object.defineProperty(Foo.prototype, 'x',
{
    get: () => 3
}); 

console.dir(new Foo().x) // will show 3
console.dir(Foo.x) // undefined - protorype is for class objects

Or extending Foo __proto__, thus updating Foo as a function and while not affecting objectes created from it

Object.defineProperty(Foo.__proto__, 'x',
{
    get: () => 3
}); 

console.dir(new Foo().x) // undefined - x is not a member of Foo
console.dir(Foo.x) // 3

Bonus: a very good article about JS prototypes and why this is happening

Original Answer

This is happening because in JS function is a way to declare BOTH functions and classes!

So your Foo function can be also used as a class

var Foo = function(){};
Foo(); // call Foo as a function
var obj = new Foo(); // initiate an instance from class Foo

Because you are using Foo.prototype object (as a function) and then you create a new instance from your Foo class you guarantee that:

  1. Your function prototype will be modified with your new getter
  2. Every new object (that inherits) from your class will also use your new getter (in object level)

I really think that your code should be something like this instead:

function Foo ()
{
    Object.defineProperty(this,'x',{
       get(){
           return 3;
       }
    });
}
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
ymz
  • 6,602
  • 1
  • 20
  • 39
  • "*you are using `Foo.prototype` object (as a function)*" - no, he doesn't? And that's not even possible, as `Foo.prototype` is not a function! – Bergi Dec 17 '18 at 18:43
  • 2
    `Foo.__proto__` is just `Function.prototype`. As you said, adding properties to will not affect objects constructed from Foo, but you seem to have missed that it will affect every single function. After you extend `Foo.__proto__` try `console.log(function(){}.x);` – Paul Dec 18 '18 at 00:07