Search code examples
javascriptannyang

this.set is not a function


I have code like this:

var ws2812 = {};


ws2812.set = function(r,g,b){
    $.get( "/light?r="+r+"&g="+g+"&b="+b, function( data ) {
        console.log("Light set to:"+"/light?r="+r+"&g="+g+"&b="+b);
    })
};

ws2812.speech = function(word){
    switch(word){
        case "czerwone":
            this.set(255,0,0);
        break;
        case "zielone":
            this.set(0,255,0);
            break;
        case "niebieskie":
            this.set(0,0,255);
            break;
        case "białe":
            this.set(255,255,255);
            break;
        default:
            this.set(0,0,0);
            break;

    }
}

When running ws2812.speech(""); inside console, everything works. However, when paired with the Annyang library, I get this:

Uncaught TypeError: this.set is not a function 
ws2812.speech @ script.js:29  
b.annyang.init.d.onresult @ annyang.min.js:6

What's wrong?

[edit]

The command is added like this:

annyang.addCommands({"ustaw *term światło":ws2812.speech});

Specifically, inside annyang, this line fails:

f[h].callback.apply(this,m)

Is replacing this with ws2812 the only way to work around this?


Solution

  • ws2812.speec is defined as a static function. So this keyword inside it refers to itself (function scope), not the object ws2812 you want.

    To fix it, either of these quick choices can be made:


    Choice#1 > Call the static function ws2812.set properly

    So your code becomes:

    ws2812.speech = function(word){
        switch(word){
        case "czerwone":
            ws2812.set(255,0,0);
        break;
        case "zielone":
            ws2812.set(0,255,0);
            break;
        case "niebieskie":
            ws2812.set(0,0,255);
            break;
        case "białe":
            ws2812.set(255,255,255);
            break;
        default:
            ws2812.set(0,0,0);
            break;
    
        }
    }
    

    However, there are some possibilities that this keywords referenced in other parts in the rest of the code may also suffer from this issue. You may need to go check it.


    Choice#2 > Convert to prototype functions

    This way you can retain this keywords, but the functions are no longer static. You need to instantiate an instance object of ws2812 to use.

    So your declarations becomes:

    var ws2812 = function(){};
    
    
    ws2812.prototype.set = function(r,g,b){
        $.get( "/light?r="+r+"&g="+g+"&b="+b, function( data ) {
            console.log("Light set to:"+"/light?r="+r+"&g="+g+"&b="+b);
        })
    };
    
    ws2812.prototype.speech = function(word){
        switch(word){
        case "czerwone":
            this.set(255,0,0);
        break;
        case "zielone":
            this.set(0,255,0);
            break;
        case "niebieskie":
            this.set(0,0,255);
            break;
        case "białe":
            this.set(255,255,255);
            break;
        default:
            this.set(0,0,0);
            break;
    
        }
    }
    

    Then use it via instance of object instead:

    var myWs2812 = new ws2812();
    myWs2812.speech('hello world!'); // inside it, should call this.set properly
    

    Choice#3 > Bind 'this' object when calling

    In case you insist you don't want to modify the implementation of this ws2812. It's okay to leave it and bind this object when using it instead.

    So when you call ws2812.speech, you need to use function.prototype.call and pass in ws2812.set as this.

    ws2812.call( ws2812.set, 'hello world!' );
    

    However, this doesn't look much semantic and it may cause confusions in the future use for people who will maintain this code.


    I'll leave it for you to decide which would be the best way for you to go.