1

I have a problem with the binding of functions in Javascript.

Be sure that I read all StackOverflow's answers I could find (like this one), and followed the instructions and examples of Mozilla's Developpers guides

here is the relevant part of my code :

class Collection extends Array {
  constructor (...args) {
    super(...args)
  }
  each (callback) {
    this.forEach(element => {
      callback.bind(element)(element) 
      // bind the function THEN call it with element as argument
      // but I also tried :
      // callback.bind(element)()
      // callback.call(element, element)
      // let bound = callback.bind(element); bound()
    })
  }
}

//the tests :
let el1 = {x:1, y:"somevars"}
let el2 = {x:42, y:"another"}

let col = new Collection()
col.push(el1)
col.push(el2)

// the test
col.each(element => console.log(Object.keys(this)))
// and I get ['console', 'global', 'process' ...]   all the global variables 
// instead of ['x','y'] which is what I want

I really don't understant why it is'nt working...

for context, it is to solve an interesting kata on Codewars, not a matter of life and death.

gui3
  • 1,711
  • 14
  • 30
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions – Teemu Jul 28 '20 at 19:52
  • *"An arrow function does not have its own this...."* ok I get it ... need to find another way to solve this puzzle then. Add an answer and I'll accept it, and Thank you – gui3 Jul 28 '20 at 19:54
  • the problem is, in this puzzle's tests they call it with an arrow function : `jane.has(2).hands.each(hand => having(5).fingers);` ... you see that `having`, which is a member of 'hand', is called from the root this .... any idea ? – gui3 Jul 28 '20 at 19:57
  • You do realize that in the example you gave with the arrow function, you were trying to bind both this and the first parameter with the same value, so one of the two is not needed. You could simply replace Object.keys(this) with Object.keys(element) in your arrow function, and then simply replace your implementation of each with this.forEach(callback) – smr Jul 28 '20 at 20:30
  • 1
    In your `each` implementation, pass a function, not an arrow function, to `this.forEach`. The Kata test doesn't contain `this` in the body of the arrow function, hence your test is different. The original Array.forEach takes two arguments, the callback function, and the context to use as `this` value in the callback, but `this` is not bound by that argument with arrow function callbacks even in the original `forEach`. As smr already stated, you want the keys of `element`, not of `this`. Maybe the Kata test should actually be `jane.has(2).arms.each(arm => arm.having(1).hand.having(5).fingers)`? – Teemu Jul 29 '20 at 08:57
  • @Teemu Thanks for you reply, but I guess it's part of the **kata's challenge** to **achieve accessing the itered item's properties at root** *(actually I don't guess, it's stated multiple times in the discussion that this is **the tough part** ... this kata clearly announces itself **being about metaprogramming**)* --- I would really like to just have one word, one hint about how to do it, I can do researches myself then. I eventually gave up finding myself (after 3 hours) but I'm not ranked enough to be permitted to see the solutions ... frustrating .. – gui3 Jul 29 '20 at 14:09
  • also : I managed to bind the array function (using the trick in my answer below) but this did not work with `jane.has(2).hand.each(hand => having(5).fingers...)` – gui3 Jul 29 '20 at 14:11
  • 1
    I guess the task is purposed to be solved with [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). By storing the `element` argument within `this.forEach` somewhere, and creating a top scope proxy for `having` (or actually for all the methods in `Thing`), you can get the current element in `having` method. That way the error won't occur, but I've no clue of how to continue from there ... – Teemu Jul 29 '20 at 14:23
  • 1
    thanks ! I used proxies for all the tasks asked before it (creating a new property `jane.is_a_woman` only by calling `jane.is_a.woman` ans so on), i'll dive deeper in what proxies offer when i'll feel brave enough ;) – gui3 Jul 29 '20 at 14:26

1 Answers1

0

Ok so as pointed by @Teemu, arrow functions can't be bound ...

but with that insight, I could look for a way to bypass this and found another StackOverflow's post that gives a trick :

(copy-pasted from the post)

function arrowBind(context, fn) {
  let arrowFn;
  (function() {
    arrowFn = eval(fn.toString());
    arrowFn();
  }).call(context);
}
arrowBind(obj, () => {console.log(this)});

this works just fine, the new this is the context...

But doesn't solve the puzzle in my case ( 'having is not defined') I need to look further

gui3
  • 1,711
  • 14
  • 30