30

I'm creating a class that extends Object in JavaScript and expect super() to initialise the keys/values when constructing a new instance of this class.

class ExtObject extends Object {
  constructor(...args) {
    super(...args);
  }
}

const obj = new Object({foo:'bar'});
console.log(obj); // { foo: 'bar' }

const ext = new ExtObject({foo:'bar'});
console.log(ext); // ExtObject {}

console.log(ext.foo); // undefined

Why isn't foo defined as 'bar' on ext in this example?

EDIT

Explanation: Using `super()` when extending `Object`

Solution: Using `super()` when extending `Object`

Tim Baas
  • 6,035
  • 5
  • 45
  • 72
  • 4
    Although I can't answer your question, I can't help but wonder why you would even want to `extend Object`. – Ahmed Bajra Jul 20 '18 at 09:36
  • 1
    Because I want the instances of the extended class to act the same as Object as well as have custom functionality. I get your point, because I guess the same functionality can be created when not extending `Object` and using `Object.assign()` in the constructor, as Gildas.Tambo suggests. But it really makes me wonder why it's not working this way.. – Tim Baas Jul 20 '18 at 09:44
  • The funny thing about this issue is that Babel can transpile this code to give the correct results – Hyyan Abo Fakher Jul 20 '18 at 09:45
  • 1
    Questions are questions. There are **no** reason why it should include a link to the answer. You can pin an answer to the top by accepting it, but that's it. – user202729 Jul 20 '18 at 15:49

3 Answers3

11

Nobody has actually explained why it doesn't work. If we look at the latest spec, the Object function is defined as follows:

19.1.1.1 Object ( [ value ] )

When Object function is called with optional argument value, the following steps are taken:

  1. If NewTarget is neither undefined nor the active function, then
    1. Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
  2. If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
  3. Return ! ToObject(value).

The first step is the important one here: NewTarget refers to the function that new was called upon. So if you do new Object, it will be Object. If you call new ExtObject it will ExtObject.

Because ExtObject is not Object ("nor the active function"), the condition matches and OrdinaryCreateFromConstructor is evaluated and its result returned. As you can see, nothing is done with the value passed to the function.

value is only used if neither 1. nor 2. are fulfilled. And if value is an object, it is simply returned as is, no new object is created. So, new Object(objectValue) is actually the same as Object(objectValue):

var foo = {bar: 42};
console.log(new Object(foo) === foo);
console.log(Object(foo) === foo);

In other words: Object does not copy the properties of the passed in object, it simply returns the object as is. So extending Object wouldn't copy the properties either.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 1
    Great, finally understand why using `Object.create()` can only do the same job as `Object` but cannot add an extend method. – MT-FreeHK Jul 20 '18 at 13:54
5

You are missing the Object.assign

class ExtObject extends Object {
  constructor(...args) {
    super(...args);
    Object.assign(this, ...args);
  }
}

const obj = new Object({foo:'bar'});
console.log(obj); // { foo: 'bar' }

const ext = new ExtObject({foo:'bar'});
console.log(ext); // { foo: 'bar' }

console.log(ext.foo); // bar
Hyyan Abo Fakher
  • 3,497
  • 3
  • 21
  • 35
Gildas.Tambo
  • 22,173
  • 7
  • 50
  • 78
  • Although this works, we wouldn't be needing the `extends Object` and `super()` in this case, right? I guess this is more like 'copying' the source of the parents (Object) constructor method? – Tim Baas Jul 20 '18 at 09:41
  • 1
    Yes , since we are working with objects try to understand ECMAScript 6 Class and Inheritance and Super class calls with super https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes – Gildas.Tambo Jul 20 '18 at 09:45
  • @TimBaas I am doubt with this ans.... Let say you remove `extends Object` and `super(...args)` , everything work well... it is just copying the value. – MT-FreeHK Jul 20 '18 at 13:48
5

This answer works only when using the Babel transpiler.

Because Object's constructor returns value. See spec

15.2.2.1 new Object ( [ value ] )
When the Object constructor is called with no arguments or with one argument value, the following steps are taken:
...
8. Returns obj.

class ExtObject extends Object {
  constructor(...args) {
    return super(...args);
  }
}

const obj = new Object({foo:'bar'});
console.log(obj); // { foo: 'bar' }

const ext = new ExtObject({foo:'bar'});
console.log(ext); // { foo: 'bar' }

console.log(ext.foo); // bar
Hyyan Abo Fakher
  • 3,497
  • 3
  • 21
  • 35
barbsan
  • 3,418
  • 11
  • 21
  • 28
  • 2
    @barbsan this is not a valid answer, you are using babel to transpile the code, that is why it worked , in browsers which supports ES6 natively , this will not work – Hyyan Abo Fakher Jul 20 '18 at 10:03
  • 1
    @Caramiriel That's because you're returning a new Object: `{foo:'bar'}` in the constructor. In barbsan's answer `super()` is returned which is not a new object but a new instance of this class. – Tim Baas Jul 20 '18 at 10:11
  • 1
    @Caramiriel because I selected Babel transpiler in snippet's settings – barbsan Jul 20 '18 at 10:58