Difficulty in storing property in object in Javascript

5

I'm having a difficulty (which is probably pretty silly) in storing a value in a property of an object in Javascript. I have done something similar recently using this.propriedade = valor within the "constructor" function and I had no problem reusing the value in a function defined as 'Object.prototype.function_name'.

However, in this example ( available in JSFiddle ) a property ( this.m_oClickCallback ) is not visible in the function of ( MyButton.prototype.handleClick ), generating the following exception when the button is clicked:

  

Uncaught TypeError: Object [object global] has no method   'm_oClickCallback'

I imagine that probably the error is due to something very silly, but I can not see what it is.

Here is the example html that I have prepared:

<body onload="init();">
    <canvas id="myCanvas" width="800" height="600">
        O seu navegador não suporta HTML5. (<i>Your browser does not support HTML5.</i>)
    </canvas>
</body>

And the Javascript code:

(function() {

    // Objeto de botão customizado
    var MyButton = function(sLabel, sColor, oClickCallback) {
        // Chama o initialize do objeto protótipo (createjs.Container)
        this.initialize();

        // Armazena a referência para o callback do evento de click
        this.m_oClickCallback = oClickCallback;

        // Cria o conteúdo do botão
        var oText = new createjs.Text(sLabel, "40px Arial", "#ffffff");
        oText.textBaseline = "top";
        oText.textAlign = "center";

        var iWidth = oText.getMeasuredWidth() + 30;
        var iHeight = oText.getMeasuredHeight() + 20;

        var oBackground = new createjs.Shape();
        oBackground.name = "Background";
        oBackground.graphics.beginFill(sColor).drawRoundRect(0, 0, iWidth, iHeight, 10);

        oText.x = iWidth / 2;
        oText.y = 10;

        this.addChild(oBackground, oText);

        // Captura o evento de click        
        this.addEventListener("click", this.handleClick);
    }  

    // Define o protótipo
    MyButton.prototype = new createjs.Container();

    // Função de tratamento do click no botão. Invoca o callback armazenado.
    MyButton.prototype.handleClick = function(oEvent) {
        this.m_oClickCallback(oEvent.target);
    } 

    // Atribui o objeto ao escopo global de "window"
    window.MyButton = MyButton;
}());

// Função de inicialização. Cria o botão no onload do corpo da página.
function init() {
    g_oStage = new createjs.Stage("myCanvas");
    var oButton = new MyButton("Olá mundo!", "red", function() { alert("Funciona!"); });
    g_oStage.addChild(oButton);
    g_oStage.update();
}

EDIT: The idea is to store the property so that it is differentiated between each new instance of the object (as if it were a private or public attribute of a class in other languages). Solutions where ownership is shared (as if it were static) are not in the interest of the question.

EDIT2:

I found that in the createjs.Container there is a property called this.mouseChildren which when disabled causes the target object sent in the payload of the mouse events to be directly the MyButton object instead of its components (the text or the shape background color). Using this property and the target object I can solve the problem in the following way (see this new example JSFiddle , now with two button instances):

//. . .

// Faz o target dos eventos ser diretamente o objeto MyButton, ao invés do texto
// ou do background nele inclusos
this.mouseChildren = false;

//. . .

// Função de tratamento do click no botão. Invoca o callback armazenado.
MyButton.prototype.handleClick = function(oEvent) {
    oEvent.target.clickCallback(oEvent.target);
}

Logging the this into the console, I realized that it is indeed the object window . That's why the code did not work. I did not add this conclusion as a response because I still have the doubt as to why the mouse click handling function is not invoked at the button's scope but rather from the browser window. Anyone know?

    
asked by anonymous 31.01.2014 / 19:47

4 answers

6

Not always this will refer to the object you are creating: when you use this.addEventListener("click", this.handleClick); the value of this ceases to be the object and becomes the global scope, usually window . >

This problem can be solved in several ways. I will not go into too much detail because this tutorial abort this subject in detail, but based on it you would solve your problem simply by doing this:

/* remova */ this.addEventListener("click", this.handleClick);
/* insira */ this.addEventListener("click", this.handleClick.bind(this));
    
31.01.2014 / 20:50
3

From what I've seen in your code, at no time do you declare m_oClickCallBack within the scope of MyButton . You need to add the line within the MyButton :

var m_oClickCallBack;

For more information, see this answer .     

31.01.2014 / 19:53
1

Luiz Vieira The problem is that ECMAScript 5.1 (if I'm not mistaken) puts the variable this as a global scope, the most appropriate thing is to replace the line

this.m_oClickCallback = oClickCallback;

By

MyButton.m_oClickCallback = oClickCallback;

And at the time of calling m_oClickCallback you can do so

this.MyButton.m_oClickCallback(oEvent.target);
    
31.01.2014 / 19:56
1

Look, I made it work see this example in JSFiddle

But I had to change your code, you may not accept my solution, however, it is functional, what I did was:

In this part of creation:

    // Armazena a referência para o callback do evento de click
    this.m_oClickCallback = oClickCallback;

Modified to:

    // Armazena a referência para o callback do evento de click
    m_oClickCallback = oClickCallback;

And where do you assign the event to the button:

// Função de tratamento do click no botão. Invoca o callback armazenado.
MyButton.prototype.handleClick = function(oEvent) {
      this.m_oClickCallback(oEvent.target);
} 

Modified to:

// Função de tratamento do click no botão. Invoca o callback armazenado.
MyButton.prototype.handleClick = function(oEvent) {
  m_oClickCallback(oEvent.target);
} 

I do not think it's the best solution, but it works.

I hope I have helped.

    
31.01.2014 / 20:02