1

If i want to destruct an Object i would do :

const obj = {
  a: 'a',
  fn: () => 'some function'
}

// const fn = obj.fn;
// OR

const {
  a,
  fn
} = obj;

console.log( fn() );

this doesn't work for the Date Object :

Uncaught TypeError: this is not a Date object.

const date = new Date();

const day = date.getDate();
console.log(day); // works

const {
  getDate
} = date;
console.log( getDate() ); // doesn't work

Why is this possible with the first Object and not with the Date ? how would one acheive that if it's possible.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Taki
  • 17,320
  • 4
  • 26
  • 47
  • Destructuring is just a shorthand for `name = obj.name` and in both cases you loose `this` context of the `obj`. As such this is not limited to dates, any obj will loose `this`. – Keith Jan 30 '19 at 19:38
  • The 2 date examples are not equivalent. Your destructuring is like this: `const getDate = date.getDate; getDate()` which makes little sense without `date` object being `this` https://stackoverflow.com/a/2025839 – adiga Jan 30 '19 at 19:43
  • 1
    From [*ECMA-262*](http://ecma-international.org/ecma-262/9.0/#sec-date.prototype.setdate): *Let t be LocalTime(? thisTimeValue(this value)).*. What is *this* when you call the destructured *getDate*? You can fix it with `getDate.call(date)`. ;-) – RobG Jan 30 '19 at 23:37
  • @RobG the question in your comment helped understanding what's happening, makes much sense now, when i destructure a function i loose *this*, thank you :) – Taki Feb 04 '19 at 13:55

2 Answers2

2

Because this it not a Date object. When you call getDate() without its proper context (ie. date.getDate()), then you're calling it in the context of the window (or null in strict mode). Neither window nor null are Date objects, therefore the function fails.

Try const getDate = date.getDate.bind(date);

Demo:

const test = { fn : function() { return this.constructor; } };

const normal = test.fn();
console.log(normal); // object

const {fn} = test;
console.log( fn() ); // window

const bound = test.fn.bind(test);
console.log( bound() ); // object
Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • 1
    @ABOS So...? The question doesn't have anything to do with destructors, it's asking about destructuring. – Niet the Dark Absol Jan 30 '19 at 19:24
  • The title said destruct, so I thought OP wanted to do it that way. – ABOS Jan 30 '19 at 19:26
  • @ABOS: That's the main point of this answer. Destructuring here gives you a bare function reference, and not a bound method. If you can find a way to do this with destructuring, we're all ears. – Scott Sauyet Jan 30 '19 at 19:26
  • @ScottSauyet, I think the difficulty is how to write a 'one-liner' using destruct, if doing it in plain assignment like `f = date.getDate, f()`, I am pretty it has been discussed a lot on SO before... – ABOS Jan 30 '19 at 19:29
  • @ABOS: I'm not following. `f = date.getDate, f()` has exactly the same issue as described in the question. – Scott Sauyet Jan 30 '19 at 19:31
  • Please don't confuse "context" with "this". If a function is called in a window (global) **execution** context, the fact that its *this* defaults to the window object has nothing to do with the context it was called from. So *this* is not "context". Similarly for arrow functions, where *this* is adopted from its outer execution context. – RobG Jan 30 '19 at 23:21
2

It's likely not worth it, but you could write a function to help you destructure methods from an object. Here bindMethods does this, using helper allKeys, which collects the keys from the entire prototype chain of an object and which in turn depends on walkPrototypeChain. They could obviously be folded into a single function if desired.

const walkPrototypeChain = (process, init, finish) => (obj) => {
  let currObj = obj, currRes = init();
  do {
    currRes = process(currRes, currObj)
  } while (currObj = Object.getPrototypeOf(currObj))
  return finish(currRes)
}

const allKeys = walkPrototypeChain(
  (set, obj) => {Object.getOwnPropertyNames(obj).forEach(k => set.add(k)); return set},
  () => new Set(),
  s => [...s]
)

const bindMethods = (obj) => allKeys(obj).reduce(
  (o, n) => typeof obj[n] == 'function' ? ({...o, [n]: obj[n].bind(obj)}) : o, 
  {}
)

const date = new Date()
const {getDate, getFullYear} = bindMethods(date) // or any other date function

console.log(getDate())
console.log(getFullYear())
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103