0

I'm learning object oriented Javascript and am trying to solve a very simple problem. Given a string, convert it to another using the rules below:

  • G >> C
  • C >> G
  • T >> A
  • A >> U

Here is my code:

    var DnaTranscriber = function(){};
    
    DnaTranscriber.prototype = {
        map: {G:'C', C:'G', T:'A', A:'U'},
        toRna: function(dna) {
            return dna.split('').map(this.mapper).join('');
        },
        mapper: function(dnaChar){
            if(this.map[dnaChar]) return this.map[dnaChar];
            throw new Error('Invalid input');
        }
    };

    var d = new DnaTranscriber();
    d.toRna('C');

However, trying to execute this code throws an error:

TypeError: Cannot read property 'C' of undefined

Why is this.map undefined inside mapper?

Xcodian Solangi
  • 2,342
  • 5
  • 24
  • 52
BeetleJuice
  • 39,516
  • 19
  • 105
  • 165
  • 2
    Because Javascript. `this.mapper` is not "bound" to your `d` object anymore. You need `this.mapper.bind(this)` or something like this. – georg Sep 27 '17 at 19:23
  • thnks @georg you're right that works but it's confusing. How could the `this` context have changed though? I'm calling a function within the same object as the calling code right? – BeetleJuice Sep 27 '17 at 19:24
  • @Hektor the empty function `var DnaTranscriber = function(){};` is the constructor of the `DnaTranscriber`, and it is empty because the OP does nothing in it. – t.niese Sep 27 '17 at 19:28
  • Your issue is here: `map(this.mapper)`. Passing in `this.mapper` as a callback loses the context of `this` when it is called. try `map(this.mapper.bind(this))` ... ugly but, welcome to OO javascript – Damon Sep 27 '17 at 19:30
  • Not related to your problem. But in most cases you want create a named function `function name() {}` instead of assigning an anonymous function to a variable `var name = function() {}`. – t.niese Sep 27 '17 at 19:30
  • It's because`this.map[dnaChar]` is inside of the function `function(dnaChar){ ...` and the `this` keyword in JS represents the current context. It reference this direct parent function, not your entire object. – Chris Barr Sep 27 '17 at 19:30
  • @Quentin thanks for linking to the Q this one duplicates! – BeetleJuice Sep 27 '17 at 19:34
  • @ChrisBarr What do you want to say with `[...]It reference this direct parent function, not your entire object.[...]`? The `this` is always the context on which the function was called. In the case of the `.map(this.mapper)` the `mapper` function will be called with no context by the `map` algorithm, so `this` will refer to the global object, in case of the browser it would be `window`. – t.niese Sep 27 '17 at 19:34
  • @t.niese Using `function name(){}` within `Object.prototype` without assigning it to a named property -- `name: function(){}` -- raises syntax errors – BeetleJuice Sep 27 '17 at 19:36
  • @BeetleJuice I did not mean the part in the functions in the object definition. I meant that you should write `function DnaTranscriber(){};` instead of `var DnaTranscriber = function() {}` in your case. But this is true in most cases for all `var name = function() {}` where you have an assignment (`=`) of an anonymous function to a variable. For properties of an object it can be useful to write `mapper: function mapper(dnaChar)`, because then the name `mapper` would appear in the stack trace of an error which could make it easy for debugging. – t.niese Sep 28 '17 at 04:39
  • @t.niese OK I understand. Thanks. Why is it better to use `function DnaTranscriber(){}` instead of `var DnaTranscriber = function() {};`? You say I should do so but not why. – BeetleJuice Sep 28 '17 at 10:56

0 Answers0